summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick McSpadden <nmcspadden@gmail.com>2018-05-14 23:33:45 -0700
committerNick McSpadden <nmcspadden@gmail.com>2018-05-14 23:33:45 -0700
commitc935ce85f591e53f2f200e55cb252185e1801387 (patch)
treedfeee6151c50d805138a3d10761a531c2ffbf984
parentc48fd22a51deb74a3d4bc3abca308043689b494a (diff)
parentc0609e449135fae43d436136a4f0fd3889a9b8f1 (diff)
downloadchef-c935ce85f591e53f2f200e55cb252185e1801387.tar.gz
Merge branch 'master' into mac_uid
-rw-r--r--.bundle/config2
-rw-r--r--.dockerignore2
-rw-r--r--.expeditor/config.yml78
-rwxr-xr-x.expeditor/update_dockerfile.sh8
-rwxr-xr-x.expeditor/update_version.sh16
-rw-r--r--.gitattributes4
-rw-r--r--.github/CODEOWNERS4
-rw-r--r--.github/ISSUE_TEMPLATE.md37
-rw-r--r--.github/PULL_REQUEST_TEMPLATE.md15
-rw-r--r--.github/lock.yml1
-rw-r--r--.gitignore31
-rw-r--r--.kitchen.yml82
-rw-r--r--.mailmap69
-rw-r--r--.rubocop.yml25
-rw-r--r--.travis.yml422
-rw-r--r--CHANGELOG.md1000
-rw-r--r--CHEF_MVPS.md17
-rw-r--r--CLA_ARCHIVE.md2705
-rw-r--r--CONTRIBUTING.md247
-rw-r--r--DOC_CHANGES.md22
-rw-r--r--Dockerfile11
-rw-r--r--Gemfile67
-rw-r--r--Gemfile.lock528
-rw-r--r--ISSUE_TEMPLATE.md35
-rw-r--r--MAINTAINERS.md91
-rw-r--r--MAINTAINERS.toml126
-rw-r--r--NOTICE2
-rw-r--r--README.md275
-rw-r--r--RELEASE_NOTES.md1358
-rw-r--r--ROADMAP.md15
-rw-r--r--Rakefile7
-rw-r--r--VERSION2
-rw-r--r--acceptance/.gitignore1
-rw-r--r--acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml30
-rw-r--r--acceptance/.shared/kitchen_acceptance/.kitchen.vagrant.yml2
-rw-r--r--acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb43
-rw-r--r--acceptance/Gemfile18
-rw-r--r--acceptance/Gemfile.lock269
-rw-r--r--acceptance/README.md91
-rw-r--r--acceptance/basics/test/integration/chef-current-install/serverspec/chef_client_spec.rb2
-rw-r--r--acceptance/basics/test/integration/helpers/serverspec/Gemfile8
-rw-r--r--acceptance/data-collector/.acceptance/acceptance-cookbook/.gitignore (renamed from acceptance/fips/.acceptance/acceptance-cookbook/.gitignore)0
-rw-r--r--acceptance/data-collector/.acceptance/acceptance-cookbook/metadata.rb3
-rw-r--r--acceptance/data-collector/.acceptance/acceptance-cookbook/recipes/destroy.rb2
-rw-r--r--acceptance/data-collector/.acceptance/acceptance-cookbook/recipes/provision.rb2
-rw-r--r--acceptance/data-collector/.acceptance/acceptance-cookbook/recipes/verify.rb2
-rw-r--r--acceptance/data-collector/.acceptance/data-collector-test/.gitignore (renamed from kitchen-tests/cookbooks/audit_test/.gitignore)1
-rw-r--r--acceptance/data-collector/.acceptance/data-collector-test/Berksfile3
-rw-r--r--acceptance/data-collector/.acceptance/data-collector-test/files/default/api.rb101
-rw-r--r--acceptance/data-collector/.acceptance/data-collector-test/files/default/apigemfile (renamed from acceptance/fips/test/integration/fips-integration/serverspec/Gemfile)2
-rw-r--r--acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-both-mode.rb4
-rw-r--r--acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-client-mode.rb4
-rw-r--r--acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-no-endpoint.rb2
-rw-r--r--acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-solo-mode.rb4
-rw-r--r--acceptance/data-collector/.acceptance/data-collector-test/files/default/config.ru2
-rw-r--r--acceptance/data-collector/.acceptance/data-collector-test/metadata.rb7
-rw-r--r--acceptance/data-collector/.acceptance/data-collector-test/recipes/default.rb44
-rw-r--r--acceptance/data-collector/.kitchen.yml9
-rw-r--r--acceptance/data-collector/Berksfile3
-rw-r--r--acceptance/data-collector/test/integration/default/serverspec/default_spec.rb208
-rw-r--r--acceptance/data-collector/test/integration/helpers/serverspec/Gemfile8
-rw-r--r--acceptance/fips/.acceptance/acceptance-cookbook/metadata.rb2
-rw-r--r--acceptance/fips/.acceptance/acceptance-cookbook/recipes/destroy.rb1
-rw-r--r--acceptance/fips/.acceptance/acceptance-cookbook/recipes/provision.rb1
-rw-r--r--acceptance/fips/.acceptance/acceptance-cookbook/recipes/verify.rb1
-rw-r--r--acceptance/fips/.kitchen.yml8
-rw-r--r--acceptance/fips/test/integration/fips-integration/serverspec/fips-integration_spec.rb51
-rw-r--r--acceptance/fips/test/integration/fips-unit-functional/serverspec/Gemfile3
-rw-r--r--acceptance/fips/test/integration/fips-unit-functional/serverspec/fips-unit-functional_spec.rb56
-rw-r--r--acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb28
-rw-r--r--acceptance/top-cookbooks/.kitchen.docker.yml12
-rw-r--r--acceptance/top-cookbooks/.kitchen.git.yml11
-rw-r--r--acceptance/trivial/.kitchen.yml2
-rw-r--r--acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb2
-rw-r--r--acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb2
-rw-r--r--acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb2
-rw-r--r--acceptance/windows-service/test/integration/chef-windows-service/inspec/chef_windows_service_spec.rb8
-rw-r--r--appveyor.yml31
-rw-r--r--appveyor_registry.reg29
-rwxr-xr-x[-rw-r--r--]bin/chef-resource-inspector (renamed from lib-backcompat/chef/chef_fs/file_system/already_exists_error.rb)16
-rwxr-xr-xbin/chef-shell2
-rw-r--r--chef-config/.gitignore9
-rw-r--r--chef-config/.travis.yml31
-rw-r--r--chef-config/README.md4
-rw-r--r--chef-config/Rakefile14
-rw-r--r--chef-config/chef-config.gemspec6
-rw-r--r--chef-config/lib/chef-config/config.rb266
-rw-r--r--chef-config/lib/chef-config/exceptions.rb1
-rw-r--r--chef-config/lib/chef-config/fips.rb51
-rw-r--r--chef-config/lib/chef-config/logger.rb3
-rw-r--r--chef-config/lib/chef-config/mixin/credentials.rb57
-rw-r--r--chef-config/lib/chef-config/mixin/fuzzy_hostname_matcher.rb8
-rw-r--r--chef-config/lib/chef-config/package_task.rb79
-rw-r--r--chef-config/lib/chef-config/path_helper.rb6
-rw-r--r--chef-config/lib/chef-config/version.rb2
-rw-r--r--chef-config/lib/chef-config/workstation_config_loader.rb50
-rw-r--r--chef-config/spec/unit/config_spec.rb223
-rw-r--r--chef-config/spec/unit/fips_spec.rb128
-rw-r--r--chef-config/spec/unit/workstation_config_loader_spec.rb170
-rw-r--r--chef-universal-mingw32.gemspec7
-rw-r--r--chef.gemspec29
-rwxr-xr-xci/dependency_update.sh9
-rwxr-xr-xci/verify-chef.bat45
-rwxr-xr-xci/verify-chef.sh47
-rwxr-xr-xci/version_bump.sh11
-rwxr-xr-xci/version_show.sh3
-rw-r--r--distro/common/html/_sources/ctl_chef_client.txt36
-rw-r--r--distro/common/html/_sources/ctl_chef_server.txt341
-rw-r--r--distro/common/html/_sources/ctl_chef_shell.txt15
-rw-r--r--distro/common/html/_sources/ctl_chef_solo.txt25
-rw-r--r--distro/common/html/_sources/index.txt135
-rw-r--r--distro/common/html/_sources/knife.txt74
-rw-r--r--distro/common/html/_sources/knife_bootstrap.txt56
-rw-r--r--distro/common/html/_sources/knife_client.txt151
-rw-r--r--distro/common/html/_sources/knife_common_options.txt6
-rw-r--r--distro/common/html/_sources/knife_configure.txt29
-rw-r--r--distro/common/html/_sources/knife_cookbook.txt236
-rw-r--r--distro/common/html/_sources/knife_cookbook_site.txt157
-rw-r--r--distro/common/html/_sources/knife_data_bag.txt160
-rw-r--r--distro/common/html/_sources/knife_delete.txt26
-rw-r--r--distro/common/html/_sources/knife_deps.txt61
-rw-r--r--distro/common/html/_sources/knife_diff.txt34
-rw-r--r--distro/common/html/_sources/knife_download.txt44
-rw-r--r--distro/common/html/_sources/knife_edit.txt26
-rw-r--r--distro/common/html/_sources/knife_environment.txt158
-rw-r--r--distro/common/html/_sources/knife_exec.txt47
-rw-r--r--distro/common/html/_sources/knife_index_rebuild.txt21
-rw-r--r--distro/common/html/_sources/knife_list.txt33
-rw-r--r--distro/common/html/_sources/knife_node.txt250
-rw-r--r--distro/common/html/_sources/knife_raw.txt35
-rw-r--r--distro/common/html/_sources/knife_recipe_list.txt23
-rw-r--r--distro/common/html/_sources/knife_role.txt157
-rw-r--r--distro/common/html/_sources/knife_search.txt53
-rw-r--r--distro/common/html/_sources/knife_serve.txt19
-rw-r--r--distro/common/html/_sources/knife_show.txt27
-rw-r--r--distro/common/html/_sources/knife_ssh.txt43
-rw-r--r--distro/common/html/_sources/knife_ssl_check.txt41
-rw-r--r--distro/common/html/_sources/knife_ssl_fetch.txt41
-rw-r--r--distro/common/html/_sources/knife_status.txt37
-rw-r--r--distro/common/html/_sources/knife_tag.txt69
-rw-r--r--distro/common/html/_sources/knife_upload.txt49
-rw-r--r--distro/common/html/_sources/knife_user.txt127
-rw-r--r--distro/common/html/_sources/knife_using.txt43
-rw-r--r--distro/common/html/_sources/knife_xargs.txt30
-rw-r--r--distro/common/html/_static/ajax-loader.gifbin673 -> 0 bytes
-rw-r--r--distro/common/html/_static/basic.css537
-rw-r--r--distro/common/html/_static/chef.icobin1150 -> 0 bytes
-rw-r--r--distro/common/html/_static/chef_html_logo.pngbin57548 -> 0 bytes
-rw-r--r--distro/common/html/_static/comment-bright.pngbin3500 -> 0 bytes
-rw-r--r--distro/common/html/_static/comment-close.pngbin3578 -> 0 bytes
-rw-r--r--distro/common/html/_static/comment.pngbin3445 -> 0 bytes
-rw-r--r--distro/common/html/_static/contents.pngbin202 -> 0 bytes
-rw-r--r--distro/common/html/_static/doctools.js238
-rw-r--r--distro/common/html/_static/down-pressed.pngbin368 -> 0 bytes
-rw-r--r--distro/common/html/_static/down.pngbin363 -> 0 bytes
-rw-r--r--distro/common/html/_static/file.pngbin392 -> 0 bytes
-rw-r--r--distro/common/html/_static/guide.css505
-rw-r--r--distro/common/html/_static/jquery.js2
-rw-r--r--distro/common/html/_static/minus.pngbin199 -> 0 bytes
-rw-r--r--distro/common/html/_static/navigation.pngbin218 -> 0 bytes
-rw-r--r--distro/common/html/_static/plus.pngbin199 -> 0 bytes
-rw-r--r--distro/common/html/_static/pygments.css62
-rw-r--r--distro/common/html/_static/searchtools.js622
-rw-r--r--distro/common/html/_static/underscore.js31
-rw-r--r--distro/common/html/_static/up-pressed.pngbin372 -> 0 bytes
-rw-r--r--distro/common/html/_static/up.pngbin363 -> 0 bytes
-rw-r--r--distro/common/html/_static/websupport.js808
-rw-r--r--distro/common/html/ctl_chef_client.html270
-rw-r--r--distro/common/html/ctl_chef_server.html728
-rw-r--r--distro/common/html/ctl_chef_shell.html163
-rw-r--r--distro/common/html/ctl_chef_solo.html194
-rw-r--r--distro/common/html/index.html202
-rw-r--r--distro/common/html/knife.html170
-rw-r--r--distro/common/html/knife_bootstrap.html285
-rw-r--r--distro/common/html/knife_client.html285
-rw-r--r--distro/common/html/knife_common_options.html96
-rw-r--r--distro/common/html/knife_configure.html105
-rw-r--r--distro/common/html/knife_cookbook.html487
-rw-r--r--distro/common/html/knife_cookbook_site.html375
-rw-r--r--distro/common/html/knife_data_bag.html374
-rw-r--r--distro/common/html/knife_delete.html93
-rw-r--r--distro/common/html/knife_deps.html143
-rw-r--r--distro/common/html/knife_diff.html123
-rw-r--r--distro/common/html/knife_download.html145
-rw-r--r--distro/common/html/knife_edit.html89
-rw-r--r--distro/common/html/knife_environment.html326
-rw-r--r--distro/common/html/knife_exec.html220
-rw-r--r--distro/common/html/knife_index_rebuild.html83
-rw-r--r--distro/common/html/knife_list.html114
-rw-r--r--distro/common/html/knife_node.html458
-rw-r--r--distro/common/html/knife_raw.html110
-rw-r--r--distro/common/html/knife_recipe_list.html91
-rw-r--r--distro/common/html/knife_role.html295
-rw-r--r--distro/common/html/knife_search.html203
-rw-r--r--distro/common/html/knife_serve.html79
-rw-r--r--distro/common/html/knife_show.html104
-rw-r--r--distro/common/html/knife_ssh.html177
-rw-r--r--distro/common/html/knife_ssl_check.html151
-rw-r--r--distro/common/html/knife_ssl_fetch.html151
-rw-r--r--distro/common/html/knife_status.html144
-rw-r--r--distro/common/html/knife_tag.html138
-rw-r--r--distro/common/html/knife_upload.html153
-rw-r--r--distro/common/html/knife_user.html242
-rw-r--r--distro/common/html/knife_using.html210
-rw-r--r--distro/common/html/knife_xargs.html122
-rw-r--r--distro/common/html/objects.invbin214 -> 0 bytes
-rw-r--r--distro/common/html/search.html82
-rw-r--r--distro/common/html/searchindex.js1
-rw-r--r--distro/common/man/man1/README.md58
-rw-r--r--distro/common/man/man1/chef-shell.1194
-rw-r--r--distro/common/man/man1/knife-bootstrap.1215
-rw-r--r--distro/common/man/man1/knife-client.1443
-rw-r--r--distro/common/man/man1/knife-configure.1161
-rw-r--r--distro/common/man/man1/knife-cookbook-site.1552
-rw-r--r--distro/common/man/man1/knife-cookbook.1770
-rw-r--r--distro/common/man/man1/knife-data-bag.1617
-rw-r--r--distro/common/man/man1/knife-delete.1127
-rw-r--r--distro/common/man/man1/knife-deps.1246
-rw-r--r--distro/common/man/man1/knife-diff.1226
-rw-r--r--distro/common/man/man1/knife-download.1258
-rw-r--r--distro/common/man/man1/knife-edit.1121
-rw-r--r--distro/common/man/man1/knife-environment.1508
-rw-r--r--distro/common/man/man1/knife-exec.1362
-rw-r--r--distro/common/man/man1/knife-index-rebuild.163
-rw-r--r--distro/common/man/man1/knife-list.1174
-rw-r--r--distro/common/man/man1/knife-node.1716
-rw-r--r--distro/common/man/man1/knife-raw.1172
-rw-r--r--distro/common/man/man1/knife-recipe-list.185
-rw-r--r--distro/common/man/man1/knife-role.1426
-rw-r--r--distro/common/man/man1/knife-search.1359
-rw-r--r--distro/common/man/man1/knife-serve.1109
-rw-r--r--distro/common/man/man1/knife-show.1160
-rw-r--r--distro/common/man/man1/knife-ssh.1284
-rw-r--r--distro/common/man/man1/knife-ssl-check.1207
-rw-r--r--distro/common/man/man1/knife-ssl-fetch.1207
-rw-r--r--distro/common/man/man1/knife-status.1234
-rw-r--r--distro/common/man/man1/knife-tag.1189
-rw-r--r--distro/common/man/man1/knife-upload.1280
-rw-r--r--distro/common/man/man1/knife-user.1356
-rw-r--r--distro/common/man/man1/knife-xargs.1189
-rw-r--r--distro/common/man/man1/knife.1332
-rw-r--r--distro/common/man/man8/chef-apply.886
-rw-r--r--distro/common/man/man8/chef-client.8398
-rw-r--r--distro/common/man/man8/chef-solo.8260
-rw-r--r--distro/common/markdown/README3
-rw-r--r--distro/common/markdown/man1/chef-shell.mkd195
-rw-r--r--distro/common/markdown/man1/knife-bootstrap.mkd141
-rw-r--r--distro/common/markdown/man1/knife-client.mkd103
-rw-r--r--distro/common/markdown/man1/knife-configure.mkd70
-rw-r--r--distro/common/markdown/man1/knife-cookbook-site.mkd123
-rw-r--r--distro/common/markdown/man1/knife-cookbook.mkd263
-rw-r--r--distro/common/markdown/man1/knife-data-bag.mkd121
-rw-r--r--distro/common/markdown/man1/knife-environment.mkd151
-rw-r--r--distro/common/markdown/man1/knife-exec.mkd42
-rw-r--r--distro/common/markdown/man1/knife-index.mkd30
-rw-r--r--distro/common/markdown/man1/knife-node.mkd130
-rw-r--r--distro/common/markdown/man1/knife-role.mkd85
-rw-r--r--distro/common/markdown/man1/knife-search.mkd180
-rw-r--r--distro/common/markdown/man1/knife-ssh.mkd69
-rw-r--r--distro/common/markdown/man1/knife-status.mkd36
-rw-r--r--distro/common/markdown/man1/knife-tag.mkd39
-rw-r--r--distro/common/markdown/man1/knife.mkd213
-rw-r--r--distro/common/markdown/man8/chef-client.mkd75
-rw-r--r--distro/common/markdown/man8/chef-expander.mkd81
-rw-r--r--distro/common/markdown/man8/chef-expanderctl.mkd57
-rw-r--r--distro/common/markdown/man8/chef-server-webui.mkd121
-rw-r--r--distro/common/markdown/man8/chef-server.mkd120
-rw-r--r--distro/common/markdown/man8/chef-solo.mkd107
-rw-r--r--distro/common/markdown/man8/chef-solr.mkd89
-rw-r--r--distro/powershell/chef/Chef.PowerShell.dllbin0 -> 6144 bytes
-rw-r--r--distro/powershell/chef/Newtonsoft.Json.dllbin0 -> 664576 bytes
-rw-r--r--distro/powershell/chef/chef.psm1182
-rw-r--r--habitat/config/client.rb18
-rw-r--r--habitat/default.toml34
-rw-r--r--habitat/hooks/init5
-rw-r--r--habitat/hooks/run6
-rw-r--r--habitat/plan.sh111
-rw-r--r--kitchen-tests/.kitchen.travis.yml135
-rw-r--r--kitchen-tests/.kitchen.yml44
-rw-r--r--kitchen-tests/Berksfile10
-rw-r--r--kitchen-tests/Berksfile.lock59
-rw-r--r--kitchen-tests/Gemfile18
-rw-r--r--kitchen-tests/Gemfile.lock173
-rw-r--r--kitchen-tests/cookbooks/audit_test/Berksfile2
-rw-r--r--kitchen-tests/cookbooks/audit_test/chefignore95
-rw-r--r--kitchen-tests/cookbooks/audit_test/metadata.rb2
-rw-r--r--kitchen-tests/cookbooks/awesome_customers_rhel_wrapper/metadata.rb9
-rw-r--r--kitchen-tests/cookbooks/awesome_customers_rhel_wrapper/recipes/default.rb8
-rw-r--r--kitchen-tests/cookbooks/awesome_customers_ubuntu_wrapper/metadata.rb9
-rw-r--r--kitchen-tests/cookbooks/awesome_customers_ubuntu_wrapper/recipes/default.rb8
-rw-r--r--kitchen-tests/cookbooks/base/Berksfile3
-rw-r--r--kitchen-tests/cookbooks/base/README.md3
-rw-r--r--kitchen-tests/cookbooks/base/attributes/default.rb76
-rw-r--r--kitchen-tests/cookbooks/base/libraries/chef-sugar.rb4
-rw-r--r--kitchen-tests/cookbooks/base/metadata.rb33
-rw-r--r--kitchen-tests/cookbooks/base/recipes/default.rb87
-rw-r--r--kitchen-tests/cookbooks/base/recipes/packages.rb24
-rw-r--r--kitchen-tests/cookbooks/base/recipes/tests.rb21
-rw-r--r--kitchen-tests/cookbooks/webapp/Berksfile5
-rw-r--r--kitchen-tests/cookbooks/webapp/README.md3
-rw-r--r--kitchen-tests/cookbooks/webapp/attributes/default.rb14
-rw-r--r--kitchen-tests/cookbooks/webapp/metadata.rb12
-rw-r--r--kitchen-tests/cookbooks/webapp/recipes/default.rb64
-rw-r--r--kitchen-tests/cookbooks/webapp/templates/default/index.html.erb5
-rw-r--r--kitchen-tests/cookbooks/webapp/templates/default/index.php.erb8
-rw-r--r--kitchen-tests/data_bags/passwords/webapp.json4
-rw-r--r--kitchen-tests/data_bags/users/adam.json9
-rw-r--r--kitchen-tests/test/fixtures/platforms/centos/5.json14
-rw-r--r--kitchen-tests/test/fixtures/platforms/centos/6.json14
-rw-r--r--kitchen-tests/test/fixtures/platforms/ubuntu/10.04.json14
-rw-r--r--kitchen-tests/test/fixtures/platforms/ubuntu/12.04.json14
-rw-r--r--kitchen-tests/test/fixtures/platforms/ubuntu/14.04.json14
-rw-r--r--kitchen-tests/test/fixtures/platforms/ubuntu/14.10.json14
-rw-r--r--kitchen-tests/test/fixtures/serverspec_helper.rb32
-rw-r--r--kitchen-tests/test/integration/webapp/serverspec/Gemfile4
-rw-r--r--kitchen-tests/test/integration/webapp/serverspec/Gemfile.lock19
-rw-r--r--kitchen-tests/test/integration/webapp/serverspec/localhost/default_spec.rb127
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/acl_entry.rb5
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb5
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/chef_server_root_dir.rb5
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/cookbook_frozen_error.rb20
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/default_environment_cannot_be_modified_error.rb20
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/file_system_error.rb20
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/must_delete_recursively_error.rb20
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/not_found_error.rb20
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/operation_failed_error.rb20
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/operation_not_allowed_error.rb20
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_acls_dir.rb5
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_client_keys_dir.rb5
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_entry.rb6
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_policies_dir.rb5
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/repository/file_system_root_dir.rb34
-rw-r--r--lib/chef.rb3
-rw-r--r--lib/chef/api_client.rb33
-rw-r--r--lib/chef/api_client/registration.rb12
-rw-r--r--lib/chef/api_client_v1.rb33
-rw-r--r--lib/chef/application.rb193
-rw-r--r--lib/chef/application/apply.rb28
-rw-r--r--lib/chef/application/client.rb115
-rw-r--r--lib/chef/application/exit_code.rb164
-rw-r--r--lib/chef/application/knife.rb18
-rw-r--r--lib/chef/application/solo.rb86
-rw-r--r--lib/chef/application/windows_service.rb79
-rw-r--r--lib/chef/application/windows_service_manager.rb12
-rw-r--r--lib/chef/audit/audit_event_proxy.rb6
-rw-r--r--lib/chef/audit/audit_reporter.rb28
-rw-r--r--lib/chef/audit/control_group_data.rb2
-rw-r--r--lib/chef/audit/runner.rb6
-rw-r--r--lib/chef/blacklist.rb81
-rw-r--r--lib/chef/chef_class.rb22
-rw-r--r--lib/chef/chef_fs/chef_fs_data_store.rb19
-rw-r--r--lib/chef/chef_fs/command_line.rb68
-rw-r--r--lib/chef/chef_fs/config.rb4
-rw-r--r--lib/chef/chef_fs/data_handler/acl_data_handler.rb2
-rw-r--r--lib/chef/chef_fs/data_handler/client_data_handler.rb2
-rw-r--r--lib/chef/chef_fs/data_handler/container_data_handler.rb2
-rw-r--r--lib/chef/chef_fs/data_handler/cookbook_data_handler.rb2
-rw-r--r--lib/chef/chef_fs/data_handler/data_bag_item_data_handler.rb7
-rw-r--r--lib/chef/chef_fs/data_handler/data_handler_base.rb31
-rw-r--r--lib/chef/chef_fs/data_handler/environment_data_handler.rb4
-rw-r--r--lib/chef/chef_fs/data_handler/group_data_handler.rb2
-rw-r--r--lib/chef/chef_fs/data_handler/node_data_handler.rb2
-rw-r--r--lib/chef/chef_fs/data_handler/organization_data_handler.rb2
-rw-r--r--lib/chef/chef_fs/data_handler/organization_invites_data_handler.rb2
-rw-r--r--lib/chef/chef_fs/data_handler/policy_data_handler.rb2
-rw-r--r--lib/chef/chef_fs/data_handler/policy_group_data_handler.rb2
-rw-r--r--lib/chef/chef_fs/data_handler/role_data_handler.rb4
-rw-r--r--lib/chef/chef_fs/data_handler/user_data_handler.rb2
-rw-r--r--lib/chef/chef_fs/file_pattern.rb6
-rw-r--r--lib/chef/chef_fs/file_system.rb296
-rw-r--r--lib/chef/chef_fs/file_system/base_fs_object.rb4
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/acl_dir.rb2
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/acl_entry.rb9
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/chef_server_root_dir.rb4
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/cookbook_artifacts_dir.rb8
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/cookbook_dir.rb52
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/cookbook_file.rb22
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/cookbooks_dir.rb6
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/data_bag_dir.rb6
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/data_bag_entry.rb19
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/data_bags_dir.rb18
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/environments_dir.rb2
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/nodes_dir.rb22
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/org_entry.rb4
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/organization_invites_entry.rb4
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/organization_members_entry.rb4
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/policies_dir.rb60
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/policy_group_entry.rb2
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/policy_revision_entry.rb4
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/rest_list_dir.rb58
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/rest_list_entry.rb79
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/versioned_cookbook_dir.rb2
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/versioned_cookbooks_dir.rb6
-rw-r--r--lib/chef/chef_fs/file_system/exceptions.rb7
-rw-r--r--lib/chef/chef_fs/file_system/memory/memory_file.rb2
-rw-r--r--lib/chef/chef_fs/file_system/multiplexed_dir.rb24
-rw-r--r--lib/chef/chef_fs/file_system/nonexistent_fs_object.rb4
-rw-r--r--lib/chef/chef_fs/file_system/repository/acl.rb7
-rw-r--r--lib/chef/chef_fs/file_system/repository/acls_dir.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/acls_sub_dir.rb4
-rw-r--r--lib/chef/chef_fs/file_system/repository/base_file.rb43
-rw-r--r--lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_dir.rb29
-rw-r--r--lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb27
-rw-r--r--lib/chef/chef_fs/file_system/repository/chef_repository_file_system_root_dir.rb15
-rw-r--r--lib/chef/chef_fs/file_system/repository/client_keys_sub_dir.rb4
-rw-r--r--lib/chef/chef_fs/file_system/repository/clients_dir.rb3
-rw-r--r--lib/chef/chef_fs/file_system/repository/containers_dir.rb4
-rw-r--r--lib/chef/chef_fs/file_system/repository/cookbooks_dir.rb2
-rw-r--r--lib/chef/chef_fs/file_system/repository/directory.rb17
-rw-r--r--lib/chef/chef_fs/file_system/repository/environments_dir.rb4
-rw-r--r--lib/chef/chef_fs/file_system/repository/groups_dir.rb4
-rw-r--r--lib/chef/chef_fs/file_system/repository/nodes_dir.rb26
-rw-r--r--lib/chef/chef_fs/file_system/repository/policy_groups_dir.rb4
-rw-r--r--lib/chef/chef_fs/file_system/repository/roles_dir.rb4
-rw-r--r--lib/chef/chef_fs/file_system/repository/users_dir.rb4
-rw-r--r--lib/chef/chef_fs/file_system_cache.rb80
-rw-r--r--lib/chef/chef_fs/parallelizer.rb20
-rw-r--r--lib/chef/chef_fs/parallelizer/parallel_enumerable.rb8
-rw-r--r--lib/chef/chef_fs/path_utils.rb4
-rw-r--r--lib/chef/client.rb174
-rw-r--r--lib/chef/config.rb3
-rw-r--r--lib/chef/config_fetcher.rb10
-rw-r--r--lib/chef/constants.rb2
-rw-r--r--lib/chef/cookbook/chefignore.rb6
-rw-r--r--lib/chef/cookbook/cookbook_collection.rb8
-rw-r--r--lib/chef/cookbook/cookbook_version_loader.rb193
-rw-r--r--lib/chef/cookbook/file_system_file_vendor.rb17
-rw-r--r--lib/chef/cookbook/gem_installer.rb23
-rw-r--r--lib/chef/cookbook/manifest_v0.rb68
-rw-r--r--lib/chef/cookbook/manifest_v2.rb (renamed from lib/chef/platform/handler_map.rb)39
-rw-r--r--lib/chef/cookbook/metadata.rb184
-rw-r--r--lib/chef/cookbook/remote_file_vendor.rb12
-rw-r--r--lib/chef/cookbook/synchronizer.rb74
-rw-r--r--lib/chef/cookbook/syntax_check.rb10
-rw-r--r--lib/chef/cookbook_loader.rb14
-rw-r--r--lib/chef/cookbook_manifest.rb164
-rw-r--r--lib/chef/cookbook_site_streaming_uploader.rb26
-rw-r--r--lib/chef/cookbook_uploader.rb9
-rw-r--r--lib/chef/cookbook_version.rb244
-rw-r--r--lib/chef/data_bag.rb20
-rw-r--r--lib/chef/data_bag_item.rb25
-rw-r--r--lib/chef/data_collector.rb565
-rw-r--r--lib/chef/data_collector/messages.rb100
-rw-r--r--lib/chef/data_collector/messages/helpers.rb159
-rw-r--r--lib/chef/data_collector/resource_report.rb123
-rw-r--r--lib/chef/decorator.rb81
-rw-r--r--lib/chef/decorator/lazy.rb60
-rw-r--r--lib/chef/decorator/lazy_array.rb59
-rw-r--r--lib/chef/decorator/unchain.rb43
-rw-r--r--lib/chef/deprecated.rb301
-rw-r--r--lib/chef/deprecation/mixin/template.rb48
-rw-r--r--lib/chef/deprecation/provider/cookbook_file.rb54
-rw-r--r--lib/chef/deprecation/provider/file.rb198
-rw-r--r--lib/chef/deprecation/provider/remote_directory.rb52
-rw-r--r--lib/chef/deprecation/provider/remote_file.rb85
-rw-r--r--lib/chef/deprecation/provider/template.rb63
-rw-r--r--lib/chef/deprecation/warnings.rb9
-rw-r--r--lib/chef/dsl/audit.rb4
-rw-r--r--lib/chef/dsl/cheffish.rb1
-rw-r--r--lib/chef/dsl/core.rb32
-rw-r--r--lib/chef/dsl/data_query.rb6
-rw-r--r--lib/chef/dsl/declare_resource.rb80
-rw-r--r--lib/chef/dsl/include_attribute.rb10
-rw-r--r--lib/chef/dsl/include_recipe.rb6
-rw-r--r--lib/chef/dsl/method_missing.rb75
-rw-r--r--lib/chef/dsl/platform_introspection.rb82
-rw-r--r--lib/chef/dsl/powershell.rb2
-rw-r--r--lib/chef/dsl/reboot_pending.rb11
-rw-r--r--lib/chef/dsl/recipe.rb41
-rw-r--r--lib/chef/dsl/resources.rb17
-rw-r--r--lib/chef/dsl/universal.rb52
-rw-r--r--lib/chef/encrypted_data_bag_item.rb4
-rw-r--r--lib/chef/encrypted_data_bag_item/decryptor.rb4
-rw-r--r--lib/chef/environment.rb35
-rw-r--r--lib/chef/event_dispatch/base.rb27
-rw-r--r--lib/chef/event_dispatch/dsl.rb4
-rw-r--r--lib/chef/event_loggers/windows_eventlog.rb2
-rw-r--r--lib/chef/exceptions.rb45
-rw-r--r--lib/chef/file_access_control/unix.rb66
-rw-r--r--lib/chef/file_access_control/windows.rb8
-rw-r--r--lib/chef/file_cache.rb2
-rw-r--r--lib/chef/file_content_management/content_base.rb4
-rw-r--r--lib/chef/file_content_management/deploy/cp.rb4
-rw-r--r--lib/chef/file_content_management/deploy/mv_unix.rb8
-rw-r--r--lib/chef/file_content_management/deploy/mv_windows.rb2
-rw-r--r--lib/chef/file_content_management/tempfile.rb20
-rw-r--r--lib/chef/formatters/base.rb18
-rw-r--r--lib/chef/formatters/doc.rb21
-rw-r--r--lib/chef/formatters/error_description.rb21
-rw-r--r--lib/chef/formatters/error_inspectors/api_error_formatting.rb9
-rw-r--r--lib/chef/formatters/error_inspectors/compile_error_inspector.rb6
-rw-r--r--lib/chef/formatters/error_mapper.rb12
-rw-r--r--lib/chef/guard_interpreter/default_guard_interpreter.rb8
-rw-r--r--lib/chef/guard_interpreter/resource_guard_interpreter.rb4
-rw-r--r--lib/chef/handler.rb37
-rw-r--r--lib/chef/handler/json_file.rb3
-rw-r--r--lib/chef/http.rb179
-rw-r--r--lib/chef/http/api_versions.rb56
-rw-r--r--lib/chef/http/auth_credentials.rb4
-rw-r--r--lib/chef/http/authenticator.rb21
-rw-r--r--lib/chef/http/basic_client.rb48
-rw-r--r--lib/chef/http/decompressor.rb12
-rw-r--r--lib/chef/http/http_request.rb13
-rw-r--r--lib/chef/http/json_input.rb4
-rw-r--r--lib/chef/http/json_output.rb7
-rw-r--r--lib/chef/http/socketless_chef_zero_client.rb9
-rw-r--r--lib/chef/http/validate_content_length.rb14
-rw-r--r--lib/chef/json_compat.rb110
-rw-r--r--lib/chef/key.rb114
-rw-r--r--lib/chef/knife.rb208
-rw-r--r--lib/chef/knife/bootstrap.rb54
-rw-r--r--lib/chef/knife/bootstrap/templates/chef-full.erb18
-rw-r--r--lib/chef/knife/client_delete.rb23
-rw-r--r--lib/chef/knife/client_key_create.rb3
-rw-r--r--lib/chef/knife/client_key_delete.rb1
-rw-r--r--lib/chef/knife/client_key_edit.rb1
-rw-r--r--lib/chef/knife/client_key_list.rb1
-rw-r--r--lib/chef/knife/client_key_show.rb1
-rw-r--r--lib/chef/knife/client_reregister.rb2
-rw-r--r--lib/chef/knife/configure.rb58
-rw-r--r--lib/chef/knife/configure_client.rb4
-rw-r--r--lib/chef/knife/cookbook_bulk_delete.rb2
-rw-r--r--lib/chef/knife/cookbook_create.rb431
-rw-r--r--lib/chef/knife/cookbook_download.rb11
-rw-r--r--lib/chef/knife/cookbook_metadata.rb6
-rw-r--r--lib/chef/knife/cookbook_show.rb24
-rw-r--r--lib/chef/knife/cookbook_site_download.rb18
-rw-r--r--lib/chef/knife/cookbook_site_install.rb26
-rw-r--r--lib/chef/knife/cookbook_site_list.rb9
-rw-r--r--lib/chef/knife/cookbook_site_search.rb9
-rw-r--r--lib/chef/knife/cookbook_site_share.rb37
-rw-r--r--lib/chef/knife/cookbook_site_show.rb17
-rw-r--r--lib/chef/knife/cookbook_site_unshare.rb9
-rw-r--r--lib/chef/knife/cookbook_test.rb6
-rw-r--r--lib/chef/knife/cookbook_upload.rb12
-rw-r--r--lib/chef/knife/core/bootstrap_context.rb62
-rw-r--r--lib/chef/knife/core/cookbook_scm_repo.rb6
-rw-r--r--lib/chef/knife/core/custom_manifest_loader.rb69
-rw-r--r--lib/chef/knife/core/gem_glob_loader.rb8
-rw-r--r--lib/chef/knife/core/generic_presenter.rb50
-rw-r--r--lib/chef/knife/core/status_presenter.rb39
-rw-r--r--lib/chef/knife/core/subcommand_loader.rb36
-rw-r--r--lib/chef/knife/core/ui.rb51
-rw-r--r--lib/chef/knife/data_bag_create.rb10
-rw-r--r--lib/chef/knife/data_bag_secret_options.rb4
-rw-r--r--lib/chef/knife/data_bag_show.rb2
-rw-r--r--lib/chef/knife/deps.rb66
-rw-r--r--lib/chef/knife/edit.rb4
-rw-r--r--lib/chef/knife/environment_compare.rb8
-rw-r--r--lib/chef/knife/exec.rb6
-rw-r--r--lib/chef/knife/help.rb101
-rw-r--r--lib/chef/knife/help_topics.rb4
-rw-r--r--lib/chef/knife/index_rebuild.rb133
-rw-r--r--lib/chef/knife/list.rb7
-rw-r--r--lib/chef/knife/node_delete.rb12
-rw-r--r--lib/chef/knife/node_policy_set.rb79
-rw-r--r--lib/chef/knife/node_run_list_add.rb2
-rw-r--r--lib/chef/knife/node_run_list_remove.rb2
-rw-r--r--lib/chef/knife/node_show.rb5
-rw-r--r--lib/chef/knife/osc_user_reregister.rb2
-rw-r--r--lib/chef/knife/osc_user_show.rb1
-rw-r--r--lib/chef/knife/role_env_run_list_add.rb2
-rw-r--r--lib/chef/knife/role_run_list_add.rb2
-rw-r--r--lib/chef/knife/search.rb37
-rw-r--r--lib/chef/knife/ssh.rb236
-rw-r--r--lib/chef/knife/ssl_check.rb11
-rw-r--r--lib/chef/knife/ssl_fetch.rb16
-rw-r--r--lib/chef/knife/status.rb4
-rw-r--r--lib/chef/knife/supermarket_download.rb33
-rw-r--r--lib/chef/knife/supermarket_install.rb33
-rw-r--r--lib/chef/knife/supermarket_list.rb (renamed from lib/chef/resource/easy_install_package.rb)21
-rw-r--r--lib/chef/knife/supermarket_search.rb (renamed from lib/chef/provider/breakpoint.rb)29
-rw-r--r--lib/chef/knife/supermarket_share.rb33
-rw-r--r--lib/chef/knife/supermarket_show.rb33
-rw-r--r--lib/chef/knife/supermarket_unshare.rb33
-rw-r--r--lib/chef/knife/user_create.rb2
-rw-r--r--lib/chef/knife/user_delete.rb4
-rw-r--r--lib/chef/knife/user_edit.rb2
-rw-r--r--lib/chef/knife/user_key_create.rb1
-rw-r--r--lib/chef/knife/user_key_delete.rb1
-rw-r--r--lib/chef/knife/user_key_edit.rb1
-rw-r--r--lib/chef/knife/user_key_list.rb1
-rw-r--r--lib/chef/knife/user_key_show.rb1
-rw-r--r--lib/chef/knife/user_reregister.rb4
-rw-r--r--lib/chef/knife/user_show.rb2
-rw-r--r--lib/chef/knife/xargs.rb4
-rw-r--r--lib/chef/local_mode.rb1
-rw-r--r--lib/chef/log.rb2
-rw-r--r--lib/chef/mash.rb8
-rw-r--r--lib/chef/mixin/api_version_request_handling.rb6
-rw-r--r--lib/chef/mixin/checksum.rb5
-rw-r--r--lib/chef/mixin/command.rb193
-rw-r--r--lib/chef/mixin/command/unix.rb220
-rw-r--r--lib/chef/mixin/command/windows.rb71
-rw-r--r--lib/chef/mixin/create_path.rb8
-rw-r--r--lib/chef/mixin/deep_merge.rb4
-rw-r--r--lib/chef/mixin/deprecation.rb42
-rw-r--r--lib/chef/mixin/from_file.rb9
-rw-r--r--lib/chef/mixin/get_source_from_package.rb8
-rw-r--r--lib/chef/mixin/homebrew_user.rb15
-rw-r--r--lib/chef/mixin/language.rb48
-rw-r--r--lib/chef/mixin/notifying_block.rb16
-rw-r--r--lib/chef/mixin/openssl_helper.rb119
-rw-r--r--lib/chef/mixin/params_validate.rb45
-rw-r--r--lib/chef/mixin/path_sanity.rb36
-rw-r--r--lib/chef/mixin/powershell_exec.rb105
-rw-r--r--lib/chef/mixin/powershell_out.rb4
-rw-r--r--lib/chef/mixin/powershell_type_coercions.rb40
-rw-r--r--lib/chef/mixin/properties.rb20
-rw-r--r--lib/chef/mixin/provides.rb5
-rw-r--r--lib/chef/mixin/recipe_definition_dsl_core.rb35
-rw-r--r--lib/chef/mixin/securable.rb12
-rw-r--r--lib/chef/mixin/shell_out.rb139
-rw-r--r--lib/chef/mixin/template.rb4
-rw-r--r--lib/chef/mixin/unformatter.rb4
-rw-r--r--lib/chef/mixin/uris.rb11
-rw-r--r--lib/chef/mixin/user_context.rb55
-rw-r--r--lib/chef/mixin/versioned_api.rb84
-rw-r--r--lib/chef/mixin/which.rb41
-rw-r--r--lib/chef/mixin/why_run.rb17
-rw-r--r--lib/chef/mixin/wide_string.rb2
-rw-r--r--lib/chef/mixin/windows_architecture_helper.rb7
-rw-r--r--lib/chef/mixin/xml_escape.rb8
-rw-r--r--lib/chef/mixins.rb1
-rw-r--r--lib/chef/monkey_patches/net-ssh-multi.rb141
-rw-r--r--lib/chef/monkey_patches/net_http.rb14
-rw-r--r--lib/chef/monkey_patches/webrick-utils.rb6
-rw-r--r--lib/chef/monkey_patches/win32/registry.rb11
-rw-r--r--lib/chef/monologger.rb88
-rw-r--r--lib/chef/node.rb145
-rw-r--r--lib/chef/node/attribute.rb405
-rw-r--r--lib/chef/node/attribute_collections.rb218
-rw-r--r--lib/chef/node/common_api.rb121
-rw-r--r--lib/chef/node/immutable_collections.rb213
-rw-r--r--lib/chef/node/mixin/deep_merge_cache.rb61
-rw-r--r--lib/chef/node/mixin/immutablize_array.rb175
-rw-r--r--lib/chef/node/mixin/immutablize_hash.rb162
-rw-r--r--lib/chef/node/mixin/state_tracking.rb96
-rw-r--r--lib/chef/node_map.rb135
-rw-r--r--lib/chef/null_logger.rb7
-rw-r--r--lib/chef/org.rb27
-rw-r--r--lib/chef/platform/provider_handler_map.rb4
-rw-r--r--lib/chef/platform/provider_mapping.rb163
-rw-r--r--lib/chef/platform/query_helpers.rb14
-rw-r--r--lib/chef/platform/rebooter.rb28
-rw-r--r--lib/chef/platform/resource_handler_map.rb4
-rw-r--r--lib/chef/platform/service_helpers.rb2
-rw-r--r--lib/chef/policy_builder/dynamic.rb4
-rw-r--r--lib/chef/policy_builder/expand_node_object.rb38
-rw-r--r--lib/chef/policy_builder/policyfile.rb62
-rw-r--r--lib/chef/powershell.rb55
-rw-r--r--lib/chef/property.rb272
-rw-r--r--lib/chef/provider.rb233
-rw-r--r--lib/chef/provider/apt_preference.rb94
-rw-r--r--lib/chef/provider/apt_repository.rb221
-rw-r--r--lib/chef/provider/apt_update.rb17
-rw-r--r--lib/chef/provider/batch.rb6
-rw-r--r--lib/chef/provider/cookbook_file.rb12
-rw-r--r--lib/chef/provider/cookbook_file/content.rb2
-rw-r--r--lib/chef/provider/cron.rb109
-rw-r--r--lib/chef/provider/cron/unix.rb6
-rw-r--r--lib/chef/provider/deploy.rb476
-rw-r--r--lib/chef/provider/deploy/revision.rb109
-rw-r--r--lib/chef/provider/directory.rb72
-rw-r--r--lib/chef/provider/dsc_resource.rb42
-rw-r--r--lib/chef/provider/dsc_script.rb28
-rw-r--r--lib/chef/provider/env.rb169
-rw-r--r--lib/chef/provider/env/windows.rb72
-rw-r--r--lib/chef/provider/erl_call.rb108
-rw-r--r--lib/chef/provider/execute.rb32
-rw-r--r--lib/chef/provider/file.rb129
-rw-r--r--lib/chef/provider/git.rb193
-rw-r--r--lib/chef/provider/group.rb95
-rw-r--r--lib/chef/provider/group/aix.rb31
-rw-r--r--lib/chef/provider/group/dscl.rb93
-rw-r--r--lib/chef/provider/group/gpasswd.rb14
-rw-r--r--lib/chef/provider/group/groupadd.rb64
-rw-r--r--lib/chef/provider/group/groupmod.rb57
-rw-r--r--lib/chef/provider/group/pw.rb59
-rw-r--r--lib/chef/provider/group/suse.rb35
-rw-r--r--lib/chef/provider/group/usermod.rb21
-rw-r--r--lib/chef/provider/group/windows.rb38
-rw-r--r--lib/chef/provider/http_request.rb92
-rw-r--r--lib/chef/provider/ifconfig.rb217
-rw-r--r--lib/chef/provider/ifconfig/aix.rb53
-rw-r--r--lib/chef/provider/ifconfig/debian.rb47
-rw-r--r--lib/chef/provider/ifconfig/redhat.rb30
-rw-r--r--lib/chef/provider/launchd.rb31
-rw-r--r--lib/chef/provider/link.rb109
-rw-r--r--lib/chef/provider/log.rb26
-rw-r--r--lib/chef/provider/lwrp_base.rb14
-rw-r--r--lib/chef/provider/mdadm.rb58
-rw-r--r--lib/chef/provider/mount.rb42
-rw-r--r--lib/chef/provider/mount/aix.rb82
-rw-r--r--lib/chef/provider/mount/mount.rb58
-rw-r--r--lib/chef/provider/mount/solaris.rb47
-rw-r--r--lib/chef/provider/mount/windows.rb14
-rw-r--r--lib/chef/provider/noop.rb4
-rw-r--r--lib/chef/provider/ohai.rb12
-rw-r--r--lib/chef/provider/osx_profile.rb92
-rw-r--r--lib/chef/provider/package.rb328
-rw-r--r--lib/chef/provider/package/aix.rb136
-rw-r--r--lib/chef/provider/package/apt.rb256
-rw-r--r--lib/chef/provider/package/bff.rb142
-rw-r--r--lib/chef/provider/package/cab.rb183
-rw-r--r--lib/chef/provider/package/chocolatey.rb76
-rw-r--r--lib/chef/provider/package/dnf.rb196
-rw-r--r--lib/chef/provider/package/dnf/dnf_helper.py100
-rw-r--r--lib/chef/provider/package/dnf/python_helper.rb172
-rw-r--r--lib/chef/provider/package/dnf/version.rb56
-rw-r--r--lib/chef/provider/package/dpkg.rb55
-rw-r--r--lib/chef/provider/package/easy_install.rb135
-rw-r--r--lib/chef/provider/package/freebsd/base.rb20
-rw-r--r--lib/chef/provider/package/freebsd/pkg.rb30
-rw-r--r--lib/chef/provider/package/freebsd/pkgng.rb30
-rw-r--r--lib/chef/provider/package/freebsd/port.rb14
-rw-r--r--lib/chef/provider/package/homebrew.rb25
-rw-r--r--lib/chef/provider/package/ips.rb44
-rw-r--r--lib/chef/provider/package/macports.rb48
-rw-r--r--lib/chef/provider/package/msu.rb161
-rw-r--r--lib/chef/provider/package/openbsd.rb53
-rw-r--r--lib/chef/provider/package/pacman.rb35
-rw-r--r--lib/chef/provider/package/paludis.rb55
-rw-r--r--lib/chef/provider/package/portage.rb93
-rw-r--r--lib/chef/provider/package/powershell.rb129
-rw-r--r--lib/chef/provider/package/rpm.rb61
-rw-r--r--lib/chef/provider/package/rubygems.rb210
-rw-r--r--lib/chef/provider/package/smartos.rb36
-rw-r--r--lib/chef/provider/package/solaris.rb91
-rw-r--r--lib/chef/provider/package/windows.rb39
-rw-r--r--lib/chef/provider/package/windows/exe.rb30
-rw-r--r--lib/chef/provider/package/windows/msi.rb33
-rw-r--r--lib/chef/provider/package/windows/registry_uninstall_entry.rb34
-rw-r--r--lib/chef/provider/package/yum-dump.py307
-rw-r--r--lib/chef/provider/package/yum.rb1484
-rw-r--r--lib/chef/provider/package/yum/python_helper.rb221
-rw-r--r--lib/chef/provider/package/yum/rpm_utils.rb651
-rw-r--r--lib/chef/provider/package/yum/simplejson/LICENSE.txt79
-rw-r--r--lib/chef/provider/package/yum/simplejson/__init__.py318
-rw-r--r--lib/chef/provider/package/yum/simplejson/__init__.pycbin0 -> 12059 bytes
-rw-r--r--lib/chef/provider/package/yum/simplejson/decoder.py354
-rw-r--r--lib/chef/provider/package/yum/simplejson/decoder.pycbin0 -> 11088 bytes
-rw-r--r--lib/chef/provider/package/yum/simplejson/encoder.py440
-rw-r--r--lib/chef/provider/package/yum/simplejson/encoder.pycbin0 -> 13588 bytes
-rw-r--r--lib/chef/provider/package/yum/simplejson/scanner.py65
-rw-r--r--lib/chef/provider/package/yum/simplejson/scanner.pycbin0 -> 2405 bytes
-rw-r--r--lib/chef/provider/package/yum/simplejson/tool.py37
-rw-r--r--lib/chef/provider/package/yum/version.rb56
-rw-r--r--lib/chef/provider/package/yum/yum_cache.rb93
-rw-r--r--lib/chef/provider/package/yum/yum_helper.py210
-rw-r--r--lib/chef/provider/package/zypper.rb80
-rw-r--r--lib/chef/provider/powershell_script.rb26
-rw-r--r--lib/chef/provider/reboot.rb36
-rw-r--r--lib/chef/provider/registry_key.rb129
-rw-r--r--lib/chef/provider/remote_directory.rb26
-rw-r--r--lib/chef/provider/remote_file.rb31
-rw-r--r--lib/chef/provider/remote_file/cache_control_data.rb2
-rw-r--r--lib/chef/provider/remote_file/content.rb10
-rw-r--r--lib/chef/provider/remote_file/fetcher.rb3
-rw-r--r--lib/chef/provider/remote_file/ftp.rb4
-rw-r--r--lib/chef/provider/remote_file/http.rb18
-rw-r--r--lib/chef/provider/remote_file/local_file.rb2
-rw-r--r--lib/chef/provider/remote_file/network_file.rb23
-rw-r--r--lib/chef/provider/remote_file/sftp.rb4
-rw-r--r--lib/chef/provider/route.rb391
-rw-r--r--lib/chef/provider/ruby_block.rb12
-rw-r--r--lib/chef/provider/script.rb48
-rw-r--r--lib/chef/provider/service.rb109
-rw-r--r--lib/chef/provider/service/aix.rb12
-rw-r--r--lib/chef/provider/service/aixinit.rb4
-rw-r--r--lib/chef/provider/service/arch.rb4
-rw-r--r--lib/chef/provider/service/debian.rb65
-rw-r--r--lib/chef/provider/service/freebsd.rb9
-rw-r--r--lib/chef/provider/service/gentoo.rb9
-rw-r--r--lib/chef/provider/service/init.rb1
-rw-r--r--lib/chef/provider/service/insserv.rb8
-rw-r--r--lib/chef/provider/service/macosx.rb27
-rw-r--r--lib/chef/provider/service/openbsd.rb5
-rw-r--r--lib/chef/provider/service/redhat.rb8
-rw-r--r--lib/chef/provider/service/simple.rb26
-rw-r--r--lib/chef/provider/service/solaris.rb19
-rw-r--r--lib/chef/provider/service/systemd.rb37
-rw-r--r--lib/chef/provider/service/upstart.rb86
-rw-r--r--lib/chef/provider/service/windows.rb321
-rw-r--r--lib/chef/provider/subversion.rb86
-rw-r--r--lib/chef/provider/support/yum_repo.erb138
-rw-r--r--lib/chef/provider/support/zypper_repo.erb17
-rw-r--r--lib/chef/provider/systemd_unit.rb119
-rw-r--r--lib/chef/provider/template.rb12
-rw-r--r--lib/chef/provider/template/content.rb25
-rw-r--r--lib/chef/provider/template_finder.rb2
-rw-r--r--lib/chef/provider/user.rb157
-rw-r--r--lib/chef/provider/user/aix.rb54
-rw-r--r--lib/chef/provider/user/dscl.rb130
-rw-r--r--lib/chef/provider/user/linux.rb126
-rw-r--r--lib/chef/provider/user/pw.rb73
-rw-r--r--lib/chef/provider/user/solaris.rb58
-rw-r--r--lib/chef/provider/user/useradd.rb63
-rw-r--r--lib/chef/provider/user/windows.rb47
-rw-r--r--lib/chef/provider/whyrun_safe_ruby_block.rb8
-rw-r--r--lib/chef/provider/windows_env.rb207
-rw-r--r--lib/chef/provider/windows_path.rb61
-rw-r--r--lib/chef/provider/windows_script.rb7
-rw-r--r--lib/chef/provider/windows_task.rb587
-rw-r--r--lib/chef/provider/yum_repository.rb130
-rw-r--r--lib/chef/provider/zypper_repository.rb169
-rw-r--r--lib/chef/provider_resolver.rb56
-rw-r--r--lib/chef/providers.rb27
-rw-r--r--lib/chef/recipe.rb25
-rw-r--r--lib/chef/resource.rb447
-rw-r--r--lib/chef/resource/action_class.rb28
-rw-r--r--lib/chef/resource/apt_package.rb13
-rw-r--r--lib/chef/resource/apt_preference.rb51
-rw-r--r--lib/chef/resource/apt_repository.rb75
-rw-r--r--lib/chef/resource/apt_update.rb13
-rw-r--r--lib/chef/resource/bash.rb1
-rw-r--r--lib/chef/resource/batch.rb3
-rw-r--r--lib/chef/resource/bff_package.rb6
-rw-r--r--lib/chef/resource/breakpoint.rb15
-rw-r--r--lib/chef/resource/build_essential.rb142
-rw-r--r--lib/chef/resource/cab_package.rb45
-rw-r--r--lib/chef/resource/chef_gem.rb29
-rw-r--r--lib/chef/resource/chef_handler.rb130
-rw-r--r--lib/chef/resource/chocolatey_package.rb28
-rw-r--r--lib/chef/resource/conditional.rb10
-rw-r--r--lib/chef/resource/cookbook_file.rb21
-rw-r--r--lib/chef/resource/cron.rb85
-rw-r--r--lib/chef/resource/csh.rb6
-rw-r--r--lib/chef/resource/deploy.rb443
-rw-r--r--lib/chef/resource/directory.rb35
-rw-r--r--lib/chef/resource/dmg_package.rb162
-rw-r--r--lib/chef/resource/dnf_package.rb82
-rw-r--r--lib/chef/resource/dpkg_package.rb6
-rw-r--r--lib/chef/resource/dsc_resource.rb30
-rw-r--r--lib/chef/resource/dsc_script.rb47
-rw-r--r--lib/chef/resource/erl_call.rb85
-rw-r--r--lib/chef/resource/execute.rb178
-rw-r--r--lib/chef/resource/file.rb17
-rw-r--r--lib/chef/resource/file/verification.rb27
-rw-r--r--lib/chef/resource/file/verification/systemd_unit.rb67
-rw-r--r--lib/chef/resource/freebsd_package.rb17
-rw-r--r--lib/chef/resource/gem_package.rb25
-rw-r--r--lib/chef/resource/git.rb17
-rw-r--r--lib/chef/resource/group.rb52
-rw-r--r--lib/chef/resource/homebrew_cask.rb99
-rw-r--r--lib/chef/resource/homebrew_package.rb3
-rw-r--r--lib/chef/resource/homebrew_tap.rb87
-rw-r--r--lib/chef/resource/hostname.rb249
-rw-r--r--lib/chef/resource/http_request.rb30
-rw-r--r--lib/chef/resource/ifconfig.rb136
-rw-r--r--lib/chef/resource/ips_package.rb6
-rw-r--r--lib/chef/resource/ksh.rb7
-rw-r--r--lib/chef/resource/launchd.rb75
-rw-r--r--lib/chef/resource/link.rb70
-rw-r--r--lib/chef/resource/log.rb63
-rw-r--r--lib/chef/resource/lwrp_base.rb11
-rw-r--r--lib/chef/resource/macos_userdefaults.rb133
-rw-r--r--lib/chef/resource/macosx_service.rb30
-rw-r--r--lib/chef/resource/macports_package.rb2
-rw-r--r--lib/chef/resource/mdadm.rb93
-rw-r--r--lib/chef/resource/mount.rb168
-rw-r--r--lib/chef/resource/msu_package.rb46
-rw-r--r--lib/chef/resource/ohai.rb32
-rw-r--r--lib/chef/resource/ohai_hint.rb93
-rw-r--r--lib/chef/resource/openbsd_package.rb3
-rw-r--r--lib/chef/resource/openssl_dhparam.rb81
-rw-r--r--lib/chef/resource/openssl_rsa_private_key.rb93
-rw-r--r--lib/chef/resource/openssl_rsa_public_key.rb74
-rw-r--r--lib/chef/resource/osx_profile.rb55
-rw-r--r--lib/chef/resource/package.rb14
-rw-r--r--lib/chef/resource/pacman_package.rb4
-rw-r--r--lib/chef/resource/paludis_package.rb5
-rw-r--r--lib/chef/resource/perl.rb6
-rw-r--r--lib/chef/resource/portage_package.rb8
-rw-r--r--lib/chef/resource/powershell_package.rb42
-rw-r--r--lib/chef/resource/powershell_script.rb11
-rw-r--r--lib/chef/resource/python.rb5
-rw-r--r--lib/chef/resource/reboot.rb31
-rw-r--r--lib/chef/resource/registry_key.rb45
-rw-r--r--lib/chef/resource/remote_directory.rb55
-rw-r--r--lib/chef/resource/remote_file.rb113
-rw-r--r--lib/chef/resource/resource_notification.rb19
-rw-r--r--lib/chef/resource/rhsm_errata.rb45
-rw-r--r--lib/chef/resource/rhsm_errata_level.rb52
-rw-r--r--lib/chef/resource/rhsm_register.rb165
-rw-r--r--lib/chef/resource/rhsm_repo.rb63
-rw-r--r--lib/chef/resource/rhsm_subscription.rb96
-rw-r--r--lib/chef/resource/route.rb123
-rw-r--r--lib/chef/resource/rpm_package.rb5
-rw-r--r--lib/chef/resource/ruby.rb6
-rw-r--r--lib/chef/resource/ruby_block.rb20
-rw-r--r--lib/chef/resource/scm.rb168
-rw-r--r--lib/chef/resource/script.rb50
-rw-r--r--lib/chef/resource/service.rb40
-rw-r--r--lib/chef/resource/smartos_package.rb6
-rw-r--r--lib/chef/resource/solaris_package.rb4
-rw-r--r--lib/chef/resource/subversion.rb24
-rw-r--r--lib/chef/resource/sudo.rb230
-rw-r--r--lib/chef/resource/support/sudoer.erb18
-rw-r--r--lib/chef/resource/swap_file.rb209
-rw-r--r--lib/chef/resource/sysctl.rb115
-rw-r--r--lib/chef/resource/systemd_unit.rb22
-rw-r--r--lib/chef/resource/template.rb42
-rw-r--r--lib/chef/resource/user.rb19
-rw-r--r--lib/chef/resource/user/aix_user.rb (renamed from lib/chef/resource/timestamped_deploy.rb)15
-rw-r--r--lib/chef/resource/user/dscl_user.rb (renamed from lib/chef/provider/deploy/timestamped.rb)21
-rw-r--r--lib/chef/resource/user/linux_user.rb (renamed from lib/chef/resource/deploy_revision.rb)17
-rw-r--r--lib/chef/resource/user/pw_user.rb (renamed from lib/chef/mixin/language_include_recipe.rb)20
-rw-r--r--lib/chef/resource/user/solaris_user.rb31
-rw-r--r--lib/chef/resource/user/windows_user.rb31
-rw-r--r--lib/chef/resource/windows_ad_join.rb92
-rw-r--r--lib/chef/resource/windows_auto_run.rb87
-rw-r--r--lib/chef/resource/windows_env.rb (renamed from lib/chef/resource/env.rb)49
-rw-r--r--lib/chef/resource/windows_feature.rb95
-rw-r--r--lib/chef/resource/windows_feature_dism.rb226
-rw-r--r--lib/chef/resource/windows_feature_powershell.rb256
-rw-r--r--lib/chef/resource/windows_font.rb127
-rw-r--r--lib/chef/resource/windows_package.rb11
-rw-r--r--lib/chef/resource/windows_pagefile.rb206
-rw-r--r--lib/chef/resource/windows_path.rb36
-rw-r--r--lib/chef/resource/windows_printer.rb148
-rw-r--r--lib/chef/resource/windows_printer_port.rb136
-rw-r--r--lib/chef/resource/windows_service.rb114
-rw-r--r--lib/chef/resource/windows_shortcut.rb79
-rw-r--r--lib/chef/resource/windows_task.rb281
-rw-r--r--lib/chef/resource/yum_package.rb56
-rw-r--r--lib/chef/resource/yum_repository.rb92
-rw-r--r--lib/chef/resource/zypper_package.rb7
-rw-r--r--lib/chef/resource/zypper_repository.rb64
-rw-r--r--lib/chef/resource_builder.rb63
-rw-r--r--lib/chef/resource_collection.rb10
-rw-r--r--lib/chef/resource_collection/resource_collection_serialization.rb15
-rw-r--r--lib/chef/resource_collection/resource_list.rb6
-rw-r--r--lib/chef/resource_collection/resource_set.rb61
-rw-r--r--lib/chef/resource_collection/stepable_iterator.rb4
-rw-r--r--lib/chef/resource_inspector.rb92
-rw-r--r--lib/chef/resource_reporter.rb34
-rw-r--r--lib/chef/resource_resolver.rb35
-rw-r--r--lib/chef/resources.rb51
-rw-r--r--lib/chef/rest.rb209
-rw-r--r--lib/chef/role.rb10
-rw-r--r--lib/chef/run_context.rb150
-rw-r--r--lib/chef/run_context/cookbook_compiler.rb82
-rw-r--r--lib/chef/run_list.rb4
-rw-r--r--lib/chef/run_list/run_list_item.rb2
-rw-r--r--lib/chef/run_list/versioned_recipe_list.rb26
-rw-r--r--lib/chef/runner.rb3
-rw-r--r--lib/chef/scan_access_control.rb8
-rw-r--r--lib/chef/search/query.rb80
-rw-r--r--lib/chef/server_api.rb5
-rw-r--r--lib/chef/server_api_versions.rb59
-rw-r--r--lib/chef/shell.rb43
-rw-r--r--lib/chef/shell/ext.rb14
-rw-r--r--lib/chef/shell/model_wrapper.rb2
-rw-r--r--lib/chef/shell/shell_session.rb39
-rw-r--r--lib/chef/shell_out.rb13
-rw-r--r--lib/chef/tasks/chef_repo.rake200
-rw-r--r--lib/chef/user.rb29
-rw-r--r--lib/chef/user_v1.rb25
-rw-r--r--lib/chef/util/diff.rb6
-rw-r--r--lib/chef/util/dsc/configuration_generator.rb4
-rw-r--r--lib/chef/util/dsc/lcm_output_parser.rb63
-rw-r--r--lib/chef/util/dsc/local_configuration_manager.rb60
-rw-r--r--lib/chef/util/dsc/resource_store.rb2
-rw-r--r--lib/chef/util/powershell/cmdlet.rb7
-rw-r--r--lib/chef/util/selinux.rb13
-rw-r--r--lib/chef/util/windows/logon_session.rb129
-rw-r--r--lib/chef/util/windows/net_group.rb48
-rw-r--r--lib/chef/util/windows/net_use.rb18
-rw-r--r--lib/chef/util/windows/net_user.rb30
-rw-r--r--lib/chef/util/windows/volume.rb28
-rw-r--r--lib/chef/version.rb4
-rw-r--r--lib/chef/version/platform.rb18
-rw-r--r--lib/chef/version_class.rb11
-rw-r--r--lib/chef/version_constraint.rb4
-rw-r--r--lib/chef/version_constraint/platform.rb2
-rw-r--r--lib/chef/version_string.rb143
-rw-r--r--lib/chef/win32/api.rb10
-rw-r--r--lib/chef/win32/api/error.rb4
-rw-r--r--lib/chef/win32/api/file.rb57
-rw-r--r--lib/chef/win32/api/installer.rb4
-rw-r--r--lib/chef/win32/api/net.rb2
-rw-r--r--lib/chef/win32/api/security.rb37
-rw-r--r--lib/chef/win32/eventlog.rb4
-rw-r--r--lib/chef/win32/file.rb20
-rw-r--r--lib/chef/win32/memory.rb2
-rw-r--r--lib/chef/win32/mutex.rb2
-rw-r--r--lib/chef/win32/net.rb2
-rw-r--r--lib/chef/win32/registry.rb52
-rw-r--r--lib/chef/win32/security.rb84
-rw-r--r--lib/chef/win32/security/acl.rb4
-rw-r--r--lib/chef/win32/security/sid.rb14
-rw-r--r--lib/chef/win32/unicode.rb4
-rw-r--r--lib/chef/win32/version.rb25
-rw-r--r--lib/chef/win32_service_constants.rb143
-rw-r--r--omnibus/.kitchen.yml51
-rw-r--r--omnibus/Gemfile19
-rw-r--r--omnibus/Gemfile.lock279
-rw-r--r--omnibus/README.md2
-rw-r--r--omnibus/config/projects/angrychef.rb2
-rw-r--r--omnibus/config/projects/chef.rb53
-rw-r--r--omnibus/config/software/chef-appbundle.rb14
-rw-r--r--omnibus/config/software/chef-complete.rb20
-rw-r--r--omnibus/config/software/chef-gem-binding_of_caller.rb6
-rw-r--r--omnibus/config/software/chef-gem-byebug.rb6
-rw-r--r--omnibus/config/software/chef-gem-debug_inspector.rb6
-rw-r--r--omnibus/config/software/chef-gem-ffi-yajl.rb8
-rw-r--r--omnibus/config/software/chef-gem-ffi.rb6
-rw-r--r--omnibus/config/software/chef-gem-json.rb6
-rw-r--r--omnibus/config/software/chef-gem-libyajl2.rb6
-rw-r--r--omnibus/config/software/chef-gem-mini_portile2.rb6
-rw-r--r--omnibus/config/software/chef-gem-nokogiri.rb8
-rw-r--r--omnibus/config/software/chef-gem-ruby-prof.rb6
-rw-r--r--omnibus/config/software/chef-gem-ruby-shadow.rb6
-rw-r--r--omnibus/config/software/chef-remove-docs.rb33
-rw-r--r--omnibus/config/software/chef.rb90
-rw-r--r--omnibus/config/software/unf_ext.rb23
-rw-r--r--omnibus/files/chef-appbundle/build-chef-appbundle.rb93
-rw-r--r--omnibus/files/chef-gem/build-chef-gem.rb123
-rw-r--r--omnibus/files/chef-gem/build-chef-gem/gem-install-software-def.rb118
-rw-r--r--omnibus/files/chef/build-chef.rb134
-rw-r--r--omnibus/files/mapfiles/solaris18
-rw-r--r--omnibus/omnibus.rb9
-rw-r--r--omnibus/resources/chef/ips/chef-symlinks.erb6
-rw-r--r--omnibus/resources/chef/msi/localization-en-us.wxl.erb12
-rw-r--r--omnibus/resources/chef/msi/source.wxs.erb186
-rw-r--r--omnibus_overrides.rb24
-rw-r--r--pkg.rb1
-rw-r--r--rubygems-pkg/rubygems-update-2.4.6.gembin451072 -> 0 bytes
-rw-r--r--spec/data/client.d_00/02-strings.rb2
-rw-r--r--spec/data/cookbooks/openldap/templates/default/openldap_nested_variable_stuff.erb1
-rw-r--r--spec/data/lwrp/providers/buck_passer.rb2
-rw-r--r--spec/data/lwrp/providers/buck_passer_2.rb2
-rw-r--r--spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb2
-rw-r--r--spec/data/lwrp/providers/inline_compiler.rb2
-rw-r--r--spec/data/lwrp/resources/buck_passer.rb5
-rw-r--r--spec/data/lwrp/resources/buck_passer_2.rb3
-rw-r--r--spec/data/lwrp/resources/embedded_resource_accesses_providers_scope.rb3
-rw-r--r--spec/data/lwrp/resources/inline_compiler.rb3
-rw-r--r--spec/data/lwrp/resources/monkey_name_printer.rb5
-rw-r--r--spec/data/lwrp/resources/paint_drying_watcher.rb3
-rw-r--r--spec/data/lwrp/resources/thumb_twiddler.rb3
-rw-r--r--spec/data/mixin/invalid_data.rb3
-rw-r--r--spec/data/mixin/real_data.rb2
-rw-r--r--spec/data/prefer_metadata_json/metadata.json51
-rw-r--r--spec/data/prefer_metadata_json/metadata.rb6
-rw-r--r--spec/data/prefer_metadata_json/recipes/default.rb0
-rw-r--r--spec/data/root_alias_cookbooks/dup_attr/attributes.rb1
-rw-r--r--spec/data/root_alias_cookbooks/dup_attr/attributes/default.rb1
-rw-r--r--spec/data/root_alias_cookbooks/dup_attr/metadata.rb2
-rw-r--r--spec/data/root_alias_cookbooks/dup_attr/recipe.rb3
-rw-r--r--spec/data/root_alias_cookbooks/dup_recipe/attributes.rb1
-rw-r--r--spec/data/root_alias_cookbooks/dup_recipe/metadata.rb2
-rw-r--r--spec/data/root_alias_cookbooks/dup_recipe/recipe.rb3
-rw-r--r--spec/data/root_alias_cookbooks/dup_recipe/recipes/default.rb3
-rw-r--r--spec/data/root_alias_cookbooks/simple/attributes.rb1
-rw-r--r--spec/data/root_alias_cookbooks/simple/metadata.rb2
-rw-r--r--spec/data/root_alias_cookbooks/simple/recipe.rb3
-rw-r--r--spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb6
-rw-r--r--spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb5
-rw-r--r--spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb4
-rw-r--r--spec/data/run_context/cookbooks/dependency1/attributes/default.rb4
-rw-r--r--spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb5
-rw-r--r--spec/data/run_context/cookbooks/dependency2/attributes/default.rb5
-rw-r--r--spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb5
-rw-r--r--spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb5
-rw-r--r--spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb5
-rw-r--r--spec/data/sample_msu1.xml10
-rw-r--r--spec/data/sample_msu2.xml14
-rw-r--r--spec/data/sample_msu3.xml16
-rw-r--r--spec/data/shef-config.rb21
-rw-r--r--spec/data/templates/chef-seattle20160930-4388-1crv7ef.txt1
-rw-r--r--spec/data/templates/chef-seattle20160930-4388-jjfoae.txt1
-rw-r--r--spec/data/templates/chef-seattle20160930-4388-umeq2c.txt1
-rw-r--r--spec/data/trusted_certs/example_no_cn.crt36
-rwxr-xr-xspec/functional/assets/chefinittest10
-rw-r--r--spec/functional/assets/chocolatey_feed/test-A.1.0.nupkgbin2667 -> 2678 bytes
-rw-r--r--spec/functional/assets/chocolatey_feed/test-A.1.5.nupkgbin2669 -> 2679 bytes
-rw-r--r--spec/functional/assets/chocolatey_feed/test-A.2.0.nupkgbin2667 -> 2678 bytes
-rw-r--r--spec/functional/assets/chocolatey_feed/test-B.1.0.nupkgbin2667 -> 2678 bytes
-rwxr-xr-xspec/functional/assets/testchefsubsys5
-rw-r--r--spec/functional/assets/yumrepo/chef_rpm-1.10-1.aarch64.rpmbin0 -> 2096 bytes
-rw-r--r--spec/functional/assets/yumrepo/chef_rpm-1.10-1.i686.rpmbin0 -> 2148 bytes
-rw-r--r--spec/functional/assets/yumrepo/chef_rpm-1.10-1.ppc64.rpmbin0 -> 2118 bytes
-rw-r--r--spec/functional/assets/yumrepo/chef_rpm-1.10-1.ppc64le.rpmbin0 -> 2098 bytes
-rw-r--r--spec/functional/assets/yumrepo/chef_rpm-1.10-1.s390x.rpmbin0 -> 2102 bytes
-rw-r--r--spec/functional/assets/yumrepo/chef_rpm-1.10-1.src.rpmbin0 -> 2132 bytes
-rw-r--r--spec/functional/assets/yumrepo/chef_rpm-1.10-1.x86_64.rpmbin0 -> 2110 bytes
-rw-r--r--spec/functional/assets/yumrepo/chef_rpm-1.2-1.aarch64.rpmbin0 -> 2096 bytes
-rw-r--r--spec/functional/assets/yumrepo/chef_rpm-1.2-1.i686.rpmbin0 -> 2149 bytes
-rw-r--r--spec/functional/assets/yumrepo/chef_rpm-1.2-1.ppc64.rpmbin0 -> 2119 bytes
-rw-r--r--spec/functional/assets/yumrepo/chef_rpm-1.2-1.ppc64le.rpmbin0 -> 2098 bytes
-rw-r--r--spec/functional/assets/yumrepo/chef_rpm-1.2-1.s390x.rpmbin0 -> 2102 bytes
-rw-r--r--spec/functional/assets/yumrepo/chef_rpm-1.2-1.src.rpmbin0 -> 2133 bytes
-rw-r--r--spec/functional/assets/yumrepo/chef_rpm-1.2-1.x86_64.rpmbin0 -> 2113 bytes
-rw-r--r--spec/functional/assets/yumrepo/repodata/4632d67cb92636e7575d911c24f0e04d3505a944e97c483abe0c3e73a7c62d33-filelists.sqlite.bz2bin0 -> 1322 bytes
-rw-r--r--spec/functional/assets/yumrepo/repodata/74599b793e54d877323837d2d81a1c3c594c44e4335f9528234bb490f7b9b439-other.xml.gzbin0 -> 502 bytes
-rw-r--r--spec/functional/assets/yumrepo/repodata/a845d418f919d2115ab95a56b2c76f6825ad0d0bede49181a55c04f58995d057-primary.sqlite.bz2bin0 -> 2569 bytes
-rw-r--r--spec/functional/assets/yumrepo/repodata/af9b7cf9ef23bd7b43068d74a460f3b5d06753d638e58e4a0c9edc35bfb9cdc4-other.sqlite.bz2bin0 -> 1115 bytes
-rw-r--r--spec/functional/assets/yumrepo/repodata/bdb4f5f1492a3b9532f22c43110a81500dd744f23da0aec5c33b2a41317c737d-filelists.xml.gzbin0 -> 529 bytes
-rw-r--r--spec/functional/assets/yumrepo/repodata/c10d1d34ce99e02f12ec96ef68360543ab1bb7c3cb81a4a2bf78df7d8597e9df-primary.xml.gzbin0 -> 966 bytes
-rw-r--r--spec/functional/assets/yumrepo/repodata/filelists.xml.gzbin0 -> 773 bytes
-rw-r--r--spec/functional/assets/yumrepo/repodata/other.xml.gzbin0 -> 742 bytes
-rw-r--r--spec/functional/assets/yumrepo/repodata/primary.xml.gzbin0 -> 1459 bytes
-rw-r--r--spec/functional/assets/yumrepo/repodata/repomd.xml21
-rw-r--r--spec/functional/event_loggers/windows_eventlog_spec.rb23
-rw-r--r--spec/functional/file_content_management/deploy_strategies_spec.rb2
-rw-r--r--spec/functional/http/simple_spec.rb20
-rw-r--r--spec/functional/knife/cookbook_delete_spec.rb103
-rw-r--r--spec/functional/knife/exec_spec.rb12
-rw-r--r--spec/functional/knife/ssh_spec.rb103
-rw-r--r--spec/functional/mixin/from_file_spec.rb82
-rw-r--r--spec/functional/mixin/powershell_out_spec.rb6
-rw-r--r--spec/functional/mixin/user_context_spec.rb117
-rw-r--r--spec/functional/notifications_spec.rb6
-rw-r--r--spec/functional/provider/whyrun_safe_ruby_block_spec.rb4
-rw-r--r--spec/functional/rebooter_spec.rb42
-rw-r--r--spec/functional/resource/apt_package_spec.rb (renamed from spec/functional/resource/package_spec.rb)10
-rw-r--r--spec/functional/resource/bash_spec.rb60
-rw-r--r--spec/functional/resource/batch_spec.rb6
-rw-r--r--spec/functional/resource/bff_spec.rb6
-rw-r--r--spec/functional/resource/chocolatey_package_spec.rb14
-rw-r--r--spec/functional/resource/cron_spec.rb13
-rw-r--r--spec/functional/resource/deploy_revision_spec.rb881
-rw-r--r--spec/functional/resource/dnf_package_spec.rb686
-rw-r--r--spec/functional/resource/dpkg_package_spec.rb4
-rw-r--r--spec/functional/resource/dsc_resource_spec.rb10
-rw-r--r--spec/functional/resource/dsc_script_spec.rb57
-rwxr-xr-xspec/functional/resource/env_spec.rb192
-rw-r--r--spec/functional/resource/execute_spec.rb29
-rw-r--r--spec/functional/resource/group_spec.rb71
-rw-r--r--spec/functional/resource/ifconfig_spec.rb9
-rw-r--r--spec/functional/resource/link_spec.rb132
-rw-r--r--spec/functional/resource/mount_spec.rb10
-rw-r--r--spec/functional/resource/msu_package_spec.rb84
-rw-r--r--spec/functional/resource/ohai_spec.rb12
-rw-r--r--spec/functional/resource/powershell_script_spec.rb2
-rw-r--r--spec/functional/resource/reboot_spec.rb4
-rw-r--r--spec/functional/resource/registry_spec.rb138
-rw-r--r--spec/functional/resource/remote_file_spec.rb200
-rw-r--r--spec/functional/resource/rpm_spec.rb17
-rw-r--r--spec/functional/resource/template_spec.rb39
-rw-r--r--spec/functional/resource/user/dscl_spec.rb12
-rw-r--r--spec/functional/resource/user/useradd_spec.rb75
-rw-r--r--spec/functional/resource/user/windows_spec.rb4
-rw-r--r--spec/functional/resource/windows_env_spec.rb285
-rw-r--r--spec/functional/resource/windows_path_spec.rb64
-rw-r--r--spec/functional/resource/windows_service_spec.rb30
-rw-r--r--spec/functional/resource/windows_task_spec.rb1454
-rw-r--r--spec/functional/resource/yum_package_spec.rb957
-rw-r--r--spec/functional/rest_spec.rb95
-rw-r--r--spec/functional/root_alias_spec.rb78
-rw-r--r--spec/functional/run_lock_spec.rb45
-rw-r--r--spec/functional/shell_spec.rb80
-rw-r--r--spec/functional/tiny_server_spec.rb17
-rw-r--r--spec/functional/win32/crypto_spec.rb2
-rw-r--r--spec/functional/win32/registry_spec.rb1
-rw-r--r--spec/functional/win32/security_spec.rb118
-rw-r--r--spec/functional/win32/versions_spec.rb16
-rw-r--r--spec/integration/client/client_spec.rb273
-rw-r--r--spec/integration/client/exit_code_spec.rb141
-rw-r--r--spec/integration/client/ipv6_spec.rb2
-rw-r--r--spec/integration/knife/chef_repo_path_spec.rb75
-rw-r--r--spec/integration/knife/chef_repository_file_system_spec.rb97
-rw-r--r--spec/integration/knife/client_bulk_delete_spec.rb130
-rw-r--r--spec/integration/knife/client_create_spec.rb69
-rw-r--r--spec/integration/knife/client_delete_spec.rb63
-rw-r--r--spec/integration/knife/client_key_create_spec.rb65
-rw-r--r--spec/integration/knife/client_key_delete_spec.rb42
-rw-r--r--spec/integration/knife/client_key_list_spec.rb60
-rw-r--r--spec/integration/knife/client_key_show_spec.rb44
-rw-r--r--spec/integration/knife/client_list_spec.rb48
-rw-r--r--spec/integration/knife/client_show_spec.rb36
-rw-r--r--spec/integration/knife/common_options_spec.rb40
-rw-r--r--spec/integration/knife/cookbook_bulk_delete_spec.rb64
-rw-r--r--spec/integration/knife/cookbook_download_spec.rb71
-rw-r--r--spec/integration/knife/cookbook_list_spec.rb54
-rw-r--r--spec/integration/knife/cookbook_show_spec.rb148
-rw-r--r--spec/integration/knife/cookbook_upload_spec.rb90
-rw-r--r--spec/integration/knife/data_bag_create_spec.rb55
-rw-r--r--spec/integration/knife/data_bag_delete_spec.rb58
-rw-r--r--spec/integration/knife/data_bag_from_file_spec.rb115
-rw-r--r--spec/integration/knife/data_bag_list_spec.rb43
-rw-r--r--spec/integration/knife/data_bag_show_spec.rb53
-rw-r--r--spec/integration/knife/deps_spec.rb32
-rw-r--r--spec/integration/knife/diff_spec.rb4
-rw-r--r--spec/integration/knife/download_spec.rb27
-rw-r--r--spec/integration/knife/environment_compare_spec.rb74
-rw-r--r--spec/integration/knife/environment_create_spec.rb40
-rw-r--r--spec/integration/knife/environment_delete_spec.rb36
-rw-r--r--spec/integration/knife/environment_from_file_spec.rb115
-rw-r--r--spec/integration/knife/environment_list_spec.rb41
-rw-r--r--spec/integration/knife/environment_show_spec.rb76
-rw-r--r--spec/integration/knife/list_spec.rb8
-rw-r--r--spec/integration/knife/node_bulk_delete_spec.rb51
-rw-r--r--spec/integration/knife/node_create_spec.rb46
-rw-r--r--spec/integration/knife/node_delete_spec.rb47
-rw-r--r--spec/integration/knife/node_environment_set_spec.rb45
-rw-r--r--spec/integration/knife/node_from_file_spec.rb58
-rw-r--r--spec/integration/knife/node_list_spec.rb44
-rw-r--r--spec/integration/knife/node_run_list_add_spec.rb53
-rw-r--r--spec/integration/knife/node_run_list_remove_spec.rb35
-rw-r--r--spec/integration/knife/node_run_list_set_spec.rb40
-rw-r--r--spec/integration/knife/node_show_spec.rb35
-rw-r--r--spec/integration/knife/raw_spec.rb2
-rw-r--r--spec/integration/knife/role_bulk_delete_spec.rb51
-rw-r--r--spec/integration/knife/role_create_spec.rb40
-rw-r--r--spec/integration/knife/role_delete_spec.rb47
-rw-r--r--spec/integration/knife/role_from_file_spec.rb95
-rw-r--r--spec/integration/knife/role_list_spec.rb44
-rw-r--r--spec/integration/knife/role_show_spec.rb50
-rw-r--r--spec/integration/knife/search_node_spec.rb39
-rw-r--r--spec/integration/knife/serve_spec.rb80
-rw-r--r--spec/integration/knife/upload_spec.rb45
-rw-r--r--spec/integration/recipes/accumulator_spec.rb232
-rw-r--r--spec/integration/recipes/lwrp_inline_resources_spec.rb30
-rw-r--r--spec/integration/recipes/noop_resource_spec.rb8
-rw-r--r--spec/integration/recipes/notifies_spec.rb60
-rw-r--r--spec/integration/recipes/provider_choice.rb6
-rw-r--r--spec/integration/recipes/recipe_dsl_spec.rb916
-rw-r--r--spec/integration/recipes/resource_action_spec.rb387
-rw-r--r--spec/integration/recipes/resource_converge_if_changed_spec.rb154
-rw-r--r--spec/integration/recipes/resource_load_spec.rb74
-rw-r--r--spec/integration/solo/solo_spec.rb103
-rw-r--r--spec/scripts/ssl-serve.rb2
-rw-r--r--spec/spec_helper.rb98
-rw-r--r--spec/stress/win32/security_spec.rb8
-rw-r--r--spec/support/chef_helpers.rb34
-rw-r--r--spec/support/lib/chef/provider/snakeoil.rb1
-rw-r--r--spec/support/lib/chef/resource/cat.rb1
-rw-r--r--spec/support/lib/chef/resource/one_two_three_four.rb3
-rw-r--r--spec/support/lib/chef/resource/openldap_includer.rb2
-rw-r--r--spec/support/lib/chef/resource/with_state.rb2
-rw-r--r--spec/support/lib/chef/resource/zen_master.rb5
-rw-r--r--spec/support/mock/constant.rb2
-rw-r--r--spec/support/platform_helpers.rb81
-rw-r--r--spec/support/platforms/prof/gc.rb10
-rw-r--r--spec/support/shared/context/client.rb52
-rw-r--r--spec/support/shared/examples/client.rb51
-rw-r--r--spec/support/shared/functional/execute_resource.rb150
-rw-r--r--spec/support/shared/functional/file_resource.rb11
-rw-r--r--spec/support/shared/functional/http.rb32
-rw-r--r--spec/support/shared/functional/securable_resource.rb10
-rw-r--r--spec/support/shared/functional/win32_service.rb12
-rw-r--r--spec/support/shared/functional/windows_script.rb78
-rw-r--r--spec/support/shared/integration/app_server_support.rb6
-rw-r--r--spec/support/shared/integration/knife_support.rb29
-rw-r--r--spec/support/shared/shared_examples.rb4
-rw-r--r--spec/support/shared/unit/api_versioning.rb4
-rw-r--r--spec/support/shared/unit/application_dot_d.rb17
-rw-r--r--spec/support/shared/unit/execute_resource.rb53
-rw-r--r--spec/support/shared/unit/provider/file.rb24
-rw-r--r--spec/support/shared/unit/provider/useradd_based_user_provider.rb137
-rw-r--r--spec/support/shared/unit/resource/static_provider_resolution.rb4
-rw-r--r--spec/support/shared/unit/script_resource.rb18
-rw-r--r--spec/support/shared/unit/user_and_client_shared.rb8
-rw-r--r--spec/tiny_server.rb117
-rw-r--r--spec/unit/api_client_v1_spec.rb4
-rw-r--r--spec/unit/application/apply_spec.rb10
-rw-r--r--spec/unit/application/client_spec.rb90
-rw-r--r--spec/unit/application/exit_code_spec.rb144
-rw-r--r--spec/unit/application/solo_spec.rb231
-rw-r--r--spec/unit/application_spec.rb137
-rw-r--r--spec/unit/audit/audit_event_proxy_spec.rb63
-rw-r--r--spec/unit/audit/audit_reporter_spec.rb87
-rw-r--r--spec/unit/audit/control_group_data_spec.rb33
-rw-r--r--spec/unit/chef_class_spec.rb2
-rw-r--r--spec/unit/chef_fs/config_spec.rb129
-rw-r--r--spec/unit/chef_fs/data_handler/data_bag_item_data_handler.rb82
-rw-r--r--spec/unit/chef_fs/data_handler/group_handler_spec.rb2
-rw-r--r--spec/unit/chef_fs/diff_spec.rb8
-rw-r--r--spec/unit/chef_fs/file_system/operation_failed_error_spec.rb10
-rw-r--r--spec/unit/chef_fs/file_system/repository/base_file_spec.rb18
-rw-r--r--spec/unit/chef_fs/file_system/repository/directory_spec.rb5
-rw-r--r--spec/unit/chef_fs/file_system_spec.rb6
-rw-r--r--spec/unit/chef_fs/parallelizer.rb12
-rw-r--r--spec/unit/client_spec.rb123
-rw-r--r--spec/unit/config_fetcher_spec.rb4
-rw-r--r--spec/unit/cookbook/cookbook_version_loader_spec.rb50
-rw-r--r--spec/unit/cookbook/file_vendor_spec.rb40
-rw-r--r--spec/unit/cookbook/gem_installer_spec.rb85
-rw-r--r--spec/unit/cookbook/manifest_v0_spec.rb133
-rw-r--r--spec/unit/cookbook/manifest_v2_spec.rb70
-rw-r--r--spec/unit/cookbook/metadata_spec.rb346
-rw-r--r--spec/unit/cookbook/synchronizer_spec.rb59
-rw-r--r--spec/unit/cookbook/syntax_check_spec.rb11
-rw-r--r--spec/unit/cookbook_loader_spec.rb72
-rw-r--r--spec/unit/cookbook_manifest_spec.rb79
-rw-r--r--spec/unit/cookbook_site_streaming_uploader_spec.rb4
-rw-r--r--spec/unit/cookbook_spec.rb24
-rw-r--r--spec/unit/cookbook_uploader_spec.rb2
-rw-r--r--spec/unit/cookbook_version_file_specificity_spec.rb99
-rw-r--r--spec/unit/cookbook_version_spec.rb155
-rw-r--r--spec/unit/daemon_spec.rb35
-rw-r--r--spec/unit/data_bag_item_spec.rb44
-rw-r--r--spec/unit/data_bag_spec.rb8
-rw-r--r--spec/unit/data_collector/messages/helpers_spec.rb202
-rw-r--r--spec/unit/data_collector/messages_spec.rb329
-rw-r--r--spec/unit/data_collector/resource_report_spec.rb145
-rw-r--r--spec/unit/data_collector_spec.rb875
-rw-r--r--spec/unit/decorator/lazy_array_spec.rb58
-rw-r--r--spec/unit/decorator/lazy_spec.rb39
-rw-r--r--spec/unit/decorator_spec.rb142
-rw-r--r--spec/unit/deprecated_spec.rb59
-rw-r--r--spec/unit/deprecation_spec.rb23
-rw-r--r--spec/unit/dsl/audit_spec.rb6
-rw-r--r--spec/unit/dsl/data_query_spec.rb5
-rw-r--r--spec/unit/dsl/declare_resource_spec.rb61
-rw-r--r--spec/unit/dsl/reboot_pending_spec.rb13
-rw-r--r--spec/unit/dsl/recipe_spec.rb13
-rw-r--r--spec/unit/encrypted_data_bag_item/check_encrypted_spec.rb5
-rw-r--r--spec/unit/encrypted_data_bag_item_spec.rb17
-rw-r--r--spec/unit/environment_spec.rb26
-rw-r--r--spec/unit/event_dispatch/dispatcher_spec.rb4
-rw-r--r--spec/unit/exceptions_spec.rb4
-rw-r--r--spec/unit/file_access_control_spec.rb6
-rw-r--r--spec/unit/file_content_management/tempfile_spec.rb48
-rw-r--r--spec/unit/formatters/base_spec.rb39
-rw-r--r--spec/unit/formatters/error_description_spec.rb59
-rw-r--r--spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb4
-rw-r--r--spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb4
-rw-r--r--spec/unit/handler_spec.rb91
-rw-r--r--spec/unit/http/api_versions_spec.rb82
-rw-r--r--spec/unit/http/authenticator_spec.rb20
-rw-r--r--spec/unit/http/basic_client_spec.rb20
-rw-r--r--spec/unit/http/socketless_chef_zero_client_spec.rb3
-rw-r--r--spec/unit/http/validate_content_length_spec.rb26
-rw-r--r--spec/unit/http_spec.rb25
-rw-r--r--spec/unit/json_compat_spec.rb32
-rw-r--r--spec/unit/knife/bootstrap/chef_vault_handler_spec.rb4
-rw-r--r--spec/unit/knife/bootstrap/client_builder_spec.rb4
-rw-r--r--spec/unit/knife/bootstrap_spec.rb83
-rw-r--r--spec/unit/knife/client_bulk_delete_spec.rb16
-rw-r--r--spec/unit/knife/client_delete_spec.rb16
-rw-r--r--spec/unit/knife/client_edit_spec.rb4
-rw-r--r--spec/unit/knife/configure_client_spec.rb6
-rw-r--r--spec/unit/knife/configure_spec.rb75
-rw-r--r--spec/unit/knife/cookbook_create_spec.rb222
-rw-r--r--spec/unit/knife/cookbook_download_spec.rb74
-rw-r--r--spec/unit/knife/cookbook_metadata_spec.rb229
-rw-r--r--spec/unit/knife/cookbook_show_spec.rb224
-rw-r--r--spec/unit/knife/cookbook_site_download_spec.rb1
-rw-r--r--spec/unit/knife/cookbook_site_install_spec.rb13
-rw-r--r--spec/unit/knife/cookbook_site_share_spec.rb12
-rw-r--r--spec/unit/knife/cookbook_test_spec.rb4
-rw-r--r--spec/unit/knife/core/bootstrap_context_spec.rb77
-rw-r--r--spec/unit/knife/core/cookbook_scm_repo_spec.rb2
-rw-r--r--spec/unit/knife/core/custom_manifest_loader_spec.rb41
-rw-r--r--spec/unit/knife/core/gem_glob_loader_spec.rb6
-rw-r--r--spec/unit/knife/core/hashed_command_loader_spec.rb9
-rw-r--r--spec/unit/knife/core/node_editor_spec.rb18
-rw-r--r--spec/unit/knife/core/subcommand_loader_spec.rb6
-rw-r--r--spec/unit/knife/core/ui_spec.rb69
-rw-r--r--spec/unit/knife/data_bag_create_spec.rb148
-rw-r--r--spec/unit/knife/data_bag_from_file_spec.rb7
-rw-r--r--spec/unit/knife/data_bag_show_spec.rb23
-rw-r--r--spec/unit/knife/environment_compare_spec.rb6
-rw-r--r--spec/unit/knife/index_rebuild_spec.rb125
-rw-r--r--spec/unit/knife/key_create_spec.rb28
-rw-r--r--spec/unit/knife/key_delete_spec.rb4
-rw-r--r--spec/unit/knife/key_edit_spec.rb32
-rw-r--r--spec/unit/knife/key_list_spec.rb12
-rw-r--r--spec/unit/knife/key_show_spec.rb8
-rw-r--r--spec/unit/knife/knife_help.rb92
-rw-r--r--spec/unit/knife/node_delete_spec.rb29
-rw-r--r--spec/unit/knife/node_edit_spec.rb4
-rw-r--r--spec/unit/knife/node_environment_set_spec.rb24
-rw-r--r--spec/unit/knife/node_policy_set_spec.rb122
-rw-r--r--spec/unit/knife/node_run_list_set_spec.rb25
-rw-r--r--spec/unit/knife/osc_user_reregister_spec.rb2
-rw-r--r--spec/unit/knife/osc_user_show_spec.rb2
-rw-r--r--spec/unit/knife/ssh_spec.rb204
-rw-r--r--spec/unit/knife/ssl_check_spec.rb16
-rw-r--r--spec/unit/knife/ssl_fetch_spec.rb38
-rw-r--r--spec/unit/knife/status_spec.rb7
-rw-r--r--spec/unit/knife/user_create_spec.rb8
-rw-r--r--spec/unit/knife_spec.rb72
-rw-r--r--spec/unit/lib_backcompat_spec.rb34
-rw-r--r--spec/unit/log/syslog_spec.rb6
-rw-r--r--spec/unit/log/winevt_spec.rb5
-rw-r--r--spec/unit/lwrp_spec.rb200
-rw-r--r--spec/unit/mixin/api_version_request_handling_spec.rb4
-rw-r--r--spec/unit/mixin/checksum_spec.rb14
-rw-r--r--spec/unit/mixin/command_spec.rb104
-rw-r--r--spec/unit/mixin/deprecation_spec.rb2
-rw-r--r--spec/unit/mixin/homebrew_user_spec.rb10
-rw-r--r--spec/unit/mixin/openssl_helper_spec.rb252
-rw-r--r--spec/unit/mixin/params_validate_spec.rb118
-rw-r--r--spec/unit/mixin/path_sanity_spec.rb14
-rw-r--r--spec/unit/mixin/powershell_exec_spec.rb43
-rw-r--r--spec/unit/mixin/powershell_out_spec.rb30
-rw-r--r--spec/unit/mixin/powershell_type_coercions_spec.rb17
-rw-r--r--spec/unit/mixin/properties_spec.rb14
-rw-r--r--spec/unit/mixin/shell_out_spec.rb103
-rw-r--r--spec/unit/mixin/user_context_spec.rb108
-rw-r--r--spec/unit/mixin/versioned_api_spec.rb128
-rw-r--r--spec/unit/mixin/which.rb160
-rw-r--r--spec/unit/mixin/xml_escape_spec.rb6
-rw-r--r--spec/unit/node/attribute_spec.rb173
-rw-r--r--spec/unit/node/immutable_collections_spec.rb140
-rw-r--r--spec/unit/node/vivid_mash_spec.rb353
-rw-r--r--spec/unit/node_map_spec.rb83
-rw-r--r--spec/unit/node_spec.rb456
-rw-r--r--spec/unit/platform/query_helpers_spec.rb19
-rw-r--r--spec/unit/platform_spec.rb241
-rw-r--r--spec/unit/policy_builder/dynamic_spec.rb2
-rw-r--r--spec/unit/policy_builder/expand_node_object_spec.rb29
-rw-r--r--spec/unit/policy_builder/policyfile_spec.rb157
-rw-r--r--spec/unit/property/validation_spec.rb178
-rw-r--r--spec/unit/property_spec.rb270
-rw-r--r--spec/unit/provider/apt_preference_spec.rb87
-rw-r--r--spec/unit/provider/apt_repository_spec.rb187
-rw-r--r--spec/unit/provider/apt_update_spec.rb14
-rw-r--r--spec/unit/provider/breakpoint_spec.rb53
-rw-r--r--spec/unit/provider/cookbook_file_spec.rb11
-rw-r--r--spec/unit/provider/cron/unix_spec.rb19
-rw-r--r--spec/unit/provider/cron_spec.rb149
-rw-r--r--spec/unit/provider/deploy/revision_spec.rb110
-rw-r--r--spec/unit/provider/deploy/timestamped_spec.rb40
-rw-r--r--spec/unit/provider/deploy_spec.rb641
-rw-r--r--spec/unit/provider/directory_spec.rb22
-rw-r--r--spec/unit/provider/dsc_resource_spec.rb200
-rw-r--r--spec/unit/provider/dsc_script_spec.rb18
-rw-r--r--spec/unit/provider/env/windows_spec.rb103
-rw-r--r--spec/unit/provider/erl_call_spec.rb85
-rw-r--r--spec/unit/provider/execute_spec.rb63
-rw-r--r--spec/unit/provider/file/content_spec.rb12
-rw-r--r--spec/unit/provider/file_spec.rb11
-rw-r--r--spec/unit/provider/git_spec.rb150
-rw-r--r--spec/unit/provider/group/dscl_spec.rb92
-rw-r--r--spec/unit/provider/group/gpasswd_spec.rb27
-rw-r--r--spec/unit/provider/group/groupadd_spec.rb232
-rw-r--r--spec/unit/provider/group/groupmod_spec.rb41
-rw-r--r--spec/unit/provider/group/pw_spec.rb35
-rw-r--r--spec/unit/provider/group/suse_spec.rb90
-rw-r--r--spec/unit/provider/group/usermod_spec.rb42
-rw-r--r--spec/unit/provider/group/windows_spec.rb6
-rw-r--r--spec/unit/provider/group_spec.rb8
-rw-r--r--spec/unit/provider/http_request_spec.rb2
-rw-r--r--spec/unit/provider/ifconfig/aix_spec.rb28
-rw-r--r--spec/unit/provider/ifconfig/debian_spec.rb32
-rw-r--r--spec/unit/provider/ifconfig/redhat_spec.rb16
-rw-r--r--spec/unit/provider/ifconfig_spec.rb48
-rw-r--r--spec/unit/provider/launchd_spec.rb91
-rw-r--r--spec/unit/provider/link_spec.rb159
-rw-r--r--spec/unit/provider/log_spec.rb45
-rw-r--r--spec/unit/provider/mount/aix_spec.rb34
-rw-r--r--spec/unit/provider/mount/mount_spec.rb68
-rw-r--r--spec/unit/provider/mount/solaris_spec.rb100
-rw-r--r--spec/unit/provider/mount_spec.rb7
-rw-r--r--spec/unit/provider/osx_profile_spec.rb6
-rw-r--r--spec/unit/provider/package/apt_spec.rb713
-rw-r--r--spec/unit/provider/package/bff_spec.rb (renamed from spec/unit/provider/package/aix_spec.rb)67
-rw-r--r--spec/unit/provider/package/cab_spec.rb272
-rw-r--r--spec/unit/provider/package/chocolatey_spec.rb81
-rw-r--r--spec/unit/provider/package/dnf/python_helper_spec.rb29
-rw-r--r--spec/unit/provider/package/dpkg_spec.rb32
-rw-r--r--spec/unit/provider/package/easy_install_spec.rb114
-rw-r--r--spec/unit/provider/package/freebsd/pkg_spec.rb30
-rw-r--r--spec/unit/provider/package/freebsd/pkgng_spec.rb18
-rw-r--r--spec/unit/provider/package/freebsd/port_spec.rb26
-rw-r--r--spec/unit/provider/package/homebrew_spec.rb21
-rw-r--r--spec/unit/provider/package/ips_spec.rb70
-rw-r--r--spec/unit/provider/package/macports_spec.rb28
-rw-r--r--spec/unit/provider/package/msu_spec.rb283
-rw-r--r--spec/unit/provider/package/openbsd_spec.rb20
-rw-r--r--spec/unit/provider/package/pacman_spec.rb16
-rw-r--r--spec/unit/provider/package/paludis_spec.rb14
-rw-r--r--spec/unit/provider/package/portage_spec.rb177
-rw-r--r--spec/unit/provider/package/powershell_spec.rb488
-rw-r--r--spec/unit/provider/package/rpm_spec.rb46
-rw-r--r--spec/unit/provider/package/rubygems_spec.rb346
-rw-r--r--spec/unit/provider/package/solaris_spec.rb30
-rw-r--r--spec/unit/provider/package/windows/exe_spec.rb32
-rw-r--r--spec/unit/provider/package/windows/msi_spec.rb19
-rw-r--r--spec/unit/provider/package/windows/registry_uninstall_entry_spec.rb78
-rw-r--r--spec/unit/provider/package/windows_spec.rb56
-rw-r--r--spec/unit/provider/package/yum/python_helper_spec.rb29
-rw-r--r--spec/unit/provider/package/yum/yum_cache_spec.rb109
-rw-r--r--spec/unit/provider/package/yum_spec.rb2267
-rw-r--r--spec/unit/provider/package/zypper_spec.rb273
-rw-r--r--spec/unit/provider/package_spec.rb148
-rw-r--r--spec/unit/provider/powershell_script_spec.rb34
-rw-r--r--spec/unit/provider/registry_key_spec.rb152
-rw-r--r--spec/unit/provider/remote_directory_spec.rb19
-rw-r--r--spec/unit/provider/remote_file/content_spec.rb3
-rw-r--r--spec/unit/provider/remote_file/fetcher_spec.rb1
-rw-r--r--spec/unit/provider/remote_file/ftp_spec.rb8
-rw-r--r--spec/unit/provider/remote_file/http_spec.rb48
-rw-r--r--spec/unit/provider/remote_file/local_file_spec.rb6
-rw-r--r--spec/unit/provider/remote_file/network_file_spec.rb9
-rw-r--r--spec/unit/provider/remote_file/sftp_spec.rb14
-rw-r--r--spec/unit/provider/remote_file_spec.rb11
-rw-r--r--spec/unit/provider/route_spec.rb86
-rw-r--r--spec/unit/provider/script_spec.rb65
-rw-r--r--spec/unit/provider/service/arch_service_spec.rb12
-rw-r--r--spec/unit/provider/service/debian_service_spec.rb212
-rw-r--r--spec/unit/provider/service/freebsd_service_spec.rb20
-rw-r--r--spec/unit/provider/service/init_service_spec.rb4
-rw-r--r--spec/unit/provider/service/invokercd_service_spec.rb2
-rw-r--r--spec/unit/provider/service/macosx_spec.rb13
-rw-r--r--spec/unit/provider/service/openbsd_service_spec.rb20
-rw-r--r--spec/unit/provider/service/simple_service_spec.rb2
-rw-r--r--spec/unit/provider/service/solaris_smf_service_spec.rb45
-rw-r--r--spec/unit/provider/service/systemd_service_spec.rb56
-rw-r--r--spec/unit/provider/service/upstart_service_spec.rb89
-rw-r--r--spec/unit/provider/service/windows_spec.rb493
-rw-r--r--spec/unit/provider/service_spec.rb2
-rw-r--r--spec/unit/provider/subversion_spec.rb13
-rw-r--r--spec/unit/provider/systemd_unit_spec.rb228
-rw-r--r--spec/unit/provider/template/content_spec.rb16
-rw-r--r--spec/unit/provider/template_spec.rb22
-rw-r--r--spec/unit/provider/user/aix_spec.rb97
-rw-r--r--spec/unit/provider/user/dscl_spec.rb235
-rw-r--r--spec/unit/provider/user/linux_spec.rb74
-rw-r--r--spec/unit/provider/user/pw_spec.rb83
-rw-r--r--spec/unit/provider/user/solaris_spec.rb133
-rw-r--r--spec/unit/provider/user/useradd_spec.rb51
-rw-r--r--spec/unit/provider/user/windows_spec.rb17
-rw-r--r--spec/unit/provider/user_spec.rb52
-rw-r--r--spec/unit/provider/windows_env_spec.rb (renamed from spec/unit/provider/env_spec.rb)118
-rw-r--r--spec/unit/provider/windows_path_spec.rb65
-rw-r--r--spec/unit/provider/windows_task_spec.rb420
-rw-r--r--spec/unit/provider/yum_repository_spec.rb35
-rw-r--r--spec/unit/provider/zypper_repository_spec.rb126
-rw-r--r--spec/unit/provider_resolver_spec.rb582
-rw-r--r--spec/unit/provider_spec.rb13
-rw-r--r--spec/unit/recipe_spec.rb153
-rw-r--r--spec/unit/resource/apt_package_spec.rb6
-rw-r--r--spec/unit/resource/apt_preference_spec.rb44
-rw-r--r--spec/unit/resource/apt_repository_spec.rb65
-rw-r--r--spec/unit/resource/apt_update_spec.rb35
-rw-r--r--spec/unit/resource/bash_spec.rb17
-rw-r--r--spec/unit/resource/batch_spec.rb7
-rw-r--r--spec/unit/resource/breakpoint_spec.rb39
-rw-r--r--spec/unit/resource/build_essential_spec.rb (renamed from spec/unit/resource/deploy_revision_spec.rb)33
-rw-r--r--spec/unit/resource/cab_package_spec.rb54
-rw-r--r--spec/unit/resource/chef_gem_spec.rb82
-rw-r--r--spec/unit/resource/chef_handler_spec.rb (renamed from lib/chef/mixin/language_include_attribute.rb)25
-rw-r--r--spec/unit/resource/chocolatey_package_spec.rb32
-rw-r--r--spec/unit/resource/conditional_spec.rb58
-rw-r--r--spec/unit/resource/cookbook_file_spec.rb56
-rw-r--r--spec/unit/resource/cron_spec.rb160
-rw-r--r--spec/unit/resource/csh_spec.rb17
-rw-r--r--spec/unit/resource/deploy_spec.rb283
-rw-r--r--spec/unit/resource/directory_spec.rb60
-rw-r--r--spec/unit/resource/dmg_package_spec.rb35
-rw-r--r--spec/unit/resource/dnf_package_spec.rb93
-rw-r--r--spec/unit/resource/dsc_resource_spec.rb22
-rw-r--r--spec/unit/resource/dsc_script_spec.rb8
-rw-r--r--spec/unit/resource/easy_install_package_spec.rb39
-rw-r--r--spec/unit/resource/erl_call_spec.rb81
-rw-r--r--spec/unit/resource/execute_spec.rb238
-rw-r--r--spec/unit/resource/file/verification/systemd_unit_spec.rb103
-rw-r--r--spec/unit/resource/file/verification_spec.rb44
-rw-r--r--spec/unit/resource/file_spec.rb111
-rw-r--r--spec/unit/resource/freebsd_package_spec.rb65
-rw-r--r--spec/unit/resource/gem_package_spec.rb10
-rw-r--r--spec/unit/resource/git_spec.rb17
-rw-r--r--spec/unit/resource/group_spec.rb131
-rw-r--r--spec/unit/resource/homebrew_cask_spec.rb35
-rw-r--r--spec/unit/resource/homebrew_package_spec.rb4
-rw-r--r--spec/unit/resource/homebrew_tap_spec.rb39
-rw-r--r--spec/unit/resource/hostname_spec.rb43
-rw-r--r--spec/unit/resource/http_request_spec.rb34
-rw-r--r--spec/unit/resource/ifconfig_spec.rb8
-rw-r--r--spec/unit/resource/ips_package_spec.rb10
-rw-r--r--spec/unit/resource/ksh_spec.rb17
-rw-r--r--spec/unit/resource/launchd_spec.rb29
-rw-r--r--spec/unit/resource/link_spec.rb107
-rw-r--r--spec/unit/resource/log_spec.rb51
-rw-r--r--spec/unit/resource/macos_user_defaults_spec.rb (renamed from spec/unit/resource/timestamped_deploy_spec.rb)21
-rw-r--r--spec/unit/resource/mdadm_spec.rb85
-rw-r--r--spec/unit/resource/mount_spec.rb213
-rw-r--r--spec/unit/resource/msu_package_spec.rb53
-rw-r--r--spec/unit/resource/ohai_hint_spec.rb43
-rw-r--r--spec/unit/resource/ohai_spec.rb31
-rw-r--r--spec/unit/resource/openbsd_package_spec.rb24
-rw-r--r--spec/unit/resource/openssl_dhparam.rb51
-rw-r--r--spec/unit/resource/openssl_rsa_private_key_spec.rb59
-rw-r--r--spec/unit/resource/openssl_rsa_public_key_spec.rb (renamed from lib/chef/knife/cookbook_site_vendor.rb)35
-rw-r--r--spec/unit/resource/osx_profile_spec.rb20
-rw-r--r--spec/unit/resource/package_spec.rb75
-rw-r--r--spec/unit/resource/perl_spec.rb18
-rw-r--r--spec/unit/resource/portage_package_spec.rb20
-rw-r--r--spec/unit/resource/powershell_package_spec.rb75
-rw-r--r--spec/unit/resource/powershell_script_spec.rb4
-rw-r--r--spec/unit/resource/python_spec.rb18
-rw-r--r--spec/unit/resource/reboot_spec.rb45
-rw-r--r--spec/unit/resource/registry_key_spec.rb189
-rw-r--r--spec/unit/resource/remote_directory_spec.rb84
-rw-r--r--spec/unit/resource/remote_file_spec.rb168
-rw-r--r--spec/unit/resource/rhsm_errata_level_spec.rb46
-rw-r--r--spec/unit/resource/rhsm_errata_spec.rb35
-rw-r--r--spec/unit/resource/rhsm_register_spec.rb199
-rw-r--r--spec/unit/resource/rhsm_repo_spec.rb59
-rw-r--r--spec/unit/resource/rhsm_subscription_spec.rb93
-rw-r--r--spec/unit/resource/route_spec.rb85
-rw-r--r--spec/unit/resource/rpm_package_spec.rb14
-rw-r--r--spec/unit/resource/ruby_block_spec.rb29
-rw-r--r--spec/unit/resource/ruby_spec.rb19
-rw-r--r--spec/unit/resource/scm_spec.rb138
-rw-r--r--spec/unit/resource/script_spec.rb4
-rw-r--r--spec/unit/resource/service_spec.rb188
-rw-r--r--spec/unit/resource/smartos_package_spec.rb5
-rw-r--r--spec/unit/resource/solaris_package_spec.rb8
-rw-r--r--spec/unit/resource/subversion_spec.rb46
-rw-r--r--spec/unit/resource/sudo_spec.rb92
-rw-r--r--spec/unit/resource/swap_file_spec.rb40
-rw-r--r--spec/unit/resource/sysctl_spec.rb56
-rw-r--r--spec/unit/resource/systemd_unit_spec.rb110
-rw-r--r--spec/unit/resource/template_spec.rb117
-rw-r--r--spec/unit/resource/user_spec.rb101
-rw-r--r--spec/unit/resource/windows_ad_join.rb45
-rw-r--r--spec/unit/resource/windows_auto_run_spec.rb50
-rw-r--r--spec/unit/resource/windows_env_spec.rb (renamed from spec/unit/resource/env_spec.rb)52
-rw-r--r--spec/unit/resource/windows_feature.rb41
-rw-r--r--spec/unit/resource/windows_feature_dism.rb51
-rw-r--r--spec/unit/resource/windows_feature_powershell.rb51
-rw-r--r--spec/unit/resource/windows_font_spec.rb44
-rw-r--r--spec/unit/resource/windows_package_spec.rb6
-rw-r--r--spec/unit/resource/windows_pagefile_spec.rb45
-rw-r--r--spec/unit/resource/windows_path_spec.rb41
-rw-r--r--spec/unit/resource/windows_printer_port_spec.rb45
-rw-r--r--spec/unit/resource/windows_printer_spec.rb45
-rw-r--r--spec/unit/resource/windows_service_spec.rb29
-rw-r--r--spec/unit/resource/windows_shortcut_spec.rb39
-rw-r--r--spec/unit/resource/windows_task_spec.rb336
-rw-r--r--spec/unit/resource/yum_package_spec.rb65
-rw-r--r--spec/unit/resource/yum_repository_spec.rb106
-rw-r--r--spec/unit/resource/zypper_repository_spec.rb70
-rw-r--r--spec/unit/resource_collection/resource_set_spec.rb47
-rw-r--r--spec/unit/resource_collection_spec.rb15
-rw-r--r--spec/unit/resource_definition_spec.rb22
-rw-r--r--spec/unit/resource_inspector_spec.rb60
-rw-r--r--spec/unit/resource_reporter_spec.rb52
-rw-r--r--spec/unit/resource_resolver_spec.rb12
-rw-r--r--spec/unit/resource_spec.rb224
-rw-r--r--spec/unit/rest/auth_credentials_spec.rb292
-rw-r--r--spec/unit/rest_spec.rb753
-rw-r--r--spec/unit/run_context/child_run_context_spec.rb8
-rw-r--r--spec/unit/run_context/cookbook_compiler_spec.rb42
-rw-r--r--spec/unit/run_context_spec.rb20
-rw-r--r--spec/unit/run_list/run_list_expansion_spec.rb4
-rw-r--r--spec/unit/run_list/versioned_recipe_list_spec.rb11
-rw-r--r--spec/unit/run_status_spec.rb2
-rw-r--r--spec/unit/runner_spec.rb41
-rw-r--r--spec/unit/search/query_spec.rb104
-rw-r--r--spec/unit/server_api_spec.rb124
-rw-r--r--spec/unit/server_api_versions_spec.rb66
-rw-r--r--spec/unit/shell/shell_session_spec.rb144
-rw-r--r--spec/unit/shell_out_spec.rb18
-rw-r--r--spec/unit/user_spec.rb2
-rw-r--r--spec/unit/user_v1_spec.rb16
-rw-r--r--spec/unit/util/dsc/configuration_generator_spec.rb24
-rw-r--r--spec/unit/util/dsc/lcm_output_parser_spec.rb120
-rw-r--r--spec/unit/util/dsc/local_configuration_manager_spec.rb100
-rw-r--r--spec/unit/util/dsc/resource_store.rb14
-rw-r--r--spec/unit/util/editor_spec.rb12
-rw-r--r--spec/unit/util/powershell/cmdlet_spec.rb20
-rw-r--r--spec/unit/util/selinux_spec.rb26
-rw-r--r--spec/unit/util/windows/logon_session_spec.rb285
-rw-r--r--spec/unit/version_string_spec.rb79
-rw-r--r--spec/unit/win32/error_spec.rb67
-rw-r--r--spec/unit/win32/link_spec.rb73
-rw-r--r--spec/unit/win32/security_spec.rb109
-rw-r--r--spec/unit/windows_service_spec.rb6
-rw-r--r--tasks/announce.rb58
-rwxr-xr-xtasks/bin/bundle-platform15
-rw-r--r--tasks/bin/bundle-platform.bat2
-rwxr-xr-xtasks/bin/create-override-gemfile110
-rwxr-xr-xtasks/bin/run_chef_tests16
-rwxr-xr-xtasks/bin/run_external_test62
-rw-r--r--tasks/bundle.rb97
-rw-r--r--tasks/bundle_util.rb94
-rw-r--r--tasks/cbgb.rb6
-rw-r--r--tasks/changelog.rb12
-rw-r--r--tasks/dependencies.rb161
-rw-r--r--tasks/gemfile_util.rb390
-rw-r--r--tasks/maintainers.rb16
-rw-r--r--tasks/rspec.rb2
-rw-r--r--tasks/templates/prerelease.md.erb26
-rw-r--r--tasks/templates/release.md.erb26
-rw-r--r--version_policy.rb111
1684 files changed, 63808 insertions, 62324 deletions
diff --git a/.bundle/config b/.bundle/config
deleted file mode 100644
index 7544be9dd7..0000000000
--- a/.bundle/config
+++ /dev/null
@@ -1,2 +0,0 @@
----
-BUNDLE_FROZEN: '1'
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000000..d698ae021c
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,2 @@
+# We don't want/need anything from the local system, so ignore everything.
+*
diff --git a/.expeditor/config.yml b/.expeditor/config.yml
new file mode 100644
index 0000000000..9bdabf7510
--- /dev/null
+++ b/.expeditor/config.yml
@@ -0,0 +1,78 @@
+# Documentation available at http://expeditor-docs.es.chef.io/
+
+# The name of the product keys for this product (from mixlib-install)
+product_key:
+ - chef
+ - angrychef
+
+# Slack channel in Chef Software slack to send notifications about build failures, etc
+slack:
+ notify_channel: chef-notify
+
+# Which Ruby Gems, built when the Omnibus package is built, to publish to rubygems.org
+# This publish is triggered by the `built_in:publish_rubygems` artifact_action.
+rubygems:
+ - chef
+ - chef-config
+
+docker_images:
+ - chef
+
+github:
+ # The file where the MAJOR.MINOR.PATCH version is kept. The version in this file
+ # is bumped automatically via the `built_in:bump_version` merge_action.
+ version_file: "VERSION"
+ # The file where our CHANGELOG is kept. This file is updated automatically with
+ # details from the Pull Request via the `built_in:update_changelog` merge_action.
+ changelog_file: "CHANGELOG.md"
+ # This deletes the GitHub PR branch after successfully merged into the release branch
+ delete_branch_on_merge: true
+ # The tag format to use (e.g. v1.0.0)
+ version_tag_format: "v{{version}}"
+ # The Github Team primarily responsible for handling incoming Pull Requests.
+ maintainer_group: chef/client-core
+ # Which Github branches to build Omnibus releases from, and what versions
+ # (as determined by the value in the VERSION file) those branches are responsible
+ # for building.
+ release_branch:
+ - master:
+ version_constraint: 14.*
+ - chef-13:
+ version_constraint: 13.*
+ - chef-12:
+ version_constraint: 12.*
+
+# These actions are taken, in order they are specified, anytime a Pull Request is merged.
+merge_actions:
+ - built_in:bump_version:
+ ignore_labels:
+ - "Version: Skip Bump"
+ - "Expeditor: Skip Version Bump"
+ - "Expeditor: Skip All"
+ - bash:.expeditor/update_version.sh:
+ only_if: built_in:bump_version
+ - built_in:update_changelog:
+ ignore_labels:
+ - "Meta: Exclude From Changelog"
+ - "Expeditor: Exclude From Changelog"
+ - "Expeditor: Skip All"
+ - built_in:trigger_omnibus_release_build:
+ ignore_labels:
+ - "Omnibus: Skip Build"
+ - "Expeditor: Skip Build"
+ - "Expeditor: Skip All"
+ only_if: built_in:bump_version
+
+# These actions are taken, in the order specified, when an Omnibus artifact is promoted
+# within Chef's internal artifact storage system.
+artifact_actions:
+ promoted_to_unstable:
+ - built_in:build_docker_image
+ promoted_to_current:
+ - built_in:tag_docker_image
+ promoted_to_stable:
+ - built_in:tag_docker_image
+ - built_in:publish_rubygems
+ - built_in:rollover_changelog
+ - bash:.expeditor/update_dockerfile.sh
+ - built_in:notify_chefio_slack_channels
diff --git a/.expeditor/update_dockerfile.sh b/.expeditor/update_dockerfile.sh
new file mode 100755
index 0000000000..9ae260fcb8
--- /dev/null
+++ b/.expeditor/update_dockerfile.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# This file updates the default VERSION build argument in the Dockerfile to the
+# VERSION passed in to the file via environment variables.
+
+set -evx
+
+sed -i -r "s/^ARG VERSION=.+/ARG VERSION=${VERSION}/" Dockerfile
diff --git a/.expeditor/update_version.sh b/.expeditor/update_version.sh
new file mode 100755
index 0000000000..b2d76bc336
--- /dev/null
+++ b/.expeditor/update_version.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+#
+# After a PR merge, Chef Expeditor will bump the PATCH version in the VERSION file.
+# It then executes this file to update any other files/components with that new version.
+#
+
+set -evx
+
+sed -i -r "s/^(\s*)VERSION = \".+\"/\1VERSION = \"$(cat VERSION)\"/" chef-config/lib/chef-config/version.rb
+sed -i -r "s/VersionString\.new\(\".+\"\)/VersionString.new(\"$(cat VERSION)\")/" lib/chef/version.rb
+
+# Update the version inside Gemfile.lock
+bundle update chef chef-config
+
+# Once Expeditor finshes executing this script, it will commit the changes and push
+# the commit as a new tag corresponding to the value in the VERSION file.
diff --git a/.gitattributes b/.gitattributes
index bb9914a8b6..311062a0b3 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,6 +1,4 @@
# git config merge.ignore.name 'ignore changes merge driver'
# git config merge.ignore.driver 'touch %A'
-distro/common/html/* merge=ignore
-distro/common/man/man1/* merge=ignore
-distro/common/man/man8/* merge=ignore
lib/chef/version.rb merge=ignore
+*.reg text eol=crlf \ No newline at end of file
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000000..fee8d42d91
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,4 @@
+# Order is important. The last matching pattern has the most precedence.
+
+* @chef/client-maintainers
+.expeditor/** @chef/jex-team
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000000..c6f0984993
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,37 @@
+<!---
+!!!!!! NOTE: CHEF CLIENT BUGS ONLY !!!!!!
+
+This issue tracker is for the code contained within this repo -- `chef-client`, base `knife` functionality (not
+plugins), `chef-apply`, `chef-solo`, `chef-client -z`, etc.
+
+* Requests for new or alternative functionality should be made to [feedback.chef.io](https://feedback.chef.io/forums/301644-chef-product-feedback/category/110832-chef-client)
+* [Chef Server issues](https://github.com/chef/chef-server/issues/new)
+* [ChefDK issues](https://github.com/chef/chef-dk/issues/new)
+* Cookbook Issues (see the https://github.com/chef-cookbooks repos or search [Supermarket](https://supermarket.chef.io) or GitHub/Google)
+
+-->
+
+## Description
+<!--- Briefly describe the issue -->
+
+## Chef Version
+<!--- Tell us which version of chef-client you are using (see below for Server+ChefDK bugs). -->
+
+## Platform Version
+<!--- Tell us which Operating System distribution and version chef-client is running on. -->
+
+## Replication Case
+<!--- Tell us what steps to take to replicate your problem. See [How to create a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve)
+for information on how to create a good replication case. -->
+
+## Client Output
+<!--- The relevant output of the chef-client run or a link to a gist of the entire run, if there is one.
+
+The debug output (chef-client -l debug) may be useful, but please link to a gist, or truncate it. -->
+
+```
+
+```
+
+## Stacktrace
+<!--- Please include the stacktrace.out output or link to a gist of it, if there is one. -->
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000000..aca7c10237
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,15 @@
+### Description
+
+[Please describe what this change achieves]
+
+### Issues Resolved
+
+[List any existing issues this PR resolves, or any Discourse or
+StackOverflow discussion that's relevant]
+
+### Check List
+
+- [ ] New functionality includes tests
+- [ ] All tests pass
+- [ ] RELEASE\_NOTES.md, has been updated if required (not required for bugfixes, required for API changes)
+- [ ] All commits have been signed-off for the Developer Certificate of Origin. See <https://github.com/chef/chef/blob/master/CONTRIBUTING.md#developer-certification-of-origin-dco>
diff --git a/.github/lock.yml b/.github/lock.yml
new file mode 100644
index 0000000000..66d5d4962c
--- /dev/null
+++ b/.github/lock.yml
@@ -0,0 +1 @@
+daysUntilLock: 60
diff --git a/.gitignore b/.gitignore
index 1e60843467..71f9cce719 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ tags
*/tags
*~
.chef
+results
# You should check in your Gemfile.lock in applications, and not in gems
external_tests/*.lock
@@ -15,7 +16,7 @@ external_tests/*.lock
# http://gembundler.com/man/bundle-exec.1.html
b/
binstubs/
-**/.bundle
+.bundle
# RVM and RBENV ruby version files
.rbenv-version
.rvmrc
@@ -24,6 +25,7 @@ binstubs/
# IDE files
.project
+.idea
# Documentation
_site/*
@@ -37,9 +39,34 @@ doc/
Vagrantfile
.vagrant/
-# Kitchen Tests Local Mode Data
+# Kitchen Tests lock files and chef data
kitchen-tests/nodes/*
+kitchen-tests/Gemfile.lock
+kitchen-tests/Berksfile.lock
# Temporary files present during spec runs
spec/data/test-dir
+spec/data/nodes
/config/
+
+# acceptance binstubs
+acceptance/bin/*
+
+vendor/
+acceptance/vendor
+kitchen-tests/vendor
+
+# Visual Studio Code files
+.vscode
+
+# ignore nodes generated during local testing
+nodes/
+
+# acceptance
+acceptance/.acceptance_logs
+acceptance/.acceptance_data
+acceptance/data-collector/Berksfile.lock
+
+# chef-config
+chef-config/.bundle
+chef-config/Gemfile.lock
diff --git a/.kitchen.yml b/.kitchen.yml
deleted file mode 100644
index ed49eb3e57..0000000000
--- a/.kitchen.yml
+++ /dev/null
@@ -1,82 +0,0 @@
-driver:
- name: vagrant
- forward_agent: yes
- customize:
- cpus: 4
- memory: 4096
- synced_folders:
- - ['.', '/home/vagrant/chef']
-
-provisioner:
- name: chef_zero
- require_chef_omnibus: 12.0.0.rc.1
-
-platforms:
- - name: centos-5.10
- run_list:
- - name: centos-6.5
- run_list:
- - name: debian-7.2.0
- run_list:
- - name: debian-7.4
- run_list:
- - name: debian-6.0.8
- run_list:
- - name: freebsd-9.2
- run_list:
- - name: freebsd-10.0
- run_list:
- - name: ubuntu-10.04
- run_list:
- - name: ubuntu-12.04
- run_list:
- - name: ubuntu-12.10
- run_list:
- - name: ubuntu-13.04
- run_list:
- - name: ubuntu-13.10
- run_list:
- - name: ubuntu-14.04
- run_list:
- # The following boxes are shared via VagrantCloud. Until kitchen-vagrant
- # is updated you'll need to add the box manually:
- #
- # vagrant box add chef/windows-8.1-professional
- #
- # Please note this may require a `vagrant login` if the box is private.
- #
- # The following boxes are VMware only also. You can enable VMware Fusion
- # as the default provider by copying `.kitchen.local.yml.vmware.example`
- # over to `.kitchen.local.yml`.
- #
- - name: macosx-10.8
- driver:
- box: chef/macosx-10.8 # private
- - name: macosx-10.9
- driver:
- box: chef/macosx-10.9 # private
- - name: macosx-10.10
- driver:
- box: chef/macosx-10.10 # private
- # - name: windows-7-professional
- # provisioner:
- # name: windows_chef_zero
- # require_chef_omnibus: 11.12.4
- # driver:
- # box: chef/windows-7-professional # private
- # - name: windows-8.1-professional
- # provisioner:
- # name: windows_chef_zero
- # require_chef_omnibus: 11.12.4
- # driver:
- # box: chef/windows-8.1-professional # private
- # - name: windows-2008r2-standard
- # provisioner:
- # name: windows_chef_zero
- # require_chef_omnibus: 11.12.4
- # driver:
- # box: chef/windows-server-2008r2-standard # private
-
-suites:
- - name: chef
- run_list:
diff --git a/.mailmap b/.mailmap
index a7b6b6b277..142d587a4e 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,5 +1,6 @@
# Daniel DeLeo
Daniel DeLeo <dan@chef.io> Daniel DeLeo <dan@opscode.com>
+Daniel DeLeo <dan@chef.io> danielsdeleo <dan@chef.io>
Daniel DeLeo <dan@chef.io> Dan DeLeo <danielsdeleo@mac.com>
Daniel DeLeo <dan@chef.io> Dan DeLeo <dan@kallistec.com>
Daniel DeLeo <dan@chef.io> danielsdeleo <dan@getchef.com>
@@ -62,11 +63,11 @@ Nuo Yan <nuo@opscode.com> Nuo Yan <nuoyan@nuo-yans-macbook-pro.(none)>
Nuo Yan <nuo@opscode.com> Nuo Yan <nuoyan@nuo-yans-macbook-pro.local>
# Thom May
-Thom May <thom@clearairturbulence.org>
-Thom May <thom@clearairturbulence.org> Thom May <thom.may@betfair.com>
-Thom May <thom@clearairturbulence.org> Thom May <thom@digital-science.com>
-Thom May <thom@clearairturbulence.org> Thom May <thom@virelais.nyc.joostas.com>
-Thom May <thom@clearairturbulence.org> Thom May <tmay@expedia.com>
+Thom May <thom@chef.io> Thom May <thom@clearairturbulence.org>
+Thom May <thom@chef.io> Thom May <thom.may@betfair.com>
+Thom May <thom@chef.io> Thom May <thom@digital-science.com>
+Thom May <thom@chef.io> Thom May <thom@virelais.nyc.joostas.com>
+Thom May <thom@chef.io> Thom May <tmay@expedia.com>
# Stephen Delano
Stephen Delano <stephen@chef.io> Stephen Delano <stephen@opscode.com>
@@ -114,4 +115,60 @@ Marc Paradise <marc@chef.io> marc@opscode.com <marc@opscode.com>
Tyler Ball <tyleraball@gmail.com> tyler-ball <tyleraball@gmail.com>
# Steven Danna
-Steven Danna <steve@chef.io> Steven Danna <steve@opscode.com> \ No newline at end of file
+Steven Danna <steve@chef.io> Steven Danna <steve@opscode.com>
+
+# Salim Alam
+Salim Alam <salam@chef.io> chefsalim <salam@chef.io>
+
+# Isa Farnik
+Isa Farnik <isa@chef.io> curiositycasualty <isa@getchef.com>
+Isa Farnik <isa@chef.io> curiositycasualty <isa@opscode.com>
+
+# Paul Mooring
+Paul Mooring <paul@chef.io> Paul Mooring <paul@opscode.com>
+
+# Jeremiah Snapp
+Jeremiah Snapp <jeremiah@chef.io> Jeremiah Snapp <jeremiah@getchef.com>
+Jeremiah Snapp <jeremiah@chef.io> Jeremiah Snapp <jeremiah.snapp@opscode.com>
+Jeremiah Snapp <jeremiah@chef.io> Jeremiah Snapp <jeremiah@opscode.com>
+
+# Mark Myzk
+Mark Mzyk <mmzyk@chef.io> Mark Mzyk <mmzyk@opscode.com>
+Mark Mzyk <mmzyk@chef.io> mmzyk <mmzyk@opscode.com>
+Mark Mzyk <mmzyk@chef.io> Mark Mzyk <markmzyk@Marks-MacBook-Pro.local>
+
+# Chris Doherty
+Chris Doherty <cdoherty@chef.io> Chris Doherty <cdoherty@getchef.com>
+Chris Doherty <cdoherty@chef.io> Chris Doherty <cdoherty@ooyala.com>
+Chris Doherty <cdoherty@chef.io> Chris Doherty <randomcamel@users.noreply.github.com>
+Chris Doherty <cdoherty@chef.io> unknown <cdoherty@chef.io>
+
+# Christopher Webber
+Christopher Webber <cwebber@chef.io> Christopher Webber <cwebber@getchef.com>
+
+# Tyler Cloke
+Tyler Cloke <tyler@chef.io> tylercloke <tyler@opscode.com>
+Tyler Cloke <tyler@chef.io> tylercloke <tylercloke@gmail.com>
+Tyler Cloke <tyler@chef.io> Tyler Cloke <tylercloke@gmail.com>
+
+# Julian Dunn
+Julian C. Dunn <jdunn@chef.io> Julian C. Dunn <jdunn@getchef.com>
+Julian C. Dunn <jdunn@chef.io> Julian C. Dunn <jdunn@opscode.com>
+Julian C. Dunn <jdunn@chef.io> Julian C. Dunn <jdunn@aquezada.com>
+
+# Tom Duffield
+Tom Duffield <tom@chef.io> Tom Duffield <tom@getchef.com>
+Tom Duffield <tom@chef.io> Tom Duffield <tom@opscode.com>
+
+# Scott Hain
+Scott Hain <shain@chef.io> Scott Hain <shain@getchef.com>
+
+# Peter Burkholder
+Peter Burkholder <pburkholder@chef.io> Peter Burkholder <peterb@getchef.com>
+
+# JJ Ashgar
+JJ Asghar <jj@chef.io> JJ Asghar <jj@getchef.com>
+
+# Tim Smith
+Tim Smith <tsmith@chef.io> Tim Smith <tim@cozy.co>
+Tim Smith <tsmith@chef.io> Tim Smith <tsmith84@gmail.co>
diff --git a/.rubocop.yml b/.rubocop.yml
index 3c2a48e548..9730861155 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -4,3 +4,28 @@ AllCops:
- "vendor/**/*"
- "pkg/**/*"
- "chef-config/pkg/**/*"
+ - "habitat/**/*"
+Security/Eval:
+ Enabled: false
+Lint/UselessAssignment:
+ Enabled: false
+Lint/DeprecatedClassMethods:
+ Enabled: false
+Lint/ParenthesesAsGroupedExpression:
+ Enabled: false
+Lint/AmbiguousRegexpLiteral:
+ Enabled: false
+Lint/AssignmentInCondition:
+ Enabled: false
+Lint/AmbiguousBlockAssociation:
+ Enabled: false
+Lint/UnneededSplatExpansion:
+ Enabled: false
+Lint/ShadowingOuterLocalVariable:
+ Enabled: false
+Lint/EmptyWhen:
+ Enabled: false
+Lint/IneffectiveAccessModifier:
+ Enabled: false
+Lint/ShadowedException:
+ Enabled: false
diff --git a/.travis.yml b/.travis.yml
index 0d977ece31..36fd343809 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,21 +1,32 @@
language: ruby
-sudo: false
cache: bundler
+dist: trusty
# Early warning system to catch if Rubygems breaks something
before_install:
- gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
- - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
+ - gem --version
+ # travis may preinstall a bundler gem which is later than the one which we pin, which may totally hose us, so we preemtively
+ # uninstall anything they may have installed here. if they haven't installed anything then we have to ignore the failure
+ # to uninstall the default bundler that ships embedded in ruby itself.
+ - rvm @global do gem uninstall bundler -a -x || true
+ - gem install bundler -v $(grep :bundler omnibus_overrides.rb | cut -d'"' -f2)
+ - bundle --version
- rm -f .bundle/config
-bundler_args: --without changelog development docgen guard maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
+before_script:
+ # force all .rspec tests into progress display to reduce line count
+ - echo --color > .rspec
+ - echo -fp >> .rspec
+ # necessary for sudo: true tests, ingore failures on tests invoked with sudo: false
+ - sudo sed -i -e 's/^Defaults\tsecure_path.*$//' /etc/sudoers
# do not run expensive spec tests on PRs, only on branches
branches:
only:
- master
- - 10-stable
- - 11-stable
+ - chef-13
+ - chef-12
env:
global:
@@ -23,140 +34,316 @@ env:
matrix:
include:
- - rvm: 2.1
+ - env:
+ INTEGRATION_SPECS_24: 1
+ rvm: 2.4.4
sudo: true
- script: tasks/bin/run_chef_tests
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- - rvm: 2.2
+ script: sudo -E $(which bundle) exec rake spec:integration;
+ bundler_args: --without ci docgen guard integration maintenance omnibus_package --frozen
+ - env:
+ INTEGRATION_SPECS_25: 1
+ rvm: 2.5.1
sudo: true
- script: tasks/bin/run_chef_tests
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- - rvm: 2.3.0
+ script: sudo -E $(which bundle) exec rake spec:integration;
+ bundler_args: --without ci docgen guard integration maintenance omnibus_package --frozen
+ - env:
+ FUNCTIONAL_SPECS_24: 1
+ rvm: 2.4.4
+ sudo: true
+ script: sudo -E $(which bundle) exec rake spec:functional;
+ bundler_args: --without ci docgen guard integration maintenance omnibus_package --frozen
+ - env:
+ FUNCTIONAL_SPECS_25: 1
+ rvm: 2.5.1
sudo: true
- script: tasks/bin/run_chef_tests
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
+ script: sudo -E $(which bundle) exec rake spec:functional;
+ bundler_args: --without ci docgen guard integration maintenance omnibus_package --frozen
+ - env:
+ UNIT_SPECS_24: 1
+ rvm: 2.4.4
+ sudo: true
+ script:
+ - sudo -E $(which bundle) exec rake spec:unit;
+ - sudo -E $(which bundle) exec rake component_specs
+ bundler_args: --without ci docgen guard integration maintenance omnibus_package --frozen
+ - env:
+ UNIT_SPECS_25: 1
+ rvm: 2.5.1
+ sudo: true
+ script:
+ - sudo -E $(which bundle) exec rake spec:unit;
+ - sudo -E $(which bundle) exec rake component_specs
+ bundler_args: --without ci docgen guard integration maintenance omnibus_package --frozen
- env:
CHEFSTYLE: 1
- rvm: 2.1
+ rvm: 2.5.1
script: bundle exec rake style
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- - rvm: rbx
- sudo: true
- script: tasks/bin/run_chef_tests
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
+ # also remove integration / external tests
+ bundler_args: --without ci docgen guard integration maintenance omnibus_package --frozen
#
# External tests
#
- env:
- TEST_GEM: chef-provisioning
- script: tasks/bin/run_external_test $TEST_GEM rake spec
- rvm: 2.2
- - env:
- TEST_GEM: chef-provisioning-aws
- script: tasks/bin/run_external_test $TEST_GEM rake spec
- rvm: 2.2
-# requires vagrant
-# - env: TEST_GEM=chef-rewind
-# script: tasks/bin/run_external_test $TEST_GEM "rake spec"
-# bundler_args: --without development
-# rvm: 2.2
+ TEST_GEM: sethvargo/chef-sugar
+ script: bundle exec tasks/bin/run_external_test $TEST_GEM master rake
+ rvm: 2.5.1
- env:
- TEST_GEM: chef-sugar
- script: tasks/bin/run_external_test $TEST_GEM rake
- rvm: 2.2
- - env:
- - TEST_GEM: chef-zero
- script: tasks/bin/run_external_test $TEST_GEM rake spec cheffs
- rvm: 2.2
- - env:
- TEST_GEM: cheffish
- script: tasks/bin/run_external_test $TEST_GEM rake spec
- rvm: 2.2
- - env:
- TEST_GEM: chefspec
- # The chefspec tests + bundler cache + "gem update --system" interact badly :/
- # (Cucumber doesn't start.)
- before_install:
- - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
- - bundle config --local without server:docgen:maintenance:omnibus_package:development:ruby_prof:pry
- script: tasks/bin/run_external_test $TEST_GEM rake
- rvm: 2.2.0
+ - PEDANT_OPTS=--skip-oc_id
+ - TEST_GEM=chef/chef-zero
+ - CHEF_FS=true
+ script: bundle exec tasks/bin/run_external_test $TEST_GEM master rake pedant
+ rvm: 2.5.1
- env:
- TEST_GEM: foodcritic
- script: tasks/bin/run_external_test $TEST_GEM rake test
- rvm: 2.2
+ TEST_GEM: chef/cheffish
+ script: bundle exec tasks/bin/run_external_test $TEST_GEM master rake spec
+ rvm: 2.5.1
- env:
- TEST_GEM: halite
- script: tasks/bin/run_external_test $TEST_GEM rake spec
- rvm: 2.2
+ TEST_GEM: chefspec/chefspec
+ script: bundle exec tasks/bin/run_external_test $TEST_GEM master rake
+ rvm: 2.5.1
- env:
- TEST_GEM: knife-windows
- script: tasks/bin/run_external_test $TEST_GEM rake unit_spec
- rvm: 2.2
+ TEST_GEM: poise/halite
+ script: bundle exec tasks/bin/run_external_test $TEST_GEM master rake spec
+ rvm: 2.5.1
- env:
- TEST_GEM: poise
- script: tasks/bin/run_external_test $TEST_GEM rake spec
- rvm: 2.2
- #
+ TEST_GEM: chef/knife-windows
+ script: bundle exec tasks/bin/run_external_test $TEST_GEM master rake unit_spec
+ rvm: 2.5.1
+ # disable this pending a Chef 14 compat version of poise
+ # - env:
+ # TEST_GEM: poise/poise
+ # script: bundle exec tasks/bin/run_external_test $TEST_GEM master rake spec
+ # rvm: 2.5.1
### START TEST KITCHEN ONLY ###
#
- - rvm: 2.2
+ - rvm: 2.4.4
+ services: docker
+ sudo: required
gemfile: kitchen-tests/Gemfile
before_install:
- gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
- - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
- - echo -n $DO_KEY_CHUNK_{0..30} >> ~/.ssh/id_aws.base64
- - cat ~/.ssh/id_aws.base64 | tr -d ' ' | base64 --decode > ~/.ssh/id_aws.pem
+ - gem install bundler -v $(grep :bundler omnibus_overrides.rb | cut -d'"' -f2)
before_script:
+ - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
- cd kitchen-tests
script:
- # FIXME: we should fix centos-6 against AWS and then enable it here
- - if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then bundle exec kitchen test ubuntu; fi
+ - bundle exec kitchen test base-amazonlinux-2
+ after_failure:
+ - cat .kitchen/logs/kitchen.log
+ env:
+ - AMAZON=2
+ - KITCHEN_YAML=.kitchen.travis.yml
+ - rvm: 2.4.4
+ services: docker
+ sudo: required
+ gemfile: kitchen-tests/Gemfile
+ before_install:
+ - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
+ - gem install bundler -v $(grep :bundler omnibus_overrides.rb | cut -d'"' -f2)
+ before_script:
+ - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
+ - cd kitchen-tests
+ script:
+ - bundle exec kitchen test base-amazonlinux
+ after_failure:
+ - cat .kitchen/logs/kitchen.log
+ env:
+ - AMAZON=201X
+ - KITCHEN_YAML=.kitchen.travis.yml
+ - rvm: 2.4.4
+ services: docker
+ sudo: required
+ gemfile: kitchen-tests/Gemfile
+ before_install:
+ - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
+ - gem install bundler -v $(grep :bundler omnibus_overrides.rb | cut -d'"' -f2)
+ before_script:
+ - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
+ - cd kitchen-tests
+ script:
+ - bundle exec kitchen test base-ubuntu-1404
+ after_failure:
+ - cat .kitchen/logs/kitchen.log
+ env:
+ - UBUNTU=14.04
+ - KITCHEN_YAML=.kitchen.travis.yml
+ - rvm: 2.4.4
+ services: docker
+ sudo: required
+ gemfile: kitchen-tests/Gemfile
+ before_install:
+ - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
+ - gem install bundler -v $(grep :bundler omnibus_overrides.rb | cut -d'"' -f2)
+ before_script:
+ - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
+ - cd kitchen-tests
+ script:
+ - bundle exec kitchen test base-ubuntu-1604
+ after_failure:
+ - cat .kitchen/logs/kitchen.log
+ env:
+ - UBUNTU=16.04
+ - KITCHEN_YAML=.kitchen.travis.yml
+ - rvm: 2.4.4
+ services: docker
+ sudo: required
+ gemfile: kitchen-tests/Gemfile
+ before_install:
+ - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
+ - gem install bundler -v $(grep :bundler omnibus_overrides.rb | cut -d'"' -f2)
+ before_script:
+ - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
+ - cd kitchen-tests
+ script:
+ - bundle exec kitchen test base-ubuntu-1804
+ after_failure:
+ - cat .kitchen/logs/kitchen.log
+ env:
+ - UBUNTU=18.04
+ - KITCHEN_YAML=.kitchen.travis.yml
+ - rvm: 2.4.4
+ services: docker
+ sudo: required
+ gemfile: kitchen-tests/Gemfile
+ before_install:
+ - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
+ - gem install bundler -v $(grep :bundler omnibus_overrides.rb | cut -d'"' -f2)
+ before_script:
+ - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
+ - cd kitchen-tests
+ script:
+ - bundle exec kitchen test base-debian-8
after_failure:
- cat .kitchen/logs/kitchen.log
- after_script:
- - if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then bundle exec kitchen destroy ubuntu; fi
env:
+ - DEBIAN=8
- KITCHEN_YAML=.kitchen.travis.yml
- - EC2_SSH_KEY_PATH=~/.ssh/id_aws.pem
- - secure: VAauyVnAMWhqvnhJOJ/tCDn3XAdWqzbWiDVQPNBkqtm2SBIvhmZl2hlrusvw6YLU31Prdf8fSFhOSysVQQs/rJYrmD/1BfV79p6M7cGXYZ0nGWwldF81N296lyFoZLyrqtmG4G0cx3Pw2ojADFgFe+B5eTGlqJFD+z371g4RF/Y=
- - secure: A+qtUF2LPJGkUAdvt04AwZMt69rzaeTyR0/1XEOAuntBKKXSCzddUzr5ePDc9QQ/57AWywKxhVLpnxk3QzKN7r7zerDxyIJBgklNDpNAKkeQjP3T6FpaKEIN9ROcpPtsM6FJ5Agb+bEQoRJF7s+ampO3wLV3XpTiWNuWkcAhv9A=
- - secure: J8JIg15trrPgc8X/1DsaUWDQCdDWTvN/AorXzZ/ReudHS6G/KpoynZ5lTmKjlgFiFNE/TGMDv486pStGtIcarTKTuIEmNADdEWlAVH7bxclpayMjtppVuapRCkZWccs5gz5CJyhX7yhQCFTYoqVox9Y4qHGCluF3oqCcPRtCOOw=
- - secure: NJYn0blTMwIoFxZlsoMWK8hPO/fi45rgWOqEImnjvSRk++5WL+GgjLBgLvEi7wCMkBijhIMWtnva60ojd4MrxeS7evrmGRjJKXnPuSKEsrGbArZPskBjCAcg+3PlnQQUkFf6hvbGD3HZlJtcbs4hrx8tbDT2Ie7bmQfqpsawKY4=
- - secure: FipoX1VzZkzPUP6Gxd05DEva7cX6xKK2Wdq+Y18nNkyW2afPLXCNl5kCsNrgvbqAzbjKaP2M8+b0zwKjrFzNebqmmx1RRfZUJWUkNRF1EgE+tHytmMZW6tNcQlTlvA0KqXi4Dt6SIQ0l/DhwwNKZ80jmpiyYi/ErxIXzbVgVtYA=
- - secure: T2MbE9twIkdaor796/lDioCgb2+FP3G8lXq+lIqnjaL22WMP8yKtkjNo8ggSlvQZE7MAQHqi5LISw5MU2MI6ImTU50/pgdWreM5Cx37WWYqntcbJ0Sz7v396KGJzeqbDql1fGolHDlykfi+OJzzbIGC8cjz7iAD2RUZU95wEC5s=
- - secure: hWEQInvuanQavFCE3m6/q9BjNEFZQmLc94EWnBKTMiwUAdYgQQMLohN7K1Gc8irxYKp86F+P+XWE4lfDZNK3sqmxyk51TtT2EfmKWs+jSLq4+NBYQwXCpRELC5Irpm0GRCYthhsQSuarpVWss/0s0o7iJQaHxrSPcQiwDouIpwU=
- - secure: OllJUaR/WUu+H0FIjU7vQxU10JT4d+/FZuTqnX6ZTcXN3dXCirnabYp/j+r5OBY3QeOojOyzGfHUWYEUGH/PTxcxYjrohtFTWht9N9x+SxfX2fLqieH/kRKyDmIidsY8qKChf/LD9f+SwpXRXND/PctKhNR4C5BH57fGUEqE9FU=
- - secure: KgKnGtM4e+cVYfLn78eTWJ1q4ORv128abB72QBc/xiSh0rvxSIojVKZCXmRetQPXIl7NoIzU2IyjR1ABEZ+vA83PayTEsOr2KDRDgolSIgZSSiDFt4U2phQsxl4fX7wFv/jWlbxM2fysKBSIRAF57CwBjGhLjmpUO+5PdoR7N2s=
- - secure: IgOx4STauKnJWENQGcn2iBp32XcNd2anNR0Fua0ugjudu1+CV+IxcIhI8ohOfZEXyVK4MGTF8uXWrYtoiwyExG4mTXqpRWJCgIkncqiWlfT+8BoAGWxCQhUYub3MaNZANPgebKPJhTPQ8OwNz09gPMNkewRfAqNF05eb8FU2kGA=
- - secure: CPXP6g3c1FH4Zm4U19XaPvq9nnyNsQCXRkxiPcGqsJZsGG2QMgzPQyjiAuPqnWxxZHit/6NgzUszJC+skSgcTzDTeD6rOA0Wcxtbr/Un4RRxRnTcRc6mSEZqSu9RbAZMYur/mSQ9HDHnjFe1ok85He4s9jM1iFdgjtg1ToelEmA=
- - secure: fp9pzNe09PIyZ/8NjbMPGW1zdG3Q/KhJ+stUKqA+FRopAMX/Hh24gFIVJhFOmfr4Vhn0J8sF7RsFaR1mdzcPewliOzKxknWhGEGMcG9LFCZcv+vVK0Fxs4nUzCRtaXUt08FpsRofG0iBvfapZ7YBhK7lslqGVI+fxCd3ZXmayG8=
- - secure: NT/6qcecxmuKYOnw1Atc6hsyJlfB6XI2Z1lg7dE0PhlEVW2EpkckHjAc+5hgg8Zt7TifYm2qDQWJwblwPP0mMj3ra4ZIMaZAiG2kzQoZ5kthqwjAV9fatZvrDXi+jd9wBF2hPyiCokAQiTLmKTYjzY2FBqPO3VDLWdf9qZqRmxw=
- - secure: MjIWyfquKANh/YeoyHGksdvAUQ4wc2tBCQmq1QcRhKwb7Sy6wcDk1nujDmnGE7HFpZUS6CyoZF7AMzJGFkCzrChpsLQYUP4hc7VjkXOLzi90vJUl+ANq7KPOmxC0MjKpgeHqCysRbTYbUsnJZfbbZbIZjCAjY0YCY2pGniXpvQc=
- - secure: AsZLOiFrHkGsY6jp2ShI5kYz78V6PEUyizgtPCWTgevTRGWpdCq9csIEoqUBY+vMUxmQPC6IY4fwHkrRCbv/rJyhwRl/Rnwa3aw8bdD+YD17IxnpXKGXXUyXdTZmF7HzAkVgStehL+qWZ3x9TBdExIV37KVgrVw/b+S0QqBUlQo=
- - secure: jwEnSquLreMM1M6N3gGpgTGHd8VtjBUTLDdkrokhiH1jHLpz7Hmr6xeajhZws+2sLtLiB7hYi6WsZBE5VcymBoObh9MeodO9Ve5/1z06lFmx1DyYV6euyo9WUkU2WpoVfu8k7O+eAvyrXXZVqm8Oz1p7Isb6Bh5+fJH2H8rhed4=
- - secure: HOAK620U6mlS11XK+JtXTBk26Tt2vWO4shA/6Zit/y0/kAz7JnbXtup7FSysXliBoSv4YsxA6IbgZ8V0tuIXj+q7EcqtHMmQhqzMJG5jRKVhtGiFIhDmwmxJvdfIvwtZOO3mMk0OspLz24sWp8wCciYZMPj0hZJR04R9aWEO3cE=
- - secure: DfTRP74UWWxA460XfLoJFgRLwoKbHWNIueL6qr982AnuAxeZFofsxCqxSxcSJmu67TxuPc+b201+BmanHKYmSauGS31t0F4QXk7lCTaT/x38mAPsWvMFkY8HEl56JhmzEp2hAKDB/t0/HItwmvxT1vd5WvNRSSojEVzChftV/zE=
- - secure: JoCWsJzTgj+epgzmgbvV7/bdAPHwUGXZA7Jdvv9vIJ5lCo6h9WwCw6/KCvH+bHtrT/RfZmUmxouCxJCLKwts1ZrMmedTIXpMrQJo/YgWRp7ziFnLyZ8jG8bD7rep3ngq1x/cRGc3cZvYN6IK3GS6C27OviYLFsTw74AUnWTaFSo=
- - secure: iXfl0WnAnfKurZUrMeV1yOoFiiZ+MKx/Zj6ZVP2++A9EOxxIxb/fS/gIOzSjBQwzrR+fJVHIlX0g42CiBKDQWUvIl5I8kZCVIP6AHa1jyzlmZE9lqSlojz3k5RPS7pW6nIX+z1NHMvtb3e5xeLv8y4J5kwZErqZ+YDJmBRtPxPU=
- - secure: RhAW5kABDPB3GWKD+NCg05Kcd92F/+kg+0icXXN166DWQYUut3MLrSY80xNzkz5nXTI9EFU4fUqlKLDiF/kelr0Zp/zpCQAB54o4cu5FkZz0Bgs9k7yUdCRyz6Vt2ChV5cYI4JTn9bMaeXEaGlOjP1iE51rYT6KO6kKlwsEnjUc=
- - secure: jy/3fC+UtrDcE/X6/IxkyT2SrYMKkiEMP1ht4d5mxvNA0Xxn43E16c6FNP0JWPpWRGRIP38vnQRB4yOPU9BXvRmmswVL9Ge4e/6flJvKwD5Rlqb2dfaGaHRYV9v8Nkdzl2FvZ9eBH5KHxgG19gCG6L3RXP/+zYwrr4AQdm0fpfw=
- - secure: RYEwBWYVXRTEdUWhQxdWXo6tldlVx8pha9zB0rgafcUQxaatAefnRc4X4HXTQnqr2n9TZ2TQGpM8vte/wr6Pjc85VZbimWGzgrvn0kg4MwPR8ZYiEM5qQ/pUpj4+93rpA91PhCGvZoZTqOrXHm4kMPuKro5I6qA4BFUXuANeC/s=
- - secure: gHSicpqkqcZT04QurSgszrAiI6HOCw1DBlfIIi9KAJj7mG5GijD/4AQ6HCmcRMbCDJ0nUuvm/kckASnRtF5+3xvIJnuoyyEfCZWxt1lhK2UbS87VU+pVdws/VzwpisXuKsh3H0uT8DDVkWPH/ZWDgfVa74eYDEHiQFjo+2xx5ZA=
- - secure: Q42bco3JXEpyVbL2akiOsaCHnAagAFIb3TF6H5qJfaLLqmGs/XrrgxliNaVMfWVSwPT2wpQvg9UGF9x37No9bZBv33DgYcWExmXb/lvGPpkctX37+FTMzECQHxOuUbYPQA7ZEuJ4AA7bwgpMISUeSyz5XXz44KcXIrZK2GWH+X4=
- - secure: hugd8NVukJc3redDvlOt6zhaqa63XLNMp/eIIlNllW8VfQ6CJ1P7KJPwgxH24sDyrw7rLzOkBl6R4kaVWsCLCFp+NE6yFFHl9wDkSdLC1OX1DMrJnDsogwUqqe+jX8dxePSy26MSTfG8eo9/NxN9uXr+tKaHoi6G7BRXDHtQ8dQ=
- - secure: TRkW9pIuIYHXJmPlDYoddxIp2M2W2f7qBGNJKEMB5xrOezES7w9XTg2eQXrD8jBO+fUUmMnAaDAXZuU58nMysPXx3vhtZKncg8w5CyuXJk2P8nkdPh0u5nmRhEpWrLKtLwJrX48xmJhNQvQqDAyL5c9WUzlWJ4WJFgoP5IDWmLc=
- - secure: QHuMdtFCvttiIOx6iS+lH4bKXZMwsgVQ6FPsUW5zJ7uw6mAEWKEil9xNk4aYV9FywinwUs4fnFlnIW/Gj1gLkUjm4DtxdmRZIlRXIbgsNch6H916TCPg4Q2oPsW2nVdXPjW/2jhkfLUiSnuhL+ylami1NF8Up7vokXknh/jFNZU=
- - secure: GTfrUVmMQSxho3Ia4Y1ONqKvVMD34GHF2/TJb8UdQV7iH+nVxVXpy3nWaCXa9ri7lRzMefkoVLy0gKK13YoVd7w3d2S3/IfNakC85XfN6VuOzK/FDkA0WoPrgKjcQ64I+3dQ6cgrMWWTieKwRZy+Ve24iRbnN055Hk+VRMu6OGw=
- - secure: SOMYGVfHLkHsH6koxpw68YQ4ydEo6YXPhHbrYGQbehUbFa6+OZzBcAJRJbKjyhD2AZRvNr2jB8XnjYKvVyDGQRpkWhGYZ7CpHqINpDsqKBsbiMe3/+KmKQqS+UKxNGefquoOvyQ1N8Xy77dkWYokRtGMEuR12RkZLonxiDW8Qyg=
- - secure: bSsDg+dJnPFdFiC/tbb61HdLh/Q0z2RVVAReT1wvV1BN4fN4NydvkUGbQmyFNyyunLulEs+X0oFma9L0497nUlTnan8UOg9sIleTSybPX6E9xSKKCItH1GgDw8bM9Igez5OOrrePBD3altVrH+FmGx0dlTQgM/KZMN50BJ79cXw=
- ### END TEST KITCHEN ONLY ###
- - rvm: 2.2
+ - rvm: 2.4.4
+ services: docker
sudo: required
- dist: trusty
+ gemfile: kitchen-tests/Gemfile
+ before_install:
+ - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
+ - gem install bundler -v $(grep :bundler omnibus_overrides.rb | cut -d'"' -f2)
+ before_script:
+ - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
+ - cd kitchen-tests
+ script:
+ - bundle exec kitchen test base-debian-9
+ after_failure:
+ - cat .kitchen/logs/kitchen.log
+ env:
+ - DEBIAN=9
+ - KITCHEN_YAML=.kitchen.travis.yml
+ - rvm: 2.4.4
+ services: docker
+ sudo: required
+ gemfile: kitchen-tests/Gemfile
+ before_install:
+ - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
+ - gem install bundler -v $(grep :bundler omnibus_overrides.rb | cut -d'"' -f2)
+ before_script:
+ - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
+ - cd kitchen-tests
+ script:
+ - bundle exec kitchen test base-centos-6
+ after_failure:
+ - cat .kitchen/logs/kitchen.log
+ env:
+ - CENTOS=6
+ - KITCHEN_YAML=.kitchen.travis.yml
+ - rvm: 2.4.4
+ services: docker
+ sudo: required
+ gemfile: kitchen-tests/Gemfile
before_install:
- gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
- - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
+ - gem install bundler -v $(grep :bundler omnibus_overrides.rb | cut -d'"' -f2)
+ before_script:
+ - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
+ - cd kitchen-tests
+ script:
+ - bundle exec kitchen test base-centos-7
+ after_failure:
+ - cat .kitchen/logs/kitchen.log
+ env:
+ - CENTOS=7
+ - KITCHEN_YAML=.kitchen.travis.yml
+ - rvm: 2.4.4
+ services: docker
+ sudo: required
+ gemfile: kitchen-tests/Gemfile
+ before_install:
+ - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
+ - gem install bundler -v $(grep :bundler omnibus_overrides.rb | cut -d'"' -f2)
+ before_script:
+ - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
+ - cd kitchen-tests
+ script:
+ - bundle exec kitchen test base-fedora-latest
+ after_failure:
+ - cat .kitchen/logs/kitchen.log
+ env:
+ - FEDORA=latest
+ - KITCHEN_YAML=.kitchen.travis.yml
+ - rvm: 2.4.4
+ services: docker
+ sudo: required
+ gemfile: kitchen-tests/Gemfile
+ before_install:
+ - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
+ - gem install bundler -v $(grep :bundler omnibus_overrides.rb | cut -d'"' -f2)
+ before_script:
+ - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
+ - cd kitchen-tests
+ script:
+ - bundle exec kitchen test base-opensuse-leap
+ after_failure:
+ - cat .kitchen/logs/kitchen.log
+ env:
+ - OPENSUSELEAP=42
+ - KITCHEN_YAML=.kitchen.travis.yml
+# - rvm: 2.4.4
+# services: docker
+# sudo: required
+# gemfile: kitchen-tests/Gemfile
+# before_install:
+# - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
+# - gem install bundler -v $(grep :bundler omnibus_overrides.rb | cut -d'"' -f2)
+# before_script:
+# - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
+# - cd kitchen-tests
+# script:
+# - bundle exec kitchen test awesome-customers-ubuntu-ubuntu-1404
+# after_failure:
+# - cat .kitchen/logs/kitchen.log
+# env:
+# - AWESOME_CUSTOMERS_UBUNTU=1
+# - KITCHEN_YAML=.kitchen.travis.yml
+# - rvm: 2.4.4
+# services: docker
+# sudo: required
+# gemfile: kitchen-tests/Gemfile
+# before_install:
+# - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
+# - gem install bundler -v $(grep :bundler omnibus_overrides.rb | cut -d'"' -f2)
+# before_script:
+# - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
+# - cd kitchen-tests
+# script:
+# - bundle exec kitchen test awesome-customers-rhel-centos-7
+# after_failure:
+# - cat .kitchen/logs/kitchen.log
+# env:
+# - AWESOME_CUSTOMERS_RHEL=1
+# - KITCHEN_YAML=.kitchen.travis.yml
+# ### END TEST KITCHEN ONLY ###
+ - rvm: 2.5.1
+ sudo: required
+ before_install:
+ - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
+ - rvm @global do gem uninstall bundler -a -x || true
+ - gem install bundler -v $(grep :bundler omnibus_overrides.rb | cut -d'"' -f2)
- sudo apt-get update
- sudo apt-get -y install squid3 git curl
env:
@@ -171,25 +358,8 @@ matrix:
- sudo cat /var/log/squid3/cache.log
- sudo cat /var/log/squid3/access.log
- allow_failures:
- - rvm: 2.3.0
- - rvm: rbx
-
notifications:
on_change: true
on_failure: true
on_success: change
on_pull_requests: false
- irc:
- channels:
- - chat.freenode.net#chef-hacking
- webhooks:
- urls:
- # Gitter IM
- - secure: HmMKr/ysKVyKUJ24PRCHcA8QCmlFoukrYumY0GRLzvaFWO8PknHO1t/0RbrKRb2ed/hgkFd+RKNCYvSvcE8Ahr2vlMrBeGHGfVeOGkWtbhLgNqo1b50Ll9CqvTM8X2ZIq6hIWraanwoYRQu/8uGL29yH4lBi7DhpTkFwBMLulhQ=
- hipchat:
- rooms:
- # Build Statuses
- - secure: G8MNo94L8bmWWwkH2/ViB2QaZnZHZscYM/mEjDbOGd15sqrruwckeARyBoUcRI7P1C6AFmS4IKCNVXa6KzX4Pbh51gQWM92zRpRTZpplwtXz53/1l8ajLFLLMLvEMTlBFAANUKEUFAQPY4dMa14V3Qc5oijfIncN61k4nZNTKpY=
- # Open Source
- - secure: hmcex4PpG5dn8WvjndONO4xCUKOC5kPU/bUEGRrfVbe2YKJE7t0XXbNDC96W/xBgzgnJzvf1Er0zJKDrNf4qEDEWFoozdN00WLcqREgaLLS3Seto2FjR/BpBk5q+sCV0rwwEMms2P4Qk+VSnDCnm9EaeM55hOabqNuOrRzoZLBQ=
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3fe8857c13..aa0d4099c4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,957 @@
-# Change Log
+<!-- usage documentation: http://expeditor-docs.es.chef.io/configuration/changelog/ -->
+<!-- latest_release 14.1.8 -->
+## [v14.1.8](https://github.com/chef/chef/tree/v14.1.8) (2018-05-14)
+
+#### Merged Pull Requests
+- Update Habitat plan to correctly build [#6111](https://github.com/chef/chef/pull/6111) ([elliott-davis](https://github.com/elliott-davis))
+<!-- latest_release -->
+
+<!-- release_rollup since=14.1.1 -->
+### Changes since 14.1.1 release
+
+#### Merged Pull Requests
+- Update Habitat plan to correctly build [#6111](https://github.com/chef/chef/pull/6111) ([elliott-davis](https://github.com/elliott-davis)) <!-- 14.1.8 -->
+- apt_repository: Use the repo_name name property [#7244](https://github.com/chef/chef/pull/7244) ([tas50](https://github.com/tas50)) <!-- 14.1.7 -->
+- remote_directory: restore overwrite default [#7254](https://github.com/chef/chef/pull/7254) ([rmoriz](https://github.com/rmoriz)) <!-- 14.1.6 -->
+- repo_name property should be part of new_resource object [#7252](https://github.com/chef/chef/pull/7252) ([tj-anderson](https://github.com/tj-anderson)) <!-- 14.1.5 -->
+- fix git provider: -prune-tags is not available with old git versions, fixes #7233 [#7247](https://github.com/chef/chef/pull/7247) ([rmoriz](https://github.com/rmoriz)) <!-- 14.1.4 -->
+- Switch Node#role? to use the attributes expansion instead of the run list [#7234](https://github.com/chef/chef/pull/7234) ([coderanger](https://github.com/coderanger)) <!-- 14.1.3 -->
+- Remove redundant &quot;?&quot; in knife configure [#7235](https://github.com/chef/chef/pull/7235) ([alexymik](https://github.com/alexymik)) <!-- 14.1.2 -->
+<!-- release_rollup -->
+
+<!-- latest_stable_release -->
+## [v14.1.1](https://github.com/chef/chef/tree/v14.1.1) (2018-05-08)
+
+#### Merged Pull Requests
+- fix for Red Hat Satellite yum_package bug [#7147](https://github.com/chef/chef/pull/7147) ([lamont-granquist](https://github.com/lamont-granquist))
+- add name_property to resource inspector [#7164](https://github.com/chef/chef/pull/7164) ([thommay](https://github.com/thommay))
+- Windows MSI: files are now re-unzipped during repair mode [#7111](https://github.com/chef/chef/pull/7111) ([stuartpreston](https://github.com/stuartpreston))
+- Some options, i.e. metric, require specifying dev [#7162](https://github.com/chef/chef/pull/7162) ([tomdoherty](https://github.com/tomdoherty))
+- Avoid conflict with build_powershell_command from powershell_out mixin [#7173](https://github.com/chef/chef/pull/7173) ([stuartpreston](https://github.com/stuartpreston))
+- Ubuntu 1804 - passing tests and fixed ifconfig provider [#7174](https://github.com/chef/chef/pull/7174) ([thommay](https://github.com/thommay))
+- CLI help text now includes :trace log level [#7186](https://github.com/chef/chef/pull/7186) ([stuartpreston](https://github.com/stuartpreston))
+- Fix NoMethodError when (un)locking single packages in apt and zypper [#7138](https://github.com/chef/chef/pull/7138) ([RoboticCheese](https://github.com/RoboticCheese))
+- Whitelist some additional Hash/Array methods [#7198](https://github.com/chef/chef/pull/7198) ([lamont-granquist](https://github.com/lamont-granquist))
+- Convert some of remote_directory to use properties [#7204](https://github.com/chef/chef/pull/7204) ([tas50](https://github.com/tas50))
+- Don&#39;t always request lazy files [#7208](https://github.com/chef/chef/pull/7208) ([thommay](https://github.com/thommay))
+- Allow specifying `ignore_failure :quiet` to disable the error spew [#7194](https://github.com/chef/chef/pull/7194) ([coderanger](https://github.com/coderanger))
+- [MSYS-752] windows task rewrite using win32-taskscheduler [#6815](https://github.com/chef/chef/pull/6815) ([Vasu1105](https://github.com/Vasu1105))
+- Trying to use --recipe-url on Windows with local file fails [#7223](https://github.com/chef/chef/pull/7223) ([tyler-ball](https://github.com/tyler-ball))
+<!-- latest_stable_release -->
+
+## [v14.0.202](https://github.com/chef/chef/tree/v14.0.202) (2018-04-16)
+
+#### Merged Pull Requests
+- Update InSpec to 2.1.21 [#7109](https://github.com/chef/chef/pull/7109) ([tas50](https://github.com/tas50))
+- add delegator for property_is_set? to providers [#7122](https://github.com/chef/chef/pull/7122) ([lamont-granquist](https://github.com/lamont-granquist))
+- Modify the provides for all resources from cookbooks so chef wins [#7134](https://github.com/chef/chef/pull/7134) ([tas50](https://github.com/tas50))
+- Fix RHSM registration using passwords [#7133](https://github.com/chef/chef/pull/7133) ([tas50](https://github.com/tas50))
+- fix Chef-14 chef_fs/chef-zero perf regression [#7143](https://github.com/chef/chef/pull/7143) ([lamont-granquist](https://github.com/lamont-granquist))
+- fix for enable/disable repo ordering [#7148](https://github.com/chef/chef/pull/7148) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix up knife logging [#7144](https://github.com/chef/chef/pull/7144) ([thommay](https://github.com/thommay))
+- Add support for route metric [#7140](https://github.com/chef/chef/pull/7140) ([tomdoherty](https://github.com/tomdoherty))
+- add the resources() dsl method back to providers [#7152](https://github.com/chef/chef/pull/7152) ([lamont-granquist](https://github.com/lamont-granquist))
+- bump omnibus [#7157](https://github.com/chef/chef/pull/7157) ([lamont-granquist](https://github.com/lamont-granquist))
+- add support for lock bot [#7136](https://github.com/chef/chef/pull/7136) ([lamont-granquist](https://github.com/lamont-granquist))
+- Catch json.load exceptions causing syslog errors [#7155](https://github.com/chef/chef/pull/7155) ([tomdoherty](https://github.com/tomdoherty))
+
+## [v14.0.190](https://github.com/chef/chef/tree/v14.0.190) (2018-04-03)
+
+#### Merged Pull Requests
+- Remove erl_call and deploy resources [#6753](https://github.com/chef/chef/pull/6753) ([tas50](https://github.com/tas50))
+- Remove knife index rebuild command that requires Chef &lt; 11 [#6728](https://github.com/chef/chef/pull/6728) ([tas50](https://github.com/tas50))
+- Remove deprecated -r option for Solo mode [#6719](https://github.com/chef/chef/pull/6719) ([tas50](https://github.com/tas50))
+- Chef 14: Remove deprecated knife ssh csshx command [#6444](https://github.com/chef/chef/pull/6444) ([tas50](https://github.com/tas50))
+- Add dhparam, rsa_private_key and rsa_public_key resources [#6736](https://github.com/chef/chef/pull/6736) ([tas50](https://github.com/tas50))
+- Remove deprecated knife ssh --identity-file option [#6445](https://github.com/chef/chef/pull/6445) ([tas50](https://github.com/tas50))
+- Prevent knife search --id-only from outputting IDs in the same format… [#6742](https://github.com/chef/chef/pull/6742) ([zanecodes](https://github.com/zanecodes))
+- Remove update-rc.d -n (dryrun) option. [#6723](https://github.com/chef/chef/pull/6723) ([vinsonlee](https://github.com/vinsonlee))
+- Remove unused chef_server_fqdn argument: run_status [#6670](https://github.com/chef/chef/pull/6670) ([jasonwbarnett](https://github.com/jasonwbarnett))
+- Fixing missing and/or inconsistent quoting in knife search documentation [#6607](https://github.com/chef/chef/pull/6607) ([andyfeller](https://github.com/andyfeller))
+- add create and delete actions for windows_service [#6595](https://github.com/chef/chef/pull/6595) ([jasonwbarnett](https://github.com/jasonwbarnett))
+- Convert actions in Chef::Resource::Notification to symbols to prevent double notification [#6515](https://github.com/chef/chef/pull/6515) ([dimsh99](https://github.com/dimsh99))
+- Revert &quot;add create and delete actions for windows_service&quot; [#6763](https://github.com/chef/chef/pull/6763) ([lamont-granquist](https://github.com/lamont-granquist))
+- Rename the OpenSSL mixin to avoid name conflicts [#6764](https://github.com/chef/chef/pull/6764) ([tas50](https://github.com/tas50))
+- Remove node.set and node.set_unless attribute levels [#6762](https://github.com/chef/chef/pull/6762) ([lamont-granquist](https://github.com/lamont-granquist))
+- Convert node map to last-writer-wins for ties [#6765](https://github.com/chef/chef/pull/6765) ([lamont-granquist](https://github.com/lamont-granquist))
+- [MSYS-727] Added support for setting node policy name and group from knife [#6656](https://github.com/chef/chef/pull/6656) ([piyushawasthi](https://github.com/piyushawasthi))
+- Fail on interval runs on windows [#6766](https://github.com/chef/chef/pull/6766) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump to ruby 2.5.0 [#6770](https://github.com/chef/chef/pull/6770) ([thommay](https://github.com/thommay))
+- Fix the changelog for the 13.7 release [#6772](https://github.com/chef/chef/pull/6772) ([tas50](https://github.com/tas50))
+- Revert &quot;Fail on interval runs on windows&quot; [#6776](https://github.com/chef/chef/pull/6776) ([lamont-granquist](https://github.com/lamont-granquist))
+- speed up http func tests [#6775](https://github.com/chef/chef/pull/6775) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix regression where message isn&#39;t an identity property in log resource [#6779](https://github.com/chef/chef/pull/6779) ([tas50](https://github.com/tas50))
+- update immutable API blacklist and whitelist [#6778](https://github.com/chef/chef/pull/6778) ([lamont-granquist](https://github.com/lamont-granquist))
+- Added idempotent checks to windows_task_spec [#6761](https://github.com/chef/chef/pull/6761) ([NAshwini](https://github.com/NAshwini))
+- [MSYS-724] Chef::Util::Windows::LogonSession should allow having only the prescribed users permissions [#6687](https://github.com/chef/chef/pull/6687) ([NimishaS](https://github.com/NimishaS))
+- allow uninstall of bundler to fail [#6789](https://github.com/chef/chef/pull/6789) ([lamont-granquist](https://github.com/lamont-granquist))
+- fix node assignment of ImmutableArrays to VividMashes/AttrArrays [#6790](https://github.com/chef/chef/pull/6790) ([lamont-granquist](https://github.com/lamont-granquist))
+- use a relative link so that docker does not drop our ca bundle link [#6796](https://github.com/chef/chef/pull/6796) ([thommay](https://github.com/thommay))
+- Force the creation of a relative link for cacerts [#6798](https://github.com/chef/chef/pull/6798) ([thommay](https://github.com/thommay))
+- Nillable properties are the default now [#6800](https://github.com/chef/chef/pull/6800) ([thommay](https://github.com/thommay))
+- Remove epic_fail alias to ignore_failure [#6801](https://github.com/chef/chef/pull/6801) ([tas50](https://github.com/tas50))
+- Use attempt or attempts in the retries logging [#6802](https://github.com/chef/chef/pull/6802) ([tas50](https://github.com/tas50))
+- Add MSIFASTINSTALL property, supported by Windows Installer 5.0 [#6806](https://github.com/chef/chef/pull/6806) ([stuartpreston](https://github.com/stuartpreston))
+- Add create, delete and configure actions to windows_service [#6804](https://github.com/chef/chef/pull/6804) ([jasonwbarnett](https://github.com/jasonwbarnett))
+- Update error handling for &quot;knife status&quot; #3287 [#6756](https://github.com/chef/chef/pull/6756) ([cramaechi](https://github.com/cramaechi))
+- use a stricter comparison so knife ssh only fails if --exit-on-error [#6582](https://github.com/chef/chef/pull/6582) ([sarkis](https://github.com/sarkis))
+- Ensure package (un)locking is idempotent [#6494](https://github.com/chef/chef/pull/6494) ([Rarian](https://github.com/Rarian))
+- Fix Appveyor testing: the format of this flametest doesn&#39;t matter [#6808](https://github.com/chef/chef/pull/6808) ([lamont-granquist](https://github.com/lamont-granquist))
+- Remove the spec for epic_fail [#6809](https://github.com/chef/chef/pull/6809) ([tas50](https://github.com/tas50))
+- Update /etc/fstab on FreeBSD #4959 [#6782](https://github.com/chef/chef/pull/6782) ([cramaechi](https://github.com/cramaechi))
+- Avoid dpkg prompts for modified config files [#6810](https://github.com/chef/chef/pull/6810) ([thommay](https://github.com/thommay))
+- guard against somehow being called by the package resource [#6820](https://github.com/chef/chef/pull/6820) ([thommay](https://github.com/thommay))
+- Remove testing of Debian 7 as it&#39;s going EOL [#6826](https://github.com/chef/chef/pull/6826) ([tas50](https://github.com/tas50))
+- Fix windows_task idle_time validation [#6807](https://github.com/chef/chef/pull/6807) ([algaut](https://github.com/algaut))
+- Grammar fixes in windows_task [#6828](https://github.com/chef/chef/pull/6828) ([Happycoil](https://github.com/Happycoil))
+- Link to the knife docs when the knife config file is missing [#6364](https://github.com/chef/chef/pull/6364) ([tas50](https://github.com/tas50))
+- Don&#39;t rely on the Passwd Ohai plugin in resources [#6833](https://github.com/chef/chef/pull/6833) ([thommay](https://github.com/thommay))
+- Simplify powershell_out calls in powershell_package [#6837](https://github.com/chef/chef/pull/6837) ([Happycoil](https://github.com/Happycoil))
+- Use the license_scout that comes with Omnibus gem [#6839](https://github.com/chef/chef/pull/6839) ([tduffield](https://github.com/tduffield))
+- add additional systemd_unit actions [#6835](https://github.com/chef/chef/pull/6835) ([nathwill](https://github.com/nathwill))
+- Implement resource enhancement RFCs [#6818](https://github.com/chef/chef/pull/6818) ([thommay](https://github.com/thommay))
+- invites_sort_fail: Clean the invites array before sorting it [#6463](https://github.com/chef/chef/pull/6463) ([MarkGibbons](https://github.com/MarkGibbons))
+- Fix issue #2351, chef-client doesn&#39;t make /etc/chef if the directory … [#6429](https://github.com/chef/chef/pull/6429) ([jseely](https://github.com/jseely))
+- [MSYS-726] Allow setting environment variables at the user level [#6612](https://github.com/chef/chef/pull/6612) ([harikesh-kolekar](https://github.com/harikesh-kolekar))
+- RemoteFile: unlink tempfile when using cache control shows unchanged [#6822](https://github.com/chef/chef/pull/6822) ([lamont-granquist](https://github.com/lamont-granquist))
+- only run windows env specs on windows [#6850](https://github.com/chef/chef/pull/6850) ([thommay](https://github.com/thommay))
+- add Chef::NodeMap#delete_class API [#6846](https://github.com/chef/chef/pull/6846) ([lamont-granquist](https://github.com/lamont-granquist))
+- Use the updated inspec gem - 1.51.18 [#6845](https://github.com/chef/chef/pull/6845) ([tas50](https://github.com/tas50))
+- registry_key: Add sensitive property support for suppressing output (fixes #5695) [#6496](https://github.com/chef/chef/pull/6496) ([shoekstra](https://github.com/shoekstra))
+- Add hostname resource from chef_hostname cookbook [#6795](https://github.com/chef/chef/pull/6795) ([tas50](https://github.com/tas50))
+- fix ohai tests after require_plugin removal [#6867](https://github.com/chef/chef/pull/6867) ([thommay](https://github.com/thommay))
+- Add new Redhat Subscription Manager resources [#6827](https://github.com/chef/chef/pull/6827) ([tas50](https://github.com/tas50))
+- Add powershell_package source param [#6843](https://github.com/chef/chef/pull/6843) ([Happycoil](https://github.com/Happycoil))
+- Add ohai_hint resource from ohai cookbook [#6793](https://github.com/chef/chef/pull/6793) ([tas50](https://github.com/tas50))
+- check identifier to resolve exported cookbooks by chef export [#6859](https://github.com/chef/chef/pull/6859) ([sawanoboly](https://github.com/sawanoboly))
+- fix chefstyle [#6874](https://github.com/chef/chef/pull/6874) ([thommay](https://github.com/thommay))
+- make sure all proxy settings are dealt with [#6875](https://github.com/chef/chef/pull/6875) ([thommay](https://github.com/thommay))
+- updating paranoid to verify_host_key [#6869](https://github.com/chef/chef/pull/6869) ([tarcinil](https://github.com/tarcinil))
+- Add macos_user_defaults resource from mac_os_x cookbook [#6878](https://github.com/chef/chef/pull/6878) ([tas50](https://github.com/tas50))
+- Disable sudo on unit tests [#6879](https://github.com/chef/chef/pull/6879) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update libxml2 to 2.9.7 [#6885](https://github.com/chef/chef/pull/6885) ([tas50](https://github.com/tas50))
+- Allow tarballs generated by chef export to be used [#6871](https://github.com/chef/chef/pull/6871) ([thommay](https://github.com/thommay))
+- Add new introduced and description resource properties to many resources [#6854](https://github.com/chef/chef/pull/6854) ([tas50](https://github.com/tas50))
+- The end of our long travis unit testing nightmare [#6888](https://github.com/chef/chef/pull/6888) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add chef_handler resource from chef_handler cookbook [#6895](https://github.com/chef/chef/pull/6895) ([tas50](https://github.com/tas50))
+- Modernize macosx_service [#6908](https://github.com/chef/chef/pull/6908) ([tas50](https://github.com/tas50))
+- Modernize mdadm [#6905](https://github.com/chef/chef/pull/6905) ([tas50](https://github.com/tas50))
+- Simplify how we define the powershell_package resource_name [#6901](https://github.com/chef/chef/pull/6901) ([tas50](https://github.com/tas50))
+- revert lazy attributes [#6911](https://github.com/chef/chef/pull/6911) ([lamont-granquist](https://github.com/lamont-granquist))
+- Convert parts of cron resource to use properties [#6904](https://github.com/chef/chef/pull/6904) ([tas50](https://github.com/tas50))
+- Add more introduced and description fields to resources [#6899](https://github.com/chef/chef/pull/6899) ([tas50](https://github.com/tas50))
+- surface default guard interpreter errors [#5972](https://github.com/chef/chef/pull/5972) ([nathwill](https://github.com/nathwill))
+- Add windows auto_run, font, pagefile, printer, printer_port, and shortcut resources [#6767](https://github.com/chef/chef/pull/6767) ([tas50](https://github.com/tas50))
+- Add description, validation_message, and introduced fields into openssl resources [#6855](https://github.com/chef/chef/pull/6855) ([tas50](https://github.com/tas50))
+- Added Flag to distinguish between gateway and host key to fix issue #6210 [#6514](https://github.com/chef/chef/pull/6514) ([erikparra](https://github.com/erikparra))
+- Don&#39;t use .eql? in the aix mount provider [#6915](https://github.com/chef/chef/pull/6915) ([tas50](https://github.com/tas50))
+- [knife] Don&#39;t crash when a deprecated cookbook has no replacement [#6853](https://github.com/chef/chef/pull/6853) ([rlyders](https://github.com/rlyders))
+- Add support for knife bootstrap-preinstall-command [#6861](https://github.com/chef/chef/pull/6861) ([smcavallo](https://github.com/smcavallo))
+- Raise fatal error If FQDN duplicated [#6781](https://github.com/chef/chef/pull/6781) ([linyows](https://github.com/linyows))
+- Stop mixlib-cli default clobbering mixlib-config settings [#6916](https://github.com/chef/chef/pull/6916) ([lamont-granquist](https://github.com/lamont-granquist))
+- fix for master of chefstyle [#6919](https://github.com/chef/chef/pull/6919) ([lamont-granquist](https://github.com/lamont-granquist))
+- fixing red omnibus builds [#6921](https://github.com/chef/chef/pull/6921) ([lamont-granquist](https://github.com/lamont-granquist))
+- Revert appbundler to 0.10.0 [#6922](https://github.com/chef/chef/pull/6922) ([lamont-granquist](https://github.com/lamont-granquist))
+- need to pin appbundler in omnibus [#6924](https://github.com/chef/chef/pull/6924) ([lamont-granquist](https://github.com/lamont-granquist))
+- Remove git ref on bundler-audit [#6925](https://github.com/chef/chef/pull/6925) ([lamont-granquist](https://github.com/lamont-granquist))
+- this really needs to be turned into a yml file... [#6926](https://github.com/chef/chef/pull/6926) ([lamont-granquist](https://github.com/lamont-granquist))
+- Remove the :uninstall action from chocolatey_package - CHEF-21 [#6920](https://github.com/chef/chef/pull/6920) ([tas50](https://github.com/tas50))
+- use appbundler 0.11.1 for omnibus builds [#6930](https://github.com/chef/chef/pull/6930) ([lamont-granquist](https://github.com/lamont-granquist))
+- bump appbundler again, again [#6931](https://github.com/chef/chef/pull/6931) ([lamont-granquist](https://github.com/lamont-granquist))
+- Allow specifying a comment for routes [#6929](https://github.com/chef/chef/pull/6929) ([tomdoherty](https://github.com/tomdoherty))
+- Update our tests based on new resources we ship [#6939](https://github.com/chef/chef/pull/6939) ([tas50](https://github.com/tas50))
+- Add Ubuntu 18.04 Testing in Travis [#6937](https://github.com/chef/chef/pull/6937) ([tas50](https://github.com/tas50))
+- Apt repo cleanup and testing expansion [#6498](https://github.com/chef/chef/pull/6498) ([tas50](https://github.com/tas50))
+- Use the existing helper method for package resource classes that don&#39;t support allow_downgrade [#6942](https://github.com/chef/chef/pull/6942) ([coderanger](https://github.com/coderanger))
+- Set properties in git resource using our resource DSL [#6902](https://github.com/chef/chef/pull/6902) ([tas50](https://github.com/tas50))
+- Modernize provides in the portage_package resource [#6903](https://github.com/chef/chef/pull/6903) ([tas50](https://github.com/tas50))
+- registry_key: Properly limit allowed values for architecture [#6947](https://github.com/chef/chef/pull/6947) ([tas50](https://github.com/tas50))
+- Add more description fields, style fixes, add missing requires [#6943](https://github.com/chef/chef/pull/6943) ([tas50](https://github.com/tas50))
+- Add attribute hoisting into core [#6927](https://github.com/chef/chef/pull/6927) ([jonlives](https://github.com/jonlives))
+- Don&#39;t use supervisor process for one-shot / command-line runs [#6914](https://github.com/chef/chef/pull/6914) ([lamont-granquist](https://github.com/lamont-granquist))
+- add a utility to dump info about resources [#6896](https://github.com/chef/chef/pull/6896) ([thommay](https://github.com/thommay))
+- Remove support for Windows 2003 [#6923](https://github.com/chef/chef/pull/6923) ([tas50](https://github.com/tas50))
+- Avoid compile time error in apt_repository [#6953](https://github.com/chef/chef/pull/6953) ([tas50](https://github.com/tas50))
+- Added source_file to FromFile [#6938](https://github.com/chef/chef/pull/6938) ([zfjagann](https://github.com/zfjagann))
+- remove deprecated property namespace collisions [#6952](https://github.com/chef/chef/pull/6952) ([lamont-granquist](https://github.com/lamont-granquist))
+- Remove explicit setting of @provider and depend on ProviderResolver [#6958](https://github.com/chef/chef/pull/6958) ([jasonwbarnett](https://github.com/jasonwbarnett))
+- Convert more set_or_returns to proper properties [#6950](https://github.com/chef/chef/pull/6950) ([tas50](https://github.com/tas50))
+- Rename bff provider to match its resource [#6956](https://github.com/chef/chef/pull/6956) ([tas50](https://github.com/tas50))
+- New interop between Chef and PowerShell 4.0 (or higher) [#6941](https://github.com/chef/chef/pull/6941) ([stuartpreston](https://github.com/stuartpreston))
+- Remove the manpages [#6974](https://github.com/chef/chef/pull/6974) ([tas50](https://github.com/tas50))
+- RFC 106: expose name and chef_environment as attrs [#6967](https://github.com/chef/chef/pull/6967) ([thommay](https://github.com/thommay))
+- Use node.override not node.normal in the windows_feature_dism resource [#6962](https://github.com/chef/chef/pull/6962) ([tas50](https://github.com/tas50))
+- Remove platfom restrictions in provides and don&#39;t require providers [#6957](https://github.com/chef/chef/pull/6957) ([tas50](https://github.com/tas50))
+- Properly validate reboot_action in dsc_resource [#6951](https://github.com/chef/chef/pull/6951) ([tas50](https://github.com/tas50))
+- Don&#39;t use String.new in the cron provider [#6976](https://github.com/chef/chef/pull/6976) ([tas50](https://github.com/tas50))
+- Knife should give a useful error when the chef_server_url isn&#39;t a chef server API [#6253](https://github.com/chef/chef/pull/6253) ([jeunito](https://github.com/jeunito))
+- Bump to ruby 2.5.0 [#6838](https://github.com/chef/chef/pull/6838) ([thommay](https://github.com/thommay))
+- Add output_locations functionality to data collector [#6873](https://github.com/chef/chef/pull/6873) ([jonlives](https://github.com/jonlives))
+- Require Ruby 2.4+ [#6983](https://github.com/chef/chef/pull/6983) ([tas50](https://github.com/tas50))
+- Pass pointer to LsaFreeMemory, not FFI::MemoryPointer [#6980](https://github.com/chef/chef/pull/6980) ([btm](https://github.com/btm))
+- Stripping out Authorization header on redirect to a different host [#6985](https://github.com/chef/chef/pull/6985) ([bugok](https://github.com/bugok))
+- Remove knife help which used the manpages [#6982](https://github.com/chef/chef/pull/6982) ([tas50](https://github.com/tas50))
+- update mount to use properties and fix 6851 [#6969](https://github.com/chef/chef/pull/6969) ([thommay](https://github.com/thommay))
+- Yum refactor [#6540](https://github.com/chef/chef/pull/6540) ([lamont-granquist](https://github.com/lamont-granquist))
+- Use Chef omnibus def that includes libarchive [#6993](https://github.com/chef/chef/pull/6993) ([tas50](https://github.com/tas50))
+- Revert &quot;Stripping out Authorization header on redirect to a different host [#6996](https://github.com/chef/chef/pull/6996) ([tas50](https://github.com/tas50))
+- Add the sudo resource from the sudo resource [#6979](https://github.com/chef/chef/pull/6979) ([tas50](https://github.com/tas50))
+- Add more resource descriptions and convert resources to use properties [#6994](https://github.com/chef/chef/pull/6994) ([tas50](https://github.com/tas50))
+- Lazy eval empty Hash/Array resource properties. [#6997](https://github.com/chef/chef/pull/6997) ([tas50](https://github.com/tas50))
+- Detect new &quot;automatically&quot; installed string in Zypper [#7009](https://github.com/chef/chef/pull/7009) ([tas50](https://github.com/tas50))
+- Fail with a warning if users specify apt/yum/zypper repos with slashes [#7000](https://github.com/chef/chef/pull/7000) ([tas50](https://github.com/tas50))
+- Remove Chef 12-isms from the apt_repository resource [#6998](https://github.com/chef/chef/pull/6998) ([tas50](https://github.com/tas50))
+- Add dmg_package, homebrew_cask, and homebrew_tap resources [#6963](https://github.com/chef/chef/pull/6963) ([tas50](https://github.com/tas50))
+- Add missing installed logic for macos in build_essential [#7005](https://github.com/chef/chef/pull/7005) ([tas50](https://github.com/tas50))
+- memoize some work in the package class [#6661](https://github.com/chef/chef/pull/6661) ([lamont-granquist](https://github.com/lamont-granquist))
+- Pagefile sizes are in megabytes not bytes [#7019](https://github.com/chef/chef/pull/7019) ([tas50](https://github.com/tas50))
+- Support installing removed windows features from source [#7015](https://github.com/chef/chef/pull/7015) ([tas50](https://github.com/tas50))
+- Save the node&#39;s UUID as an attribute [#7016](https://github.com/chef/chef/pull/7016) ([thommay](https://github.com/thommay))
+- rubocop fixes from engine bump to 0.54.0 [#7023](https://github.com/chef/chef/pull/7023) ([lamont-granquist](https://github.com/lamont-granquist))
+- remove dead code from property declaration [#7025](https://github.com/chef/chef/pull/7025) ([lamont-granquist](https://github.com/lamont-granquist))
+- sudo: Don&#39;t fail on FreeBSD. Turns out there&#39;s a .d directory [#7033](https://github.com/chef/chef/pull/7033) ([tas50](https://github.com/tas50))
+- Add sysctl_param resource from the sysctl cookbook [#7022](https://github.com/chef/chef/pull/7022) ([tas50](https://github.com/tas50))
+- Suppress nested causes for sensitive exceptions [#7032](https://github.com/chef/chef/pull/7032) ([thommay](https://github.com/thommay))
+- Use the latest libarchive/bzip2 defs in omnibus [#7035](https://github.com/chef/chef/pull/7035) ([tas50](https://github.com/tas50))
+- Add windows_adjoin resource [#6981](https://github.com/chef/chef/pull/6981) ([tas50](https://github.com/tas50))
+- Fix a few bugs in the sudo resource [#7038](https://github.com/chef/chef/pull/7038) ([tas50](https://github.com/tas50))
+- Upgrade to openssl 1.1 [#7044](https://github.com/chef/chef/pull/7044) ([tas50](https://github.com/tas50))
+- Revert &quot;Upgrade to openssl 1.1&quot; [#7045](https://github.com/chef/chef/pull/7045) ([tas50](https://github.com/tas50))
+- Add swap_file resource from the swap cookbook [#6990](https://github.com/chef/chef/pull/6990) ([tas50](https://github.com/tas50))
+- Parser 2.5.0.4 was yanked [#7055](https://github.com/chef/chef/pull/7055) ([tas50](https://github.com/tas50))
+- Ensure that we pass the correct options to mount [#7030](https://github.com/chef/chef/pull/7030) ([thommay](https://github.com/thommay))
+- RFC-102: Deprecation warning in resources [#7050](https://github.com/chef/chef/pull/7050) ([thommay](https://github.com/thommay))
+- Ship InSpec 2 [#7051](https://github.com/chef/chef/pull/7051) ([thommay](https://github.com/thommay))
+- Update information on updating gems / Expeditor [#7057](https://github.com/chef/chef/pull/7057) ([tas50](https://github.com/tas50))
+- Update openssl to 1.0.2o [#7075](https://github.com/chef/chef/pull/7075) ([tas50](https://github.com/tas50))
+- Add / update resource descriptions [#7073](https://github.com/chef/chef/pull/7073) ([tas50](https://github.com/tas50))
+- Sudo resource: specify ruby type for visudo_binary [#7076](https://github.com/chef/chef/pull/7076) ([brewn](https://github.com/brewn))
+- Add Chef 13.8 and 14.0 release notes [#7074](https://github.com/chef/chef/pull/7074) ([tas50](https://github.com/tas50))
+- Fix array parsing in windows_feature_dism / windows_feature_powershell [#7078](https://github.com/chef/chef/pull/7078) ([tas50](https://github.com/tas50))
+- Setting nil to properties with implicit nil sets default value [#7037](https://github.com/chef/chef/pull/7037) ([lamont-granquist](https://github.com/lamont-granquist))
+- windows_feature_dism: Be case insensitive with feature names [#7079](https://github.com/chef/chef/pull/7079) ([tas50](https://github.com/tas50))
+- Add basic hostname validation on Windows [#7087](https://github.com/chef/chef/pull/7087) ([tas50](https://github.com/tas50))
+- [windows_ad_join] add description for :join action [#7082](https://github.com/chef/chef/pull/7082) ([brewn](https://github.com/brewn))
+- [windows_font] get rid of &quot;remove&quot; in description [#7089](https://github.com/chef/chef/pull/7089) ([brewn](https://github.com/brewn))
+- Fix method missing error in dmg_package [#7091](https://github.com/chef/chef/pull/7091) ([tas50](https://github.com/tas50))
+- Avoid lookups for rights of &#39;LocalSystem&#39; in windows service [#7083](https://github.com/chef/chef/pull/7083) ([btm](https://github.com/btm))
+- macos_userdefaults: Fix 2 failures [#7095](https://github.com/chef/chef/pull/7095) ([tas50](https://github.com/tas50))
+- Bump Ruby to 2.5.1 [#7090](https://github.com/chef/chef/pull/7090) ([tas50](https://github.com/tas50))
+- homebrew_tap / homebrew_cask: Fix compile time errors with the user mixin [#7097](https://github.com/chef/chef/pull/7097) ([tas50](https://github.com/tas50))
+- scrub tempfile names [#7104](https://github.com/chef/chef/pull/7104) ([lamont-granquist](https://github.com/lamont-granquist))
+- Address possible gem installs between interval runs that are then used in the config [#7106](https://github.com/chef/chef/pull/7106) ([coderanger](https://github.com/coderanger))
+- Bring in the windows_feature_powershell improvements from the cookbook [#7098](https://github.com/chef/chef/pull/7098) ([tas50](https://github.com/tas50))
+- Stripping Authorization header upon redirects (second try) [#7006](https://github.com/chef/chef/pull/7006) ([bugok](https://github.com/bugok))
+- [CHEF-7026] Rewrite portage package provider candidate_version determination and fix tests [#7027](https://github.com/chef/chef/pull/7027) ([gengor](https://github.com/gengor))
+- Don&#39;t fail on every hostname with windows [#7107](https://github.com/chef/chef/pull/7107) ([tas50](https://github.com/tas50))
+- [windows_printer_port] fix typo + add action descriptions [#7093](https://github.com/chef/chef/pull/7093) ([brewn](https://github.com/brewn))
+
+## [v13.8.3](https://github.com/chef/chef/tree/v13.8.3) (2018-03-05)
+
+#### Merged Pull Requests
+- Link to the knife.rb docs when the knife.rb file is missing [#6892](https://github.com/chef/chef/pull/6892) ([tas50](https://github.com/tas50))
+- Revert &quot;Revert &quot;fixup some unit tests&quot;&quot; [#6912](https://github.com/chef/chef/pull/6912) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump depenencies to pull in Ohai 13.8 / InSpec 1.51.21 [#6934](https://github.com/chef/chef/pull/6934) ([tas50](https://github.com/tas50))
+
+## [v13.8.0](https://github.com/chef/chef/tree/v13.8.0) (2018-02-27)
+
+#### Merged Pull Requests
+- Fix regression where message isn&#39;t an identity property in log resource [#6780](https://github.com/chef/chef/pull/6780) ([tas50](https://github.com/tas50))
+- fix node assignment of ImmutableArrays to VividMashes/AttrArrays (Chef-13 backport) [#6791](https://github.com/chef/chef/pull/6791) ([lamont-granquist](https://github.com/lamont-granquist))
+- Ensure that we create a docker compatible ca-certs symlink [#6799](https://github.com/chef/chef/pull/6799) ([thommay](https://github.com/thommay))
+- Backport the powershell spec fix to get Appveyor green again [#6813](https://github.com/chef/chef/pull/6813) ([tas50](https://github.com/tas50))
+- Use the version of LicenseScout that comes with the Omnibus gem. [#6841](https://github.com/chef/chef/pull/6841) ([tduffield](https://github.com/tduffield))
+- RemoteFile: unlink tempfile when using cache control shows unchanged (Chef-13 backport) [#6849](https://github.com/chef/chef/pull/6849) ([lamont-granquist](https://github.com/lamont-granquist))
+- add Chef::NodeMap#delete_class API (Chef 13 backport) [#6848](https://github.com/chef/chef/pull/6848) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix windows_task idle_time validation [#6856](https://github.com/chef/chef/pull/6856) ([tas50](https://github.com/tas50))
+- Update libxml2 to 2.9.7 [#6886](https://github.com/chef/chef/pull/6886) ([tas50](https://github.com/tas50))
+- use a stricter comparison so knife ssh only fails if --exit-on-error [#6894](https://github.com/chef/chef/pull/6894) ([tas50](https://github.com/tas50))
+- Prevent knife search --id-only from outputting IDs in the same format as an empty hash [#6893](https://github.com/chef/chef/pull/6893) ([tas50](https://github.com/tas50))
+- Chef-13 revert lazy attributes [#6898](https://github.com/chef/chef/pull/6898) ([lamont-granquist](https://github.com/lamont-granquist))
+
+## [v13.7.16](https://github.com/chef/chef/tree/v13.7.16) (2018-01-23)
+
+#### Merged Pull Requests
+- Update release notes for 13.7 [#6751](https://github.com/chef/chef/pull/6751) ([thommay](https://github.com/thommay))
+- Revert deprecation of use_inline_resources [#6754](https://github.com/chef/chef/pull/6754) ([tas50](https://github.com/tas50))
+- fix double-logging bug [#6752](https://github.com/chef/chef/pull/6752) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add a warning that Chef 11 server support in knife user is deprecated [#6725](https://github.com/chef/chef/pull/6725) ([tas50](https://github.com/tas50))
+- fix non-daemonized umask [#6745](https://github.com/chef/chef/pull/6745) ([lamont-granquist](https://github.com/lamont-granquist))
+- simplify node_map logic [#6637](https://github.com/chef/chef/pull/6637) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix knife status to show seconds when needed #5055 [#6738](https://github.com/chef/chef/pull/6738) ([cramaechi](https://github.com/cramaechi))
+- Enable the deprecation for use_inline_resource [#6732](https://github.com/chef/chef/pull/6732) ([tas50](https://github.com/tas50))
+- Fix dscl group provider gid_used? [#6703](https://github.com/chef/chef/pull/6703) ([get9](https://github.com/get9))
+- Fix windows_task resource not being idempotent for random_delay and execution_time_limit [#6688](https://github.com/chef/chef/pull/6688) ([Vasu1105](https://github.com/Vasu1105))
+- Update to Ruby 2.4.3 [#6737](https://github.com/chef/chef/pull/6737) ([tas50](https://github.com/tas50))
+- DSCL: Check for set home property before file existence (fixes #5777) [#6735](https://github.com/chef/chef/pull/6735) ([get9](https://github.com/get9))
+- Modernize windows_path resource [#6699](https://github.com/chef/chef/pull/6699) ([tas50](https://github.com/tas50))
+- Don&#39;t check both platform_family / os in provides when platform_family will do [#6711](https://github.com/chef/chef/pull/6711) ([tas50](https://github.com/tas50))
+- Update the knife editor error message to point to the correct document [#6726](https://github.com/chef/chef/pull/6726) ([tas50](https://github.com/tas50))
+- Remove a useless regex in zypper_repository resource [#6710](https://github.com/chef/chef/pull/6710) ([tas50](https://github.com/tas50))
+- Deprecate erl_call resource [#6720](https://github.com/chef/chef/pull/6720) ([tas50](https://github.com/tas50))
+- Improve property warnings in resources [#6717](https://github.com/chef/chef/pull/6717) ([tas50](https://github.com/tas50))
+- Remove lock files and test github masters in Kitchen Tests [#6709](https://github.com/chef/chef/pull/6709) ([tas50](https://github.com/tas50))
+- [MSYS-692] Fix issue with PowerShell function buffer [#6664](https://github.com/chef/chef/pull/6664) ([TheLunaticScripter](https://github.com/TheLunaticScripter))
+- Escape single-quoted strings from the context in knife bootstrap [#6695](https://github.com/chef/chef/pull/6695) ([aespinosa](https://github.com/aespinosa))
+- Allow injecting tempfiles into Chef::HTTP [#6701](https://github.com/chef/chef/pull/6701) ([lamont-granquist](https://github.com/lamont-granquist))
+- Modernize launchd resource [#6698](https://github.com/chef/chef/pull/6698) ([tas50](https://github.com/tas50))
+- Add an &#39;s&#39; for quantity of 0 cookbooks. [#6552](https://github.com/chef/chef/pull/6552) ([anoadragon453](https://github.com/anoadragon453))
+- Fix yum_repository allowing priority of 0 and remove string regexes [#6697](https://github.com/chef/chef/pull/6697) ([tas50](https://github.com/tas50))
+- Add descriptions and yard @since comments to all resources [#6696](https://github.com/chef/chef/pull/6696) ([tas50](https://github.com/tas50))
+- Cleanup to some of the resource specs [#6692](https://github.com/chef/chef/pull/6692) ([tas50](https://github.com/tas50))
+- fix for data bag names partially matching search reserved words [#6652](https://github.com/chef/chef/pull/6652) ([sandratiffin](https://github.com/sandratiffin))
+- Modernize directory resource [#6693](https://github.com/chef/chef/pull/6693) ([tas50](https://github.com/tas50))
+- Modernize the ifconfig resource [#6684](https://github.com/chef/chef/pull/6684) ([tas50](https://github.com/tas50))
+- Slight improvements to validation failures [#6690](https://github.com/chef/chef/pull/6690) ([thommay](https://github.com/thommay))
+- Modernize osx_profile resource [#6685](https://github.com/chef/chef/pull/6685) ([tas50](https://github.com/tas50))
+- Modernize cookbook_file resource and expand specs [#6689](https://github.com/chef/chef/pull/6689) ([tas50](https://github.com/tas50))
+- implement credential management [#6660](https://github.com/chef/chef/pull/6660) ([thommay](https://github.com/thommay))
+- Modernize reboot resource and add spec [#6683](https://github.com/chef/chef/pull/6683) ([tas50](https://github.com/tas50))
+- Fix bugs in handling &#39;source&#39; in msu_package and cab_package [#6686](https://github.com/chef/chef/pull/6686) ([tas50](https://github.com/tas50))
+- Move docker and git top cookbook tests to travis [#6673](https://github.com/chef/chef/pull/6673) ([scotthain](https://github.com/scotthain))
+- Modernize the log resource [#6676](https://github.com/chef/chef/pull/6676) ([tas50](https://github.com/tas50))
+- Avoid a few initializers in resources by using the DSL we have [#6671](https://github.com/chef/chef/pull/6671) ([tas50](https://github.com/tas50))
+- Don&#39;t use .match? which is Ruby 2.4+ only in windows_task [#6675](https://github.com/chef/chef/pull/6675) ([tas50](https://github.com/tas50))
+- windows_task: Fix resource isn&#39;t fully idempotent due to command property [#6654](https://github.com/chef/chef/pull/6654) ([Vasu1105](https://github.com/Vasu1105))
+- Invalid date error on windows_task with frequency :on_logon [#6618](https://github.com/chef/chef/pull/6618) ([NimishaS](https://github.com/NimishaS))
+- Fix sneaky chefstyle violations [#6655](https://github.com/chef/chef/pull/6655) ([thommay](https://github.com/thommay))
+- Ensure data bags names can contain reserved words [#6636](https://github.com/chef/chef/pull/6636) ([EmFl](https://github.com/EmFl))
+- windows_task: Add additional input validation to properties [#6628](https://github.com/chef/chef/pull/6628) ([tas50](https://github.com/tas50))
+- Solaris: Fix svcadm clear to only run in maintenance state [#6631](https://github.com/chef/chef/pull/6631) ([jaymalasinha](https://github.com/jaymalasinha))
+- speedup node_map get and set operations [#6632](https://github.com/chef/chef/pull/6632) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update for openssl 1.0.2n and inspec 1.48 [#6630](https://github.com/chef/chef/pull/6630) ([tas50](https://github.com/tas50))
+- Improved windows_task logging [#6617](https://github.com/chef/chef/pull/6617) ([tas50](https://github.com/tas50))
+- Update InSpec to 1.47 and Ohai to 13.7 [#6616](https://github.com/chef/chef/pull/6616) ([tas50](https://github.com/tas50))
+- Add openSUSE testing in Travis &amp; expand cookbooks we test [#6614](https://github.com/chef/chef/pull/6614) ([tas50](https://github.com/tas50))
+- Knife SSH prefix option [#6590](https://github.com/chef/chef/pull/6590) ([mal](https://github.com/mal))
+- Add Amazon Linux testing to PRs in Travis [#6611](https://github.com/chef/chef/pull/6611) ([tas50](https://github.com/tas50))
+- Hide sensitive properties in converge_if_changed. [#6576](https://github.com/chef/chef/pull/6576) ([cma-arnold](https://github.com/cma-arnold))
+- Bump dependencies to pick up InSpec v1.46.2 [#6609](https://github.com/chef/chef/pull/6609) ([adamleff](https://github.com/adamleff))
+- Fix windows_path converging on every run [#6541](https://github.com/chef/chef/pull/6541) ([tas50](https://github.com/tas50))
+- add unit_name name_property to systemd_unit (fixes #6542) [#6546](https://github.com/chef/chef/pull/6546) ([nathwill](https://github.com/nathwill))
+- fix NodeMap to not throw exceptions on platform_versions [#6608](https://github.com/chef/chef/pull/6608) ([lamont-granquist](https://github.com/lamont-granquist))
+- Enable Fedora integration testing in Travis [#6523](https://github.com/chef/chef/pull/6523) ([tas50](https://github.com/tas50))
+- Only warn if a secret was actually given [#6605](https://github.com/chef/chef/pull/6605) ([coderanger](https://github.com/coderanger))
+- Makes life easier for hook authors switching from the older report handler syntax [#6574](https://github.com/chef/chef/pull/6574) ([coderanger](https://github.com/coderanger))
+- [MSYS-688] Fixed invalid date and Invalid starttime error [#6544](https://github.com/chef/chef/pull/6544) ([NimishaS](https://github.com/NimishaS))
+- Selinux shellout fix (#6346) [#6567](https://github.com/chef/chef/pull/6567) ([deltamualpha](https://github.com/deltamualpha))
+- Switch from the Travis container to the VM [#6600](https://github.com/chef/chef/pull/6600) ([btm](https://github.com/btm))
+- Don&#39;t try to uninstall bundler on appveyor [#6597](https://github.com/chef/chef/pull/6597) ([btm](https://github.com/btm))
+- Fix variable name in solaris service provider [#6596](https://github.com/chef/chef/pull/6596) ([jaymalasinha](https://github.com/jaymalasinha))
+- Revert &quot;add missing functional tests for users&quot; [#6588](https://github.com/chef/chef/pull/6588) ([lamont-granquist](https://github.com/lamont-granquist))
+- Filter out periods from tmux session name [#6593](https://github.com/chef/chef/pull/6593) ([afn](https://github.com/afn))
+- Fix mount test, also update ifconfig to work with both common versions. [#6587](https://github.com/chef/chef/pull/6587) ([scotthain](https://github.com/scotthain))
+- Change a useradd_spec test for RHEL &gt;= 6.8 and &gt;= 7.3 [#6555](https://github.com/chef/chef/pull/6555) ([jeremiahsnapp](https://github.com/jeremiahsnapp))
+- replace deprecated Dir.exists? with Dir.exist? [#6583](https://github.com/chef/chef/pull/6583) ([thomasdziedzic](https://github.com/thomasdziedzic))
+- Add ohai_time to minimal_ohai filter [#6584](https://github.com/chef/chef/pull/6584) ([btm](https://github.com/btm))
+
+## [v13.6.4](https://github.com/chef/chef/tree/v13.6.4) (2017-11-06)
+
+#### Merged Pull Requests
+- [MSYS-492]Add missing functional tests for users [#6425](https://github.com/chef/chef/pull/6425) ([harikesh-kolekar](https://github.com/harikesh-kolekar))
+- Fix the invalid version comparison in apt/dpkg providers [#6558](https://github.com/chef/chef/pull/6558) ([tas50](https://github.com/tas50))
+- Fix the invalid version comparison of apt packages. [#6554](https://github.com/chef/chef/pull/6554) ([komazarari](https://github.com/komazarari))
+- Bump openssl and rubygems to latest [#6568](https://github.com/chef/chef/pull/6568) ([tas50](https://github.com/tas50))
+
+## [v13.6.0](https://github.com/chef/chef/tree/v13.6.0) (2017-10-30)
+
+#### Merged Pull Requests
+- Bump InSpec to v1.40.0 [#6460](https://github.com/chef/chef/pull/6460) ([adamleff](https://github.com/adamleff))
+- Force encoding to UTF_8 in chef-shell to prevent failures [#6447](https://github.com/chef/chef/pull/6447) ([tas50](https://github.com/tas50))
+- Only warn about skipping sync once [#6454](https://github.com/chef/chef/pull/6454) ([Happycoil](https://github.com/Happycoil))
+- Import the zypper GPG key before templating the repo [#6410](https://github.com/chef/chef/pull/6410) ([tas50](https://github.com/tas50))
+- Fixes to package upgrade behaviour [#6428](https://github.com/chef/chef/pull/6428) ([jonlives](https://github.com/jonlives))
+- Tweak the knife banners for multi-arg commands. [#6466](https://github.com/chef/chef/pull/6466) ([coderanger](https://github.com/coderanger))
+- dnf_resource: be more specific for rhel packages [#6435](https://github.com/chef/chef/pull/6435) ([NaomiReeves](https://github.com/NaomiReeves))
+- Prevent creation of data bags named node, role, client or environment [#6469](https://github.com/chef/chef/pull/6469) ([sanditiffin](https://github.com/sanditiffin))
+- Remove cookbook_artifacts from CHEF_11_OSS_STATIC_OBJECTS [#6478](https://github.com/chef/chef/pull/6478) ([itmustbejj](https://github.com/itmustbejj))
+- Add allow_downgrade to zypper_package resource [#6476](https://github.com/chef/chef/pull/6476) ([yeoldegrove](https://github.com/yeoldegrove))
+- Don&#39;t spin in powershell module that launches chef processes [#6481](https://github.com/chef/chef/pull/6481) ([ksubrama](https://github.com/ksubrama))
+- Sleep for another interval after handling SIGHUP [#6461](https://github.com/chef/chef/pull/6461) ([grekasius](https://github.com/grekasius))
+- Support new CriticalOhaiPlugins [#6486](https://github.com/chef/chef/pull/6486) ([jaymzh](https://github.com/jaymzh))
+- Package: only RHEL &gt;= 8 and Fedora &gt;= 22 get dnf [#6490](https://github.com/chef/chef/pull/6490) ([lamont-granquist](https://github.com/lamont-granquist))
+- Windows: Added :none frequency to windows_task resource [#6394](https://github.com/chef/chef/pull/6394) ([NAshwini](https://github.com/NAshwini))
+- Fix rebooter for solaris and background reboots [#6497](https://github.com/chef/chef/pull/6497) ([lamont-granquist](https://github.com/lamont-granquist))
+- Added parser for DSC configuration [#6473](https://github.com/chef/chef/pull/6473) ([piyushawasthi](https://github.com/piyushawasthi))
+- Add array support for choco pkg from artifactory [#6437](https://github.com/chef/chef/pull/6437) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Bump dependencies to pull in new InSpec [#6511](https://github.com/chef/chef/pull/6511) ([adamleff](https://github.com/adamleff))
+- Fix remote_file with UNC paths failing [#6510](https://github.com/chef/chef/pull/6510) ([tas50](https://github.com/tas50))
+- Deprecate the deploy resource and family [#6468](https://github.com/chef/chef/pull/6468) ([coderanger](https://github.com/coderanger))
+- Include Ohai 13.6 [#6521](https://github.com/chef/chef/pull/6521) ([btm](https://github.com/btm))
+- Use the latest libxml2, libxslt, libyaml, and openssl [#6520](https://github.com/chef/chef/pull/6520) ([tas50](https://github.com/tas50))
+- Pull in the latest libiconv and nokogiri [#6532](https://github.com/chef/chef/pull/6532) ([tas50](https://github.com/tas50))
+
+## [v13.5.3](https://github.com/chef/chef/tree/v13.5.3) (2017-10-03)
+
+#### Merged Pull Requests
+- Fix password property is sensitive for mount resource [#6442](https://github.com/chef/chef/pull/6442) ([dimsh99](https://github.com/dimsh99))
+- Only accept MM/DD/YYYY for windows_task start_day [#6434](https://github.com/chef/chef/pull/6434) ([jaym](https://github.com/jaym))
+- Update dependencies to pull in InSpec v1.39.1 [#6440](https://github.com/chef/chef/pull/6440) ([adamleff](https://github.com/adamleff))
+- Fix Knife search ID only option to actually filter result set [#6438](https://github.com/chef/chef/pull/6438) ([dimsh99](https://github.com/dimsh99))
+- Add throttle and metalink options to yum_repository [#6431](https://github.com/chef/chef/pull/6431) ([tas50](https://github.com/tas50))
+- Don&#39;t catch SIGCHLD from dnf_helper.py [#6416](https://github.com/chef/chef/pull/6416) ([nemith](https://github.com/nemith))
+- Open apt resources up to prevent breaking change [#6417](https://github.com/chef/chef/pull/6417) ([tas50](https://github.com/tas50))
+- Remove unused requires in yum_repository [#6413](https://github.com/chef/chef/pull/6413) ([tas50](https://github.com/tas50))
+- Quiet the output of the zypper refresh and add force [#6408](https://github.com/chef/chef/pull/6408) ([tas50](https://github.com/tas50))
+- Replace which apt-get check with simple debian check in apt resources [#6409](https://github.com/chef/chef/pull/6409) ([tas50](https://github.com/tas50))
+
+## [v12.21.14](https://github.com/chef/chef/tree/v12.21.14) (2017-09-27)
+
+## [v13.4.24](https://github.com/chef/chef/tree/v13.4.24) (2017-09-14)
+
+#### Merged Pull Requests
+- Fixed dsc_script for WMF5 [#6383](https://github.com/chef/chef/pull/6383) ([piyushawasthi](https://github.com/piyushawasthi))
+- windows_task resource is not idempotent when specifying start_time and start_day [#6312](https://github.com/chef/chef/pull/6312) ([harikesh-kolekar](https://github.com/harikesh-kolekar))
+- Allow specifying default gateway on RHEL/Centos [#6386](https://github.com/chef/chef/pull/6386) ([tomdoherty](https://github.com/tomdoherty))
+- Use ruby 2.4.2 to addess multiple security vulnerabilities [#6404](https://github.com/chef/chef/pull/6404) ([thommay](https://github.com/thommay))
+
+## [v13.4.19](https://github.com/chef/chef/tree/v13.4.19) (2017-09-13)
+
+#### Bug Fixes
+- Ignore validation errors in Resource#to_text [#6331](https://github.com/chef/chef/pull/6331) ([coderanger](https://github.com/coderanger))
+- Auto import gpg keys in zypper_repository [#6348](https://github.com/chef/chef/pull/6348) ([tas50](https://github.com/tas50))
+- Handle apple's git in the git resource [#6359](https://github.com/chef/chef/pull/6359) ([kzw](https://github.com/kzw))
+- Launchd should not load launchagents as root. [#6353](https://github.com/chef/chef/pull/6353) ([mikedodge04](https://github.com/mikedodge04))
+- Pass json configuration to ShellSession class [#6314](https://github.com/chef/chef/pull/6314) ([btm](https://github.com/btm))
+
+#### Merged Pull Requests
+- Add windows_path resource from the Windows cookbook [#6295](https://github.com/chef/chef/pull/6295) ([NimishaS](https://github.com/NimishaS))
+- Bump Bundler version to 1.15.4 [#6349](https://github.com/chef/chef/pull/6349) ([jakauppila](https://github.com/jakauppila))
+- dnf_provider: be more specific when we provide `package` [#6351](https://github.com/chef/chef/pull/6351) ([jaymzh](https://github.com/jaymzh))
+- Speed up immutabilization [#6355](https://github.com/chef/chef/pull/6355) ([lamont-granquist](https://github.com/lamont-granquist))
+- Node attributes: remove useless dup in merge_all [#6356](https://github.com/chef/chef/pull/6356) ([lamont-granquist](https://github.com/lamont-granquist))
+- Link to the knife docs in both places where we error on editor [#6363](https://github.com/chef/chef/pull/6363) ([tas50](https://github.com/tas50))
+- Bump rubygems to 2.6.13 [#6365](https://github.com/chef/chef/pull/6365) ([lamont-granquist](https://github.com/lamont-granquist))
+- Ship chef-vault in the omnibus package [#6370](https://github.com/chef/chef/pull/6370) ([thommay](https://github.com/thommay))
+- Support an array of keys for apt_repository [#6372](https://github.com/chef/chef/pull/6372) ([gsreynolds](https://github.com/gsreynolds))
+- Immutablize properly as we deep merge [#6362](https://github.com/chef/chef/pull/6362) ([lamont-granquist](https://github.com/lamont-granquist))
+- Alternate user local logon authentication for remote_file resource [#5832](https://github.com/chef/chef/pull/5832) ([NimishaS](https://github.com/NimishaS))
+- Add support for specifying ETHTOOL_OPTS in the ifconfig resource [#6384](https://github.com/chef/chef/pull/6384) ([tomdoherty](https://github.com/tomdoherty))
+
+## [v13.3.42](https://github.com/chef/chef/tree/v13.3.42) (2017-08-16)
+
+#### Merged Pull Requests
+- Apt: Add apt_preference resource from apt cookbooks [#5529](https://github.com/chef/chef/pull/5529) ([tas50](https://github.com/tas50))
+- Fix typos [#6298](https://github.com/chef/chef/pull/6298) ([akitada](https://github.com/akitada))
+- Set explicit page size for every search request [#6299](https://github.com/chef/chef/pull/6299) ([stevendanna](https://github.com/stevendanna))
+- Add .dockerignore to reduce size of resulting images [#6296](https://github.com/chef/chef/pull/6296) ([tduffield](https://github.com/tduffield))
+- Fix git command in DCO sign-off example [#6306](https://github.com/chef/chef/pull/6306) ([edmorley](https://github.com/edmorley))
+- Add option to enable unprivileged symlink creation on windows [#6236](https://github.com/chef/chef/pull/6236) ([svmastersamurai](https://github.com/svmastersamurai))
+- Bump omnibus-software version [#6310](https://github.com/chef/chef/pull/6310) ([thommay](https://github.com/thommay))
+- Throw readable errors if multiple dsc resources are found [#6307](https://github.com/chef/chef/pull/6307) ([Happycoil](https://github.com/Happycoil))
+- Add zypper_repository resource [#5948](https://github.com/chef/chef/pull/5948) ([tas50](https://github.com/tas50))
+- Pull in Ohai 13.3 [#6319](https://github.com/chef/chef/pull/6319) ([tas50](https://github.com/tas50))
+- Maintain compat with old zypper_repo resource used in cookbooks [#6318](https://github.com/chef/chef/pull/6318) ([tas50](https://github.com/tas50))
+- README improvement for Chef beginner. [#6297](https://github.com/chef/chef/pull/6297) ([takaya-fuj19](https://github.com/takaya-fuj19))
+- Bump InSpec to v1.33.1 [#6324](https://github.com/chef/chef/pull/6324) ([adamleff](https://github.com/adamleff))
+
+
+## [v13.3.27](https://github.com/chef/chef/tree/v13.3.27) (2017-07-26)
+[Full Changelog](https://github.com/chef/chef/compare/v13.0.118...v13.3.27)
+
+- Added username/password validation for elevated option [\#6293](https://github.com/chef/chef/pull/6293) ([NimishaS](https://github.com/NimishaS))
+- Bump mixlib-shellout for \#6271 [\#6285](https://github.com/chef/chef/pull/6285) ([btm](https://github.com/btm))
+- Added :elevated option for powershell\_script resource [\#6271](https://github.com/chef/chef/pull/6271) ([NimishaS](https://github.com/NimishaS))
+- Make mount idempotent on Aix [\#6213](https://github.com/chef/chef/pull/6213) ([NAshwini](https://github.com/NAshwini))
+- Allow windows\_task create action to update tasks. [\#6193](https://github.com/chef/chef/pull/6193) ([harikesh-kolekar](https://github.com/harikesh-kolekar))
+- Use socketless local mode by default [\#6177](https://github.com/chef/chef/pull/6177) ([coderanger](https://github.com/coderanger))
+- Convert breakpoint resource to a custom resource [\#6176](https://github.com/chef/chef/pull/6176) ([lamont-granquist](https://github.com/lamont-granquist))
+- Make non-legacy solo use socketless mode [\#6174](https://github.com/chef/chef/pull/6174) ([coderanger](https://github.com/coderanger))
+- Prefer Systemd with sysvinit script over Upstart for service provider [\#6157](https://github.com/chef/chef/pull/6157) ([shortdudey123](https://github.com/shortdudey123))
+
+## v13.0.118 (2017-04-12)
+
+- Fix Gems won't install on Windows with Chef 13
+- Fix yum_package options option broken in Chef 13
+- Fix cookbooks uploaded by Chef 13 can't be used by Chef 12
+- Update Ohai to 13.0.1 to fix the OpenStack and Eucalyptus plugins
+
+## v13.0.113 (2017-04-06)
+
+- Use Ohai 13.0
+- Add new server enforced required recipe feature
+- shell_out PATH fixes and path_sanity changes
+- Remove magic from the logger/formatter settings
+- Add new windows_task resource
+- Better solution to gem_package source issues
+- Remove the knife cookbook create command in favor of Chef-DK
+- Remove need to define use_inline_resources and always enable inline resources
+- RFC 59 - Load ohai plugins
+- Use new lzma lib
+- Have knife cookbook generate use SPDX standard license strings
+- Implement RFC033: Root aliases
+- Ensure DataBagItems are a Mash
+- Add InSpec to chef omnibus builds
+- Remove knife cookbook site vendor
+- Make Standardized Exit Codes The Default Behavior
+- Tweaks to rubygems source option for urls
+- Allow lazy{} to be used in template resource variables.
+- Freeze property defaults
+- fix knife ssh --exit-on-error
+- Add -u param to usermod in linux_user resource when using non_unique
+- Launchd limit_load_to_session_type accepts Array or String
+- Remove the consts for DSL-based resources/providers
+- Add real support for rb files (at least roles) in knife-serve
+- Adding restart action to launchd resource
+- systemd_unit verifier escape hatch
+- Ensure we check all required fields
+- V2 Cookbook Manifests
+- Fix and simplify rake bundle:* commands
+- Expand the system info displayed on error to give us some more data to work with when helping users
+- Add policy_name and policy_group indexes to converge message sent to ...
+- Turn on zypper gpg checks by default
+- Knife search exit 1 when no results
+- Remove deprecated knife subcommand APIs
+- Coerce package options property to an Array
+- Fix cookbook gem installer
+- Remove iconv from the chef build
+- Remove deprecated Chef::ResourceResolver.resource API
+- Fix notifying array resources
+- Remove deprecated run_command API entirely
+- Apply knife search node fuzzifier to knife ssh
+- Remove Chef::Resource.updated=
+- Remove deprecated launchd resource hash property
+- Remove more deprecated method_missing access
+- Support nameless resources and remove deprecated multi-arg resources
+- bumping acceptance gems
+- Set default guard_interpreter to powershell_script on Windows
+- Remove more deprecated provider_resolver code
+- Make ActionClass a class
+- Don't include nokogiri gem as it doesn't compile on Windows right now
+- Retry API requests if using an unsupported version
+- Bump _XOPEN_SOURCE to 600 for ruby 2.4 on Solaris
+- Upgrade Ruby to the 2.4.1 release
+- Fix action class weirdness in Chef-13
+- Make ResourceReporter smarter to get resource identity and state
+- Don't `rescue Exception` in retryable resources
+- Simplify DSL creation
+- Remove deprecated Chef::Client attrs
+- Remove method_missing from the DSL
+- Remove support for the sort option to searches.
+- smf_recursive_dependencies: Allow solaris services to start recursively.
+- Fix for creating users in non english windows machines
+- Remove node_map back-compat
+- Fix chef-shell option name and help message
+- Remove Chef::ShellOut
+- Remove deprecated run_context methods
+- Remove old platform mapping code
+- Remove the old rake tasks
+- Properly use chef-shell in SoloSession by deprecating old behavior into SoloLegacySession
+- Raise on properties redefining inherited methods
+- Optimize cheffs
+- Remove Chef::REST
+- Fix node#debug_value access through arrays
+- Nillable properties
+- Freeze merged node attribute
+- Properly deep dup Node#to_hash
+- Add release policy badge to README
+- Remove the deprecated easy_install resource
+- Remove declare_resource create_if_missing API
+- Kill JSON auto inflate with fire
+- Remove method_missing access to node object.
+- Cleanup of Chef::Resource
+- Add attribute blacklist
+- Enable why-run by default in resources
+- Ensure that there are no pesky // in our paths
+- Compress debs and rpms with xz
+- Fix apt_repository for Ubuntu 16.10+
+- Remove all Chef 11 era deprecations
+- Remove partial_search methods
+- Use v3 data bag encryption
+- Remove %{file} from verify interpolation
+- Revert "Remove all 11 era deprecations"
+- Convert additional resource methods to properties
+- Remove backcompat classes
+- Remove provisioning from the downstream tests
+- Remove supports API from Chef::Resource
+- Be a bit less keen to help properties
+- Add an option for gateway_identity_file that will allow key-based authentication on the gateway.
+- Mac: Validate that a machine has a computer level profile
+- Verify data bag exists before trying to create it in knife
+- Remove resource cloning and 3694 warnings
+- HTTP: add debug long for non-JSON response
+
+
+## [v12.21.4](https://github.com/chef/chef/tree/v12.21.4) (2017-08-14)
+[Full Changelog](https://github.com/chef/chef/compare/v12.21.3...v12.21.4)
+
+**Fixed bugs:**
+- Backport #5941 (Make ResourceReporter smarter to get resource identity and state) [\#6308](https://github.com/chef/chef/pull/6308)
+
+**Tech cleanup:**
+- Bump omnibus-software to fix early Rubygems segfaults on Windows [\#6329](https://github.com/chef/chef/pull/6329)
+- Upgrade Ruby from 2.3.1 to 2.3.4
+- Upgrade libiconv from 1.14 to 1.15
+- Upgrade Rubygems from 2.6.10 to 2.6.12
+
+## [v12.21.3](https://github.com/chef/chef/tree/v12.21.3) (2017-06-23)
+[Full Changelog](https://github.com/chef/chef/compare/v12.21.1...v12.21.3)
+
+**Fixed bugs:**
+- Properly send expanded run list event for policy file nodes [\#6229](https://github.com/chef/chef/pull/6229) / [\#6233](https://github.com/chef/chef/pull/6233)
+
+## [v12.21.1](https://github.com/chef/chef/tree/v12.21.1) (2017-06-20)
+[Full Changelog](https://github.com/chef/chef/compare/v12.21.0...v12.21.1)
+
+- Handle the supports pseudo-property more gracefully [\#6222](https://github.com/chef/chef/pull/6222) ([coderanger](https://github.com/coderanger))
+- Provide better system information when Chef crashes [\#6173](https://github.com/chef/chef/pull/6173) ([coderanger](https://github.com/coderanger))
+- On Debian based systems, correctly prefer Systemd to Upstart [\#6157](https://github.com/chef/chef/pull/6157) ([shortdudey123](https://github.com/shortdudey123))
+- Don't crash if we downgrade from Chef 13 to Chef 12 [\#6129](https://github.com/chef/chef/pull/6129) ([akitada](https://github.com/akitada))
+- Update zlib to 1.2.11 to resolve CVEs [#6219](https://github.com/chef/chef/pull/6219) ([thommay](https://github.com/thommay))
+- Update Ohai to 8.24 with improved EC2 metadata handling, dmi code fixes, and scala/lua detection fixes
+
+## v12.20.3 (2017-04-30)
+
+- Add the ability to define a server enforced required recipe[#6032](https://github.com/chef/chef/pull/6032)([sdelano](https://github.com/sdelano))
+- Fix apt_repository key fingerprint for Ubuntu 16.10+
+- Verify if a databag exists before we try to create it with knife
+- Bump json, winrm, plist, and net-ssh gems to the latest [#6106](https://github.com/chef/chef/pull/6106) ([rhass](https://github.com/rhass))
+
+## v12.19.36 (2017-02-23)
+
+- Use shellsplit for apt_package options [#5838](https://github.com/chef/chef/pull/5838) ([mivok](https://github.com/mivok))
+
+## [v12.19.33](https://github.com/chef/chef/tree/v12.19.33) (2017-02-16)
+[Full Changelog](https://github.com/chef/chef/compare/v12.18.31...v12.19.33)
+
+- coerce immutable arrays to normal arrays in the yum\_package resource [\#5816](https://github.com/chef/chef/pull/5816) ([lamont-granquist](https://github.com/lamont-granquist))
+- Suppress sensitive properties from resource log and reporting output [\#5803](https://github.com/chef/chef/pull/5803) ([tduffield](https://github.com/tduffield))
+- Sanitize UTF-8 data sent to Data Collector [\#5793](https://github.com/chef/chef/pull/5793) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add multipackage\_api support to yum\_package provider [\#5791](https://github.com/chef/chef/pull/5791) ([tduffield](https://github.com/tduffield))
+- rhel7 / dnf 2.0 fixes / improved errors [\#5782](https://github.com/chef/chef/pull/5782) ([lamont-granquist](https://github.com/lamont-granquist))
+- Grant Administrators group permissions to nodes directory under chef-solo [\#5781](https://github.com/chef/chef/pull/5781) ([tduffield](https://github.com/tduffield))
+- Fix --no-fips on chef-client [\#5778](https://github.com/chef/chef/pull/5778) ([btm](https://github.com/btm))
+- Raise error if ips\_package install returns non-zero [\#5773](https://github.com/chef/chef/pull/5773) ([tduffield](https://github.com/tduffield))
+- Use CIDR notation rather than netmask in route-eth0 file [\#5772](https://github.com/chef/chef/pull/5772) ([tduffield](https://github.com/tduffield))
+- Verify systemd\_unit file with custom verifier [\#5765](https://github.com/chef/chef/pull/5765) ([mal](https://github.com/mal))
+- Windows alternate user support for execute resources [\#5764](https://github.com/chef/chef/pull/5764) ([NimishaS](https://github.com/NimishaS))
+- favor metadata.json over metadata.rb [\#5750](https://github.com/chef/chef/pull/5750) ([lamont-granquist](https://github.com/lamont-granquist))
+- Ensure ssh search paginates correctly [\#5744](https://github.com/chef/chef/pull/5744) ([thommay](https://github.com/thommay))
+- Do not modify File's new\_resource during why-run [\#5742](https://github.com/chef/chef/pull/5742) ([scottopherson](https://github.com/scottopherson))
+- Add gems for ECC algorithm support to omnibus. [\#5736](https://github.com/chef/chef/pull/5736) ([rhass](https://github.com/rhass))
+- dh/url support cab [\#5732](https://github.com/chef/chef/pull/5732) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- use git archive to speed up putting source in place [\#5730](https://github.com/chef/chef/pull/5730) ([robbkidd](https://github.com/robbkidd))
+- use pkg.path variable to reference path to self [\#5729](https://github.com/chef/chef/pull/5729) ([robbkidd](https://github.com/robbkidd))
+- Raise NamedSecurityInfo related exception using HR result. [\#5727](https://github.com/chef/chef/pull/5727) ([Aliasgar16](https://github.com/Aliasgar16))
+- Core: Ensure paths are correctly escaped when syntax checking [\#5704](https://github.com/chef/chef/pull/5704) ([ceneo](https://github.com/ceneo))
+- Added module\_version attribute for dsc\_resource for SxS support [\#5701](https://github.com/chef/chef/pull/5701) ([Aliasgar16](https://github.com/Aliasgar16))
+- Bump net-ssh to v4, add dependencies for ed25519 support [\#5687](https://github.com/chef/chef/pull/5687) ([onlyhavecans](https://github.com/onlyhavecans))
+
+## [v12.18.31](https://github.com/chef/chef/tree/v12.18.31) (2017-01-11)
+[Full Changelog](https://github.com/chef/chef/compare/v12.17.44...v12.18.31)
+
+**Implemented enhancements:**
+
+- yum\_repository: Allow baseurl to be an array & allow fastestmirror\_enabled false [\#5708](https://github.com/chef/chef/pull/5708) ([tas50](https://github.com/tas50))
+- Adding returns property to chocolatey\_package resource [\#5688](https://github.com/chef/chef/pull/5688) ([Vasu1105](https://github.com/Vasu1105))
+- Code cleanup in the user provider [\#5674](https://github.com/chef/chef/pull/5674) ([lamont-granquist](https://github.com/lamont-granquist))
+- Code cleanup in the group provider [\#5673](https://github.com/chef/chef/pull/5673) ([lamont-granquist](https://github.com/lamont-granquist))
+- Core: Formally deprecate run\_command [\#5666](https://github.com/chef/chef/pull/5666) ([lamont-granquist](https://github.com/lamont-granquist))
+- Set MSI Scheduled Task name to match chef-client cookbook managed name [\#5657](https://github.com/chef/chef/pull/5657) ([mwrock](https://github.com/mwrock))
+- remove Chef::Platform::HandlerMap [\#5636](https://github.com/chef/chef/pull/5636) ([lamont-granquist](https://github.com/lamont-granquist))
+- Core: Properly deprecate old Chef::Platform methods [\#5631](https://github.com/chef/chef/pull/5631) ([lamont-granquist](https://github.com/lamont-granquist))
+
+**Fixed bugs:**
+
+- Fix error thrown by solo when run on Windows as SYSTEM [\#5693](https://github.com/chef/chef/pull/5693) ([scottopherson](https://github.com/scottopherson))
+- Report a blank resource if sensitive is enabled [\#5668](https://github.com/chef/chef/pull/5668) ([afiune](https://github.com/afiune))
+- Ensure node.docker? returns boolean [\#5645](https://github.com/chef/chef/pull/5645) ([andrewjamesbrown](https://github.com/andrewjamesbrown))
+- Fix Data Collector organization parsing regex [\#5630](https://github.com/chef/chef/pull/5630) ([adamleff](https://github.com/adamleff))
+- Core: Use object ID when detected unprocessed Resources [\#5604](https://github.com/chef/chef/pull/5604) ([adamleff](https://github.com/adamleff))
+
+**Merged pull requests:**
+
+- Core: fix node attribute "unless" API methods [\#5717](https://github.com/chef/chef/pull/5717) ([lamont-granquist](https://github.com/lamont-granquist))
+
+## [v12.17.44](https://github.com/chef/chef/tree/v12.17.44) (2016-12-07)
+[Full Changelog](https://github.com/chef/chef/compare/v12.16.42...v12.17.44)
+
+**Implemented enhancements:**
+
+- Action :umount for mount resource is an obtuse anachronism [\#5595](https://github.com/chef/chef/issues/5595)
+- Core: Update ohai resource to new style, stop overwriting name property [\#5607](https://github.com/chef/chef/pull/5607) ([adamleff](https://github.com/adamleff))
+- Linux: mount provider - skip device detection for zfs [\#5603](https://github.com/chef/chef/pull/5603) ([ttr](https://github.com/ttr))
+- Core: Ensure chef-solo creates node files w/ correct permissions [\#5601](https://github.com/chef/chef/pull/5601) ([scottopherson](https://github.com/scottopherson))
+- Resources: Add unmount as an alias to umount in the mount resource [\#5599](https://github.com/chef/chef/pull/5599) ([shortdudey123](https://github.com/shortdudey123))
+- Core: Update Data Collector to use Chef::JSONCompat [\#5590](https://github.com/chef/chef/pull/5590) ([adamleff](https://github.com/adamleff))
+- Knife: Add ability to pass multiple nodes to knife node/client delete [\#5572](https://github.com/chef/chef/pull/5572) ([jeunito](https://github.com/jeunito))
+- Core: Data Collector debug log should output JSON [\#5570](https://github.com/chef/chef/pull/5570) ([adamleff](https://github.com/adamleff))
+- Yum: Purge yum cache before deleting repo config [\#5509](https://github.com/chef/chef/pull/5509) ([iancward](https://github.com/iancward))
+- Knife Bootstrap: Passing config\_log\_level and config\_log\_location from config.rb [\#5502](https://github.com/chef/chef/pull/5502) ([dheerajd-msys](https://github.com/dheerajd-msys))
+
+**Fixed bugs:**
+
+- Custom Resources: Undefined method up\_to\_date thrown by Chef 12.16.42 [\#5593](https://github.com/chef/chef/issues/5593)
+- Core: Ensure deprecation messages are always included [\#5618](https://github.com/chef/chef/pull/5618) ([thommay](https://github.com/thommay))
+- Core: Fix bug where Access Controls on existing symlink resources would be ignored on first chef-client run [\#5616](https://github.com/chef/chef/pull/5616) ([tduffield](https://github.com/tduffield))
+- The suggested fix for the manage\_home deprecation is incorrect [\#5615](https://github.com/chef/chef/pull/5615) ([tas50](https://github.com/tas50))
+- change choco -version to choco --version [\#5613](https://github.com/chef/chef/pull/5613) ([spuder](https://github.com/spuder))
+- Knife: Correct example `chef\_server\_url` in `knife configure` [\#5602](https://github.com/chef/chef/pull/5602) ([jerryaldrichiii](https://github.com/jerryaldrichiii))
+- Windows: Ensure correct version of shutdown is called when using the reboot resource [\#5596](https://github.com/chef/chef/pull/5596) ([Xoph](https://github.com/Xoph))
+- Windows: Support for running cab\_package on non-English system locales [\#5591](https://github.com/chef/chef/pull/5591) ([jugatsu](https://github.com/jugatsu))
+- Core: Ensure Data Collector resource report exists before updating [\#5571](https://github.com/chef/chef/pull/5571) ([adamleff](https://github.com/adamleff))
+- Windows: Use the full path to expand.exe for msu\_package [\#5564](https://github.com/chef/chef/pull/5564) ([NimishaS](https://github.com/NimishaS))
+- Unset http\[s\]\_proxy in the subversion spec [\#5562](https://github.com/chef/chef/pull/5562) ([stefanor](https://github.com/stefanor))
+- Core: fix Lint/UnifiedInteger cop [\#5547](https://github.com/chef/chef/pull/5547) ([lamont-granquist](https://github.com/lamont-granquist))
+- Core: fix ImmutableArray slices [\#5541](https://github.com/chef/chef/pull/5541) ([lamont-granquist](https://github.com/lamont-granquist))
+- Prevent apt\_update failures on non-Linux platforms [\#5524](https://github.com/chef/chef/pull/5524) ([tas50](https://github.com/tas50))
+- Core: Ensure that the sensitive property is correctly accessed [\#5508](https://github.com/chef/chef/pull/5508) ([axos88](https://github.com/axos88))
+
+**Closed issues:**
+
+- cab\_package doesn't support running on non-English system locales [\#5592](https://github.com/chef/chef/issues/5592)
+- Support restarting/stopping/ the service from state paused on windows [\#5586](https://github.com/chef/chef/issues/5586)
+
+## [v12.16.42](https://github.com/chef/chef/tree/v12.16.42) (2016-11-04)
+[Full Changelog](https://github.com/chef/chef/compare/v12.15.19...v12.16.42)
+
+**Implemented enhancements:**
+
+- Core: improve readability of property-resource namespace collision exception message [\#5500](https://github.com/chef/chef/pull/5500) ([lamont-granquist](https://github.com/lamont-granquist))
+- Omnibus: Pull in Ohai 8.21.0 and other new deps [\#5499](https://github.com/chef/chef/pull/5499) ([tas50](https://github.com/tas50))
+- Core: Add deprecations to Data Collector run completion messages [\#5496](https://github.com/chef/chef/pull/5496) ([adamleff](https://github.com/adamleff))
+- Core: add attribute\_changed hook to event handlers [\#5495](https://github.com/chef/chef/pull/5495) ([lamont-granquist](https://github.com/lamont-granquist))
+- Knife: Add the `--field-separator` flag to knife show commands [\#5489](https://github.com/chef/chef/pull/5489) ([tduffield](https://github.com/tduffield))
+- Core: Enable Signed Header Auth for Data Collector, and Configure the Data Collector Automatically [\#5487](https://github.com/chef/chef/pull/5487) ([danielsdeleo](https://github.com/danielsdeleo))
+- Core: set use\_inline\_resources in package superclass [\#5483](https://github.com/chef/chef/pull/5483) ([lamont-granquist](https://github.com/lamont-granquist))
+- Package: Add new "lock" action for apt, yum and zypper packages [\#5395](https://github.com/chef/chef/pull/5395) ([yeoldegrove](https://github.com/yeoldegrove))
+
+**Fixed bugs:**
+
+- Enable data collector w/o token for solo, but require explicit URL [\#5511](https://github.com/chef/chef/pull/5511) ([danielsdeleo](https://github.com/danielsdeleo))
+- Core: Include chef/chef\_class in Chef::REST for method log\_deprecation [\#5504](https://github.com/chef/chef/pull/5504) ([smalltown](https://github.com/smalltown))
+- Knife: Updating knife ssl fetch to correctly store certificate when it does not have a CN [\#5498](https://github.com/chef/chef/pull/5498) ([tyler-ball](https://github.com/tyler-ball))
+- Knife: Fixed knife download cookbooks issue which used to corrupt the certificate files each time the command was fired. [\#5494](https://github.com/chef/chef/pull/5494) ([Aliasgar16](https://github.com/Aliasgar16))
+- Solaris: Properly check lock status of users on solaris2 [\#5486](https://github.com/chef/chef/pull/5486) ([tduffield](https://github.com/tduffield))
+- Solaris: Fix IPS package must create symlinks to package commands [\#5485](https://github.com/chef/chef/pull/5485) ([jaymalasinha](https://github.com/jaymalasinha))
+
+## [v12.15.19](https://github.com/chef/chef/tree/v12.15.19) (2016-10-07)
+[Full Changelog](https://github.com/chef/chef/compare/v12.14.89...v12.15.19)
+
+**Enhancements:**
+
+- Adding support for rfc 62 exit code 213 (Chef upgrades) [\#5428](https://github.com/chef/chef/pull/5428) ([jeremymv2](https://github.com/jeremymv2))
+- Allow raw_key to override the configured signing\_key [\#5314](https://github.com/chef/chef/pull/5314) ([thommay](https://github.com/thommay))
+- Set yum\_repository gpgcheck default to true [\#5398](https://github.com/chef/chef/pull/5398) ([shortdudey123](https://github.com/shortdudey123))
+- Allow deletion of registry\_key without the need for users to pass data key in values hash. [\#5359](https://github.com/chef/chef/pull/5359) ([Aliasgar16](https://github.com/Aliasgar16))
+- Adding support for cab files to Chef package provider on windows [\#5285](https://github.com/chef/chef/pull/5285) ([Vasu1105](https://github.com/Vasu1105))
+- Ignore unknown metadata fields in metadata.rb [\#5299](https://github.com/chef/chef/pull/5299) ([lamont-granquist](https://github.com/lamont-granquist))
+
+**Fixed Bugs:**
+
+- knife ssh: use the command line prompt for sudo if set [\#5427](https://github.com/chef/chef/pull/5427) ([lamont-granquist](https://github.com/lamont-granquist))
+- User provider: Fix manage\_home provider inconsistency for Mac and FreeBSD providers [\#5423](https://github.com/chef/chef/pull/5423) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix for "Chefspec template rendering fails when cookbook\_name != directory name" [\#5417](https://github.com/chef/chef/pull/5417) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix solaris handling for useradd -m/-M behavior [\#5408](https://github.com/chef/chef/pull/5408) ([coderanger](https://github.com/coderanger))
+- Normalize full key name to avoid resource update on identical key names [\#5290](https://github.com/chef/chef/pull/5290) ([bai](https://github.com/bai))
+- Add trailing newline to generated 15update-stamp [\#5382](https://github.com/chef/chef/pull/5382) ([pwalz](https://github.com/pwalz))
+- Invalid `dsc_scripts` should fail the run [\#5377](https://github.com/chef/chef/pull/5377) ([NimishaS](https://github.com/NimishaS))
+- Revert --local filter for gems installed from paths [\#5379](https://github.com/chef/chef/pull/5379) ([mwrock](https://github.com/mwrock))
+- Fix knife list\_commands\(\) [\#5386](https://github.com/chef/chef/pull/5386) ([lamont-granquist](https://github.com/lamont-granquist))
+- Don't use -r for users or groups on Solaris. [\#5355](https://github.com/chef/chef/pull/5355) ([coderanger](https://github.com/coderanger))
+- Chef 12 Attribute Regression [\#5360](https://github.com/chef/chef/pull/5360) ([gbagnoli](https://github.com/gbagnoli))
+- Handling Errno::ETIMEDOUT [\#5358](https://github.com/chef/chef/pull/5358) ([NimishaS](https://github.com/NimishaS))
+
+## [v12.14.89](https://github.com/chef/chef/tree/v12.14.89) (2016-09-22)
+[Full Changelog](https://github.com/chef/chef/compare/v12.14.77...v12.14.89)
+
+**Fixed Bugs:**
+
+- Revert "Verify systemd\_unit file during create" [\#5326](https://github.com/chef/chef/pull/5326) ([mwrock](https://github.com/mwrock))
+- Fix method\_access and array handling in node presenter [\#5351](https://github.com/chef/chef/pull/5351) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fixed undefined short\_cksum method issue and checksum in uppercase issue for windows\_package resource. [\#5332](https://github.com/chef/chef/pull/5332) ([Aliasgar16](https://github.com/Aliasgar16))
+- Fix makecache action name in yum\_repository [\#5348](https://github.com/chef/chef/pull/5348) ([tas50](https://github.com/tas50))
+
+## [v12.14.77](https://github.com/chef/chef/tree/v12.14.77) (2016-09-19)
+[Full Changelog](https://github.com/chef/chef/compare/v12.14.60...v12.14.77)
+
+**Fixed Bugs:**
+
+- Revert supports\[:manage\_home\] behavior [\#5322](https://github.com/chef/chef/pull/5322) ([lamont-granquist](https://github.com/lamont-granquist))
+- Preserve the extension of the file in the rendered tempfile in File providers [\#5327](https://github.com/chef/chef/pull/5327) ([lamont-granquist](https://github.com/lamont-granquist))
+- Allow the :delete action for yum\_repository + fix old property support [\#5320](https://github.com/chef/chef/pull/5320) ([tas50](https://github.com/tas50))
+
+## [v12.14.60](https://github.com/chef/chef/tree/v12.14.60) (2016-09-09)
+[Full Changelog](https://github.com/chef/chef/compare/v12.13.37...v12.14.60)
+
+**Enhancements:**
+
+- Only support Solaris 10u11 and newer [\#5264](https://github.com/chef/chef/pull/5264) ([rhass](https://github.com/rhass))
+- Added code to handle deletion of directories on Windows that are symlinks. [\#5234](https://github.com/chef/chef/pull/5234) ([Aliasgar16](https://github.com/Aliasgar16))
+- Readability improvements to options parsing code [\#5289](https://github.com/chef/chef/pull/5289) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add Hash type to launchd:keep\_alive [\#5182](https://github.com/chef/chef/pull/5182) ([erikng](https://github.com/erikng))
+- Added timeout during removing of windows package [\#5250](https://github.com/chef/chef/pull/5250) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Bump openssl to 1.0.2h [\#5260](https://github.com/chef/chef/pull/5260) ([lamont-granquist](https://github.com/lamont-granquist))
+- Rewrite linux\_user provider check\_lock [\#5248](https://github.com/chef/chef/pull/5248) ([lamont-granquist](https://github.com/lamont-granquist))
+- Allow flagging a resource property as sensitive [\#5185](https://github.com/chef/chef/pull/5185) ([adamleff](https://github.com/adamleff))
+- Rewrite linux useradd provider [\#5243](https://github.com/chef/chef/pull/5243) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add yum_repository resource from the yum cookbook [\#5187](https://github.com/chef/chef/pull/5187) ([thommay](https://github.com/thommay))
+- Verify systemd\_unit file during create [\#5210](https://github.com/chef/chef/pull/5210) ([mal](https://github.com/mal))
+- Add a warning for guard blocks that return a non-empty string [\#5233](https://github.com/chef/chef/pull/5233) ([coderanger](https://github.com/coderanger))
+- Forward package cookbook\_name to underlying remote\_file [\#5128](https://github.com/chef/chef/pull/5128) ([Annih](https://github.com/Annih))
+- Fix "URI.escape is obsolete" warnings [\#5230](https://github.com/chef/chef/pull/5230) ([jkeiser](https://github.com/jkeiser))
+- Remove ruby 2.1 support [\#5220](https://github.com/chef/chef/pull/5220) ([lamont-granquist](https://github.com/lamont-granquist))
+- User provider manage\_home behavior and refactor [\#5122](https://github.com/chef/chef/pull/5122) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix Style/BlockDelimiters, Style/MultilineBlockLayout and 0.42.0 engine upgrade [\#5218](https://github.com/chef/chef/pull/5218) ([lamont-granquist](https://github.com/lamont-granquist))
+- Switch from Ruby 2.1.9 to Ruby 2.3.1 [\#5190](https://github.com/chef/chef/pull/5190) ([jkeiser](https://github.com/jkeiser))
+- Update to latest chefstyle [\#5217](https://github.com/chef/chef/pull/5217) ([jkeiser](https://github.com/jkeiser))
+- Rubygems memory performance improvement [\#5203](https://github.com/chef/chef/pull/5203) ([lamont-granquist](https://github.com/lamont-granquist))
+- HTTP 1.1 keepalives for cookbook synchronization [\#5151](https://github.com/chef/chef/pull/5151) ([lamont-granquist](https://github.com/lamont-granquist))
+
+**Fixed Bugs:**
+
+- Fixes GH-4955, allowing local gems with remote dependencies [\#5098](https://github.com/chef/chef/pull/5098) ([jyaworski](https://github.com/jyaworski))
+- Hook up the recipe\_file\_loaded event which was defined but not actually called [\#5281](https://github.com/chef/chef/pull/5281) ([coderanger](https://github.com/coderanger))
+- fix gem\_package regression in master [\#5262](https://github.com/chef/chef/pull/5262) ([lamont-granquist](https://github.com/lamont-granquist))
+- Added fix for spaces in profile identifiers [\#5159](https://github.com/chef/chef/pull/5159) ([natewalck](https://github.com/natewalck))
+- Add a hook for compat\_resource [\#5259](https://github.com/chef/chef/pull/5259) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix flush\_cache issues in yum\_package [\#5258](https://github.com/chef/chef/pull/5258) ([jaymzh](https://github.com/jaymzh))
+- Use symbols instead of strings as keys for systemd user property [\#5241](https://github.com/chef/chef/pull/5241) ([joshuamiller01](https://github.com/joshuamiller01))
+- Use upstart goal state as service status [\#5249](https://github.com/chef/chef/pull/5249) ([evan2645](https://github.com/evan2645))
+- Fix the useradd test filters [\#5236](https://github.com/chef/chef/pull/5236) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix specify members of group on suse/openbsd/solaris2/hpux [\#5152](https://github.com/chef/chef/pull/5152) ([tas50](https://github.com/tas50))
+- Fix cookbook upload of symlinked cookbooks in Ruby 2.3 on Windows [\#5216](https://github.com/chef/chef/pull/5216) ([jkeiser](https://github.com/jkeiser))
+- Don't use relative\_path\_from on glob results [\#5215](https://github.com/chef/chef/pull/5215) ([jkeiser](https://github.com/jkeiser))
+
+## [v12.13.37](https://github.com/chef/chef/tree/v12.13.37) (2016-08-12)
+[Full Changelog](https://github.com/chef/chef/compare/v12.13.30...v12.13.37)
+
+**Enhancements:**
+
+- Bumping ohai and mixlib-log to fix regression [\#5197](https://github.com/chef/chef/pull/5197) ([mwrock](https://github.com/mwrock))
+- Remove requires in Chef::Recipe that are no longer necessary [\#5189](https://github.com/chef/chef/pull/5189) ([lamont-granquist](https://github.com/lamont-granquist))
+
+## [v12.13.30](https://github.com/chef/chef/tree/v12.13.30) (2016-08-05)
+[Full Changelog](https://github.com/chef/chef/compare/v12.12.15...v12.13.30)
+
+**Enhancements:**
+
+- noop apt_update similar to apt_repository [\#5173](https://github.com/chef/chef/pull/5173) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump dependencies to bring in Ohai 8.18 [\#5168](https://github.com/chef/chef/pull/5168) ([tas50](https://github.com/tas50))
+- Make Chef work with Ruby 2.3, update Ruby to 2.1.9 [\#5165](https://github.com/chef/chef/pull/5165) ([jkeiser](https://github.com/jkeiser))
+- Log cause chain for exceptions [\#3354](https://github.com/chef/chef/pull/3354) ([jaym](https://github.com/jaym))
+- First pass on --config-option handling. [\#5045](https://github.com/chef/chef/pull/5045) ([coderanger](https://github.com/coderanger))
+- Add bootstrap proxy authentication support. [\#4059](https://github.com/chef/chef/pull/4059) ([yossigo](https://github.com/yossigo))
+- Support setting an empty string for cron attrs [\#5127](https://github.com/chef/chef/pull/5127) ([thommay](https://github.com/thommay))
+- Also clear notifications when deleting a resource. [\#5146](https://github.com/chef/chef/pull/5146) ([coderanger](https://github.com/coderanger))
+- Clean up subscribes internals and notification storage. [\#5145](https://github.com/chef/chef/pull/5145) ([coderanger](https://github.com/coderanger))
+- Cache ChefFS children [\#5131](https://github.com/chef/chef/pull/5131) ([thommay](https://github.com/thommay))
+- Update to rspec 3.5 [\#5126](https://github.com/chef/chef/pull/5126) ([thommay](https://github.com/thommay))
+- Add `chef\_data\_bag\_item` to Cheffish DSL methods [\#5125](https://github.com/chef/chef/pull/5125) ([danielsdeleo](https://github.com/danielsdeleo))
+- replace glibc resolver with ruby resolver [\#5123](https://github.com/chef/chef/pull/5123) ([lamont-granquist](https://github.com/lamont-granquist))
+- The user must specify a category for a new cookbook [\#5091](https://github.com/chef/chef/pull/5091) ([thommay](https://github.com/thommay))
+- Warn if not installing an individual bff fileset [\#5093](https://github.com/chef/chef/pull/5093) ([mwrock](https://github.com/mwrock))
+- Use Mixlib::Archive to extract tarballs [\#5080](https://github.com/chef/chef/pull/5080) ([thommay](https://github.com/thommay))
+- Data Collector server URL validation, and disable on host down [\#5076](https://github.com/chef/chef/pull/5076) ([adamleff](https://github.com/adamleff))
+
+**Fixed Bugs:**
+
+- Don't log error for reporting audit data in when in chef-zero [\#5016](https://github.com/chef/chef/pull/5016) ([erichelgeson](https://github.com/erichelgeson))
+- Invalidate the file system cache on deletion [\#5154](https://github.com/chef/chef/pull/5154) ([thommay](https://github.com/thommay))
+- Root ACLs are a top level json file not a sub-directory [\#5155](https://github.com/chef/chef/pull/5155) ([thommay](https://github.com/thommay))
+- Install nokogiri and pin mixlib-cli [\#5118](https://github.com/chef/chef/pull/5118) ([ksubrama](https://github.com/ksubrama))
+- Ensure that the valid option is given back to the option parser [\#5114](https://github.com/chef/chef/pull/5114) ([dldinternet](https://github.com/dldinternet))
+- Fixed regex for zypper version 1.13.\*. [\#5109](https://github.com/chef/chef/pull/5109) ([yeoldegrove](https://github.com/yeoldegrove))
+- add back method\_missing support to set\_unless [\#5103](https://github.com/chef/chef/pull/5103) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix \#5094 node.default\_unless issue in 12.12.13 [\#5097](https://github.com/chef/chef/pull/5097) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix \#5078 using cwd parameter instead of Dir.pwd [\#5079](https://github.com/chef/chef/pull/5079) ([Tensibai](https://github.com/Tensibai))
+
+## [v12.12.15](https://github.com/chef/chef/tree/v12.12.15) (2016-07-08)
+[Full Changelog](https://github.com/chef/chef/compare/v12.12.13...v12.12.15)
+
+**Fixed Bugs:**
+
+- Fix for #5094 12.12.13 node.default_unless issue [\#5097](https://github.com/chef/chef/pull/5097) ([lamont-granquist](https://github.com/lamont-granquist))
+
+## [v12.12.13](https://github.com/chef/chef/tree/v12.12.13) (2016-07-01)
+[Full Changelog](https://github.com/chef/chef/compare/v12.11.18...v12.12.13)
+
+**Implemented Enhancements:**
+
+- Tweak 3694 warnings [\#5075](https://github.com/chef/chef/pull/5075) ([lamont-granquist](https://github.com/lamont-granquist))
+- Adding node object to Data collector run\_converge message [\#5065](https://github.com/chef/chef/pull/5065) ([adamleff](https://github.com/adamleff))
+- Attribute API improvements [\#5029](https://github.com/chef/chef/pull/5029) ([lamont-granquist](https://github.com/lamont-granquist))
+- Remove deprecated Thread.exclusive around require call. [\#5068](https://github.com/chef/chef/pull/5068) ([maxlazio](https://github.com/maxlazio))
+- Ensure that chef-solo uses the expected repo dir [\#5059](https://github.com/chef/chef/pull/5059) ([thommay](https://github.com/thommay))
+- Expand data\_collector resource list to include all resources [\#5058](https://github.com/chef/chef/pull/5058) ([adamleff](https://github.com/adamleff))
+- Turn off fips with an empty environment var [\#5048](https://github.com/chef/chef/pull/5048) ([mwrock](https://github.com/mwrock))
+- Deprecate knife-supermarket gem [\#4896](https://github.com/chef/chef/pull/4896) ([thommay](https://github.com/thommay))
+- Update Nokogiri [\#5042](https://github.com/chef/chef/pull/5042) ([mwrock](https://github.com/mwrock))
+- Remote resource should respect sensitive flag [\#5025](https://github.com/chef/chef/pull/5025) ([PrajaktaPurohit](https://github.com/PrajaktaPurohit))
+- Convert the 3694 warning to a deprecation so it will be subject to the usual deprecation formatting \(collected at the bottom, can be made an error, etc\). [\#5022](https://github.com/chef/chef/pull/5022) ([coderanger](https://github.com/coderanger))
+- Deprecate `knife cookbook create` in favor of `chef generate cookbook`. [\#5021](https://github.com/chef/chef/pull/5021) ([tylercloke](https://github.com/tylercloke))
+
+**Fixed Bugs:**
+
+- Fixes windows_package uninstall scenarios by calling uninstall string directly [\#5050](https://github.com/chef/chef/pull/5050) ([mwrock](https://github.com/mwrock))
+- Fix gem_package idempotency [\#5046](https://github.com/chef/chef/pull/5046) ([thommay](https://github.com/thommay))
+- Undefined local variable lookup in multiplexed_dir.rb [\#5027](https://github.com/chef/chef/issues/5027) ([robdimarco](https://github.com/robdimarco))
+- Correctly write out data collector metadata file [\#5019](https://github.com/chef/chef/pull/5019) ([adamleff](https://github.com/adamleff))
+- Eliminate missing constant errors for LWRP class [\#5000](https://github.com/chef/chef/pull/5000) ([PrajaktaPurohit](https://github.com/PrajaktaPurohit))
+- Updated_resource_count to data collector should only include updated resources [\#5006](https://github.com/chef/chef/pull/5006) ([adamleff](https://github.com/adamleff))
+- Don't mask directory deletion errors [\#4991](https://github.com/chef/chef/pull/4991) ([jaymzh](https://github.com/jaymzh))
+
+## [v12.11.18](https://github.com/chef/chef/tree/v12.11.18) (2016-06-02)
+[Full Changelog](https://github.com/chef/chef/compare/v12.11.17...v12.11.18)
+
+**Implemented Enhancements:**
+
+- Creation of the new DataCollector reporter [\#4973](https://github.com/chef/chef/pull/4973) ([adamleff](https://github.com/adamleff))
+- Add systemd\_unit try-restart, reload-or-restart, reload-or-try-restart actions [\#4908](https://github.com/chef/chef/pull/4908) ([nathwill](https://github.com/nathwill))
+- RFC062 exit status chef client [\#4611](https://github.com/chef/chef/pull/4611) ([smurawski](https://github.com/smurawski))
+- Create 'universal' DSL [\#4942](https://github.com/chef/chef/pull/4942) ([lamont-granquist](https://github.com/lamont-granquist))
+- Handle numeric id for the user value in the git resource [\#4902](https://github.com/chef/chef/pull/4902) ([MichaelPereira](https://github.com/MichaelPereira))
+- RFC 31 - Default solo to local mode [\#4919](https://github.com/chef/chef/pull/4919) ([thommay](https://github.com/thommay))
+- Wire up chef handlers directly from libraries [\#4933](https://github.com/chef/chef/pull/4933) ([lamont-granquist](https://github.com/lamont-granquist))
+- Reject malformed ini content in systemd\_unit resource [\#4907](https://github.com/chef/chef/pull/4907) ([nathwill](https://github.com/nathwill))
+- Update usage of @new\_resource.destination to `cwd` within the git hwrp [\#4898](https://github.com/chef/chef/pull/4898) ([joshburt](https://github.com/joshburt))
+- Support Ruby Files in ChefFS [\#4887](https://github.com/chef/chef/pull/4887) ([thommay](https://github.com/thommay))
+- Adds a system check for fips enablement and runs in fips mode if enabled [\#4880](https://github.com/chef/chef/pull/4880) ([mwrock](https://github.com/mwrock))
+- Lazy'ing candidate\_version in package provider [\#4869](https://github.com/chef/chef/pull/4869) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add systemd\_unit resource [\#4700](https://github.com/chef/chef/pull/4700) ([nathwill](https://github.com/nathwill))
+- Bump chef-zero to avoid aggressive logging [\#4878](https://github.com/chef/chef/pull/4878) ([stevendanna](https://github.com/stevendanna))
+
+**Fixed Bugs:**
+
+- Fix \#4949 and Avoid Errno::EBUSY on docker containers [\#4979](https://github.com/chef/chef/pull/4979) ([andrewjamesbrown](https://github.com/andrewjamesbrown))
+- Ensure recipe-url works right in solo [\#4957](https://github.com/chef/chef/pull/4957) ([thommay](https://github.com/thommay))
+- Fix portage provider to support version with character [\#4966](https://github.com/chef/chef/pull/4966) ([crigor](https://github.com/crigor))
+- Fixes \#4968 and only retrieves the latest version of packages from chocolatey [\#4977](https://github.com/chef/chef/pull/4977) ([mwrock](https://github.com/mwrock))
+- Update contributing doc to better reflect reality [\#4962](https://github.com/chef/chef/pull/4962) ([tas50](https://github.com/tas50))
+- Load cookbook versions correctly for knife [\#4936](https://github.com/chef/chef/pull/4936) ([thommay](https://github.com/thommay))
+- Gem metadata command needs Gem.clear\_paths [\#4929](https://github.com/chef/chef/pull/4929) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix os x profile provider for nil [\#4921](https://github.com/chef/chef/pull/4921) ([achand](https://github.com/achand))
+- Cookbook site install : tar error on windows [\#4867](https://github.com/chef/chef/pull/4867) ([willoucom](https://github.com/willoucom))
+- Fix yum\_package breakage \(the =~ operator in ruby is awful\) [\#4912](https://github.com/chef/chef/pull/4912) ([lamont-granquist](https://github.com/lamont-granquist))
+- Encode registry enumerated values and keys to utf8 instead of the local codepage [\#4906](https://github.com/chef/chef/pull/4906) ([mwrock](https://github.com/mwrock))
+- Chocolatey Package Provider chomps nil object [\#4760](https://github.com/chef/chef/pull/4760) ([svmastersamurai](https://github.com/svmastersamurai))
+- Fixes knife ssl check on windows [\#4886](https://github.com/chef/chef/pull/4886) ([mwrock](https://github.com/mwrock))
+
+## [v12.10.24](https://github.com/chef/chef/tree/v12.10.24) (2016-04-27)
+[Full Changelog](https://github.com/chef/chef/compare/v12.10.23...v12.10.24)
+
+**Fixed Bugs:**
+
+- Removing non-existent members from group should not fail [\#4812](https://github.com/chef/chef/pull/4812) ([chefsalim](https://github.com/chefsalim))
+- The easy\_install provider and resource are deprecated and will be removed in Chef 13 [\#4860](https://github.com/chef/chef/pull/4860) ([coderanger](https://github.com/coderanger))
+
+**Tech cleanup:**
+
+- Refactor ChefFS files to be files [\#4837](https://github.com/chef/chef/pull/4837) ([thommay](https://github.com/thommay))
+- Rename and add backcompat requires for ChefFS dirs [\#4830](https://github.com/chef/chef/pull/4830) ([thommay](https://github.com/thommay))
+- Refactor ChefFS directories to be directories [\#4826](https://github.com/chef/chef/pull/4826) ([thommay](https://github.com/thommay))
+- Move all ChefFS exceptions into a single file [\#4822](https://github.com/chef/chef/pull/4822) ([thommay](https://github.com/thommay))
+
+**Enhancements:**
+
+- Add layout option support for device creation to mdadm resource provider [\#4855](https://github.com/chef/chef/pull/4855) ([kbruner](https://github.com/kbruner))
+- add notifying\_block and subcontext\_block to chef [\#4818](https://github.com/chef/chef/pull/4818) ([lamont-granquist](https://github.com/lamont-granquist))
+- modernize shell\_out method syntax [\#4865](https://github.com/chef/chef/pull/4865) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update rubygems provider to support local install of gems if so specified [\#4847](https://github.com/chef/chef/pull/4847) ([PrajaktaPurohit](https://github.com/PrajaktaPurohit))
+- fix details in with\_run\_context [\#4839](https://github.com/chef/chef/pull/4839) ([lamont-granquist](https://github.com/lamont-granquist))
+- Lock dependencies of chef through a `Gemfile.lock` [\#4820](https://github.com/chef/chef/pull/4820) ([jkeiser](https://github.com/jkeiser))
+- add better resource manipulation API [\#4834](https://github.com/chef/chef/pull/4834) ([lamont-granquist](https://github.com/lamont-granquist))
+- add nillable apt\_repository and nillable properties [\#4832](https://github.com/chef/chef/pull/4832) ([lamont-granquist](https://github.com/lamont-granquist))
## [v12.9](https://github.com/chef/chef/tree/v12.9.38) (2016-04-09)
[Full Changelog](https://github.com/chef/chef/compare/v12.8.2...v12.9.38)
@@ -498,7 +1451,7 @@ of partial templates.
Typo fixes
* [**Tim Smith**](https://github.com/tas50)
Typo fixes
-* [Pull 2505](https://github.com/opscode/chef/pull/2505) Make Chef handle URIs in a case-insensitive manner
+* [Pull 2505](https://github.com/chef/chef/pull/2505) Make Chef handle URIs in a case-insensitive manner
* [**Phil Dibowitz**](https://github.com/jaymzh):
Drop SSL warnings now that we have a safe default
* [Pull 2684](https://github.com/chef/chef/pull/2684) Remove ole_initialize/uninitialize which cause problems with Ruby >= 2
@@ -584,32 +1537,32 @@ of partial templates.
## 12.0.3
* [**Phil Dibowitz**](https://github.com/jaymzh):
-[Issue 2594](https://github.com/opscode/chef/issues/2594) Restore missing require in `digester`.
+[Issue 2594](https://github.com/chef/chef/issues/2594) Restore missing require in `digester`.
## 12.0.2
-* [Issue 2578](https://github.com/opscode/chef/issues/2578) Check that `installed` is not empty for `keg_only` formula in Homebrew provider
-* [Issue 2609](https://github.com/opscode/chef/issues/2609) Resolve the circular dependency between ProviderResolver and Resource.
-* [Issue 2596](https://github.com/opscode/chef/issues/2596) Fix nodes not writing to disk
-* [Issue 2580](https://github.com/opscode/chef/issues/2580) Make sure the relative paths are preserved when using link resource.
-* [Pull 2630](https://github.com/opscode/chef/pull/2630) Improve knife's SSL error messaging
-* [Issue 2606](https://github.com/opscode/chef/issues/2606) chef 12 ignores default_release for apt_package
-* [Issue 2602](https://github.com/opscode/chef/issues/2602) Fix `subscribes` resource notifications.
-* [Issue 2578](https://github.com/opscode/chef/issues/2578) Check that `installed` is not empty for `keg_only` formula in Homebrew provider.
+* [Issue 2578](https://github.com/chef/chef/issues/2578) Check that `installed` is not empty for `keg_only` formula in Homebrew provider
+* [Issue 2609](https://github.com/chef/chef/issues/2609) Resolve the circular dependency between ProviderResolver and Resource.
+* [Issue 2596](https://github.com/chef/chef/issues/2596) Fix nodes not writing to disk
+* [Issue 2580](https://github.com/chef/chef/issues/2580) Make sure the relative paths are preserved when using link resource.
+* [Pull 2630](https://github.com/chef/chef/pull/2630) Improve knife's SSL error messaging
+* [Issue 2606](https://github.com/chef/chef/issues/2606) chef 12 ignores default_release for apt_package
+* [Issue 2602](https://github.com/chef/chef/issues/2602) Fix `subscribes` resource notifications.
+* [Issue 2578](https://github.com/chef/chef/issues/2578) Check that `installed` is not empty for `keg_only` formula in Homebrew provider.
* [**gh2k**](https://github.com/gh2k):
- [Issue 2625](https://github.com/opscode/chef/issues/2625) Fix missing `shell_out!` for `windows_package` resource
+ [Issue 2625](https://github.com/chef/chef/issues/2625) Fix missing `shell_out!` for `windows_package` resource
* [**BackSlasher**](https://github.com/BackSlasher):
- [Issue 2634](https://github.com/opscode/chef/issues/2634) Fix `option ':command' is not a valid option` error in subversion provider.
+ [Issue 2634](https://github.com/chef/chef/issues/2634) Fix `option ':command' is not a valid option` error in subversion provider.
* [**Seth Vargo**](https://github.com/sethvargo):
- [Issue 2345](https://github.com/opscode/chef/issues/2345) Allow knife to install cookbooks with metadata.json.
+ [Issue 2345](https://github.com/chef/chef/issues/2345) Allow knife to install cookbooks with metadata.json.
## 12.0.1
-* [Issue 2552](https://github.com/opscode/chef/issues/2552) Create constant for LWRP before calling `provides`
-* [Issue 2545](https://github.com/opscode/chef/issues/2545) `path` attribute of `execute` resource is restored to provide backwards compatibility with Chef 11.
-* [Issue 2565](https://github.com/opscode/chef/issues/2565) Fix `Chef::Knife::Core::BootstrapContext` constructor for knife-windows compat.
-* [Issue 2566](https://github.com/opscode/chef/issues/2566) Make sure Client doesn't raise error when interval is set on Windows.
-* [Issue 2560](https://github.com/opscode/chef/issues/2560) Fix `uninitialized constant Windows::Constants` in `windows_eventlog`.
-* [Issue 2563](https://github.com/opscode/chef/issues/2563) Make sure the Chef Client rpm packages are signed with GPG keys correctly.
+* [Issue 2552](https://github.com/chef/chef/issues/2552) Create constant for LWRP before calling `provides`
+* [Issue 2545](https://github.com/chef/chef/issues/2545) `path` attribute of `execute` resource is restored to provide backwards compatibility with Chef 11.
+* [Issue 2565](https://github.com/chef/chef/issues/2565) Fix `Chef::Knife::Core::BootstrapContext` constructor for knife-windows compat.
+* [Issue 2566](https://github.com/chef/chef/issues/2566) Make sure Client doesn't raise error when interval is set on Windows.
+* [Issue 2560](https://github.com/chef/chef/issues/2560) Fix `uninitialized constant Windows::Constants` in `windows_eventlog`.
+* [Issue 2563](https://github.com/chef/chef/issues/2563) Make sure the Chef Client rpm packages are signed with GPG keys correctly.
## 12.0.0
@@ -722,7 +1675,7 @@ of partial templates.
* [**Ionuț Arțăriși**](https://github.com/mapleoin):
Changed the default group provider to gpasswd on SLES versions 12 and higher.
* [**Noah Kantrowitz**](https://github.com/coderanger):
- Implemented [RFC017 - File Specificity Overhaul](https://github.com/opscode/chef-rfc/blob/master/rfc017-file-specificity.md).
+ Implemented [RFC017 - File Specificity Overhaul](https://github.com/chef/chef-rfc/blob/master/rfc017-file-specificity.md).
* [**James Bence**](https://github.com/jbence):
Improved the reliability of Git provider by making it to be more specific when selecting tags.
* [**Jean Mertz**](https://github.com/JeanMertz):
@@ -760,7 +1713,7 @@ of partial templates.
### Chef Contributions
* ruby 1.9.3 support is dropped
-* Added RFC-023 Chef 12 Attribute Changes (https://github.com/opscode/chef-rfc/blob/master/rfc023-chef-12-attributes-changes.md)
+* Added RFC-023 Chef 12 Attribute Changes (https://github.com/chef/chef-rfc/blob/master/rfc023-chef-12-attributes-changes.md)
* Added os/platform_family options to provides syntax on the Chef::Resource DSL
* Added provides methods to the Chef::Provider DSL
* Added supported?(resource, action) class method to all Providers for late-evaluation if a provider can handle a
@@ -1013,5 +1966,4 @@ of partial templates.
* Fixed a bug where nested resources that inherited from Resource::LWRPBase
would not share the same actions/default_action as their parent
* Raise error if a guard_interpreter is specified and a block is passed to a guard (conditional)
-* Allow specifying a guard_interpreter after a conditional on a resource (Fixes #1943)
-* Windows package type should be a symbol (Fixes #1997)
+* Allow specifying a guard_interpreter after a conditional on a resource (Fixes #1943) \ No newline at end of file
diff --git a/CHEF_MVPS.md b/CHEF_MVPS.md
index dd46ea9174..a5abe07a56 100644
--- a/CHEF_MVPS.md
+++ b/CHEF_MVPS.md
@@ -2,7 +2,8 @@
Every release of Chef we pick someone from the community to name as the Most Valuable Player for that release. It could be someone who provided a big feature, reported a security vulnerability, or someone doing great things in the community that we want to highlight.
-In addition to the Hall of Fame and MVP recipients, three individuals are awarded the distinction of "Awesome Community Chef" each year at ChefConf.
+In addition to the Hall of Fame and MVP recipients, a number of individuals are awarded the distinction
+of "Awesome Community Chef" each year at ChefConf.
#### Hall of Fame
@@ -91,7 +92,7 @@ After receiving three MVP awards, we add someone to the hall of fame. We want to
#### Awesome Community Chefs
-Each year at ChefConf, three individuals are awarded the Awesome Community Chef award. The Awesome Community Chef awards are a way for the community to recognize a few of the individuals who have made a dramatic impact and have helped further the cause.
+Each year at ChefConf, a number of individuals are awarded the Awesome Community Chef award. The Awesome Community Chef awards are a way for the community to recognize a few of the individuals who have made a dramatic impact and have helped further the cause.
* 2013
* [Bryan Berry](https://github.com/bryanwb)
@@ -105,4 +106,14 @@ Each year at ChefConf, three individuals are awarded the Awesome Community Chef
* 2015
* [Jon Cowie](https://github.com/jonlives)
* [Noah Kantrowitz](https://github.com/coderanger)
- * [Matt Wrock](https://github.com/mwrock) \ No newline at end of file
+ * [Matt Wrock](https://github.com/mwrock)
+* 2016
+ * [Mike Fiedler](https://github.com/miketheman)
+ * [Doug Ireton](https://github.com/dougireton)
+ * [Stuart Preston](https://github.com/stuartpreston)
+ * [Seth Thomas](https://github.com/cheeseplus)
+* 2017
+ * [Ben Dang](https://github.com/bdangit)
+ * [Annie Hedgpeth](https://github.com/anniehedgpeth)
+ * [Sean O'Meara](https://github.com/someara)
+ * [Nell Shamrell-Harrington](https://github.com/nellshamrell) \ No newline at end of file
diff --git a/CLA_ARCHIVE.md b/CLA_ARCHIVE.md
index 2593a71470..640277d354 100644
--- a/CLA_ARCHIVE.md
+++ b/CLA_ARCHIVE.md
@@ -1,9 +1,14 @@
-Corporate CLAs
+# Corporate CLAs
-The list of Corporate CLAs allowed to contribute to Opscode projects. Only contributions from approved employees of these companies are acceptable.
+An archive of the signers of Corporate Contributor License Agreements (CCLA).
+Approved employees of these companies were permitted to contribute to Chef
+projects.
-Employees get approved by being listed on the schedule A of the Corporate CLA.
+## From the Paper CLA Era (Prior to 2014-06-12)
+
+Prior to June 2014, employees were approved by being listed on the schedule A of
+the Corporate CLA.
| **Number:** | **Company:** | **Date:** |
|:------------|:---------------------------------------|:----------|
@@ -299,17 +304,364 @@ Employees get approved by being listed on the schedule A of the Corporate CLA.
| 290 | Linaro Limited | 4/4/14 |
| 291 | Spanlink Communications | 4/17/14 |
| 292 | "WESEEK, Inc" | 4/29/14 |
-| 293 | "Iniqa UK, Ltd" | 6/4/14 |
-| 294 | Jemstep | 6/13/1 |
+| 293 | "Iniqa UK, Ltd" | 6/4/14 |
+| 294 | Jemstep | 6/13/14 |
+## From the Supermarket CCLA Era (from 2014-06-12 to 2016-10-03)
+Many organizations signed the CCLA again in Supermarket.
+| Company: | Date: |
+|:---------------------------------------------|:-----------|
+| PayPal, Inc. | 2014-06-12 |
+| FullStack | 2014-06-17 |
+| OnBeep, Inc. | 2014-06-17 |
+| Orion Labs, Inc. | 2014-06-17 |
+| Ngineered | 2014-06-17 |
+| Brightcove, Inc | 2014-06-19 |
+| Belly, Inc. | 2014-06-19 |
+| OneHealth Solutions, Inc. | 2014-06-19 |
+| EverTrue, Inc. | 2014-06-23 |
+| Central Desktop | 2014-06-23 |
+| HiganWorks LLC | 2014-06-26 |
+| adesso mobile solutions GmbH | 2014-06-27 |
+| Express42 | 2014-06-29 |
+| Onddo Labs | 2014-07-01 |
+| Codaptive (Pty) Ltd | 2014-07-01 |
+| Sean Porter Consulting | 2014-07-02 |
+| Klarna AB | 2014-07-03 |
+| a Committee of Lunatics, LLC | 2014-07-03 |
+| Moriz GmbH | 2014-07-08 |
+| TCN Inc | 2014-07-09 |
+| JetThoughts LLC | 2014-07-09 |
+| Clodo | 2014-07-10 |
+| Openhood "Halter & Tron" S.E.N.C. | 2014-07-10 |
+| grincom | 2014-07-10 |
+| Facebook | 2014-07-11 |
+| Bluedog Operations | 2014-07-11 |
+| Spanlink Communications | 2014-07-11 |
+| Heartland Payment Systems | 2014-07-13 |
+| Ooyala | 2014-07-14 |
+| Deveo | 2014-07-14 |
+| Schuberg Philis B.V. | 2014-07-14 |
+| Engine Yard, Inc. | 2014-07-14 |
+| FancyGuy Technologies | 2014-07-15 |
+| Mojo Lingo LLC | 2014-07-15 |
+| Bally Technologies | 2014-07-16 |
+| Rackspace | 2014-07-16 |
+| Jemstep Inc. | 2014-07-16 |
+| Heavy Water | 2014-07-16 |
+| Rstudio, Inc. | 2014-07-17 |
+| BlackBerry Ltd. | 2014-07-17 |
+| ffuenf | 2014-07-19 |
+| Grantbow Consulting | 2014-07-22 |
+| O'Reilly Media | 2014-07-23 |
+| Revinate, Inc. | 2014-07-24 |
+| PHutchins | 2014-07-24 |
+| AdREM Systems | 2014-07-24 |
+| Deutsche Telekom HBS | 2014-07-24 |
+| Ticketmaster | 2014-07-26 |
+| chewbacco | 2014-07-28 |
+| Ontario Systems | 2014-07-28 |
+| CoreMedia AG | 2014-07-29 |
+| Democracy Works, Inc. | 2014-07-29 |
+| EITA | 2014-07-29 |
+| GitLab B.V. | 2014-07-30 |
+| Bloomberg LP | 2014-07-30 |
+| MTA SZTAKI | 2014-07-31 |
+| Travis Warlick LLC | 2014-07-31 |
+| Cerner Corporation | 2014-07-31 |
+| Texas A&M University | 2014-08-01 |
+| Granicus, Inc. | 2014-08-01 |
+| Splunk Inc. | 2014-08-05 |
+| ThirdWave Insights LLC | 2014-08-05 |
+| OurTownRentals.com | 2014-08-05 |
+| Intoximeters, Inc | 2014-08-05 |
+| Seges | 2014-08-07 |
+| Pixl8 Interactive Ltd | 2014-08-08 |
+| Gazelle | 2014-08-08 |
+| Takipi | 2014-08-12 |
+| De Marque | 2014-08-14 |
+| Web Man d.o.o. | 2014-08-17 |
+| Picabit | 2014-08-26 |
+| Widgit Software | 2014-08-26 |
+| Bitcellar, Inc. | 2014-08-27 |
+| Yelp Inc | 2014-08-27 |
+| TORCH GmbH | 2014-08-28 |
+| McKesson | 2014-08-28 |
+| pronetics | 2014-09-01 |
+| Numergy | 2014-09-03 |
+| EveryPay AS | 2014-09-09 |
+| Appriss, Inc. | 2014-09-09 |
+| Claude Tech | 2014-09-10 |
+| Omise.co | 2014-09-11 |
+| Criteo S.A. | 2014-09-11 |
+| TelVue Corporation | 2014-09-11 |
+| Expedia | 2014-09-15 |
+| EBSCO | 2014-09-22 |
+| SAP | 2014-09-26 |
+| inGenerator Ltd | 2014-09-29 |
+| WWWH LLC | 2014-09-30 |
+| RapidWorkspace | 2014-10-04 |
+| Monomono | 2014-10-08 |
+| Nordstrom | 2014-10-10 |
+| InfoLens, Inc. | 2014-10-13 |
+| Webhippie | 2014-10-13 |
+| IBM | 2014-10-13 |
+| Xhost Australia | 2014-10-14 |
+| Simulmedia, Inc | 2014-10-14 |
+| Mypaulie | 2014-10-15 |
+| SUSE LINUX Products GmbH | 2014-10-15 |
+| Yahoo | 2014-10-17 |
+| Juniper Networks, Inc. | 2014-10-19 |
+| Bulletproof Networks | 2014-10-20 |
+| fd | 2014-10-20 |
+| Norman Joyner | 2014-10-21 |
+| TJSoftworks | 2014-10-21 |
+| Sneacho | 2014-10-22 |
+| Ecodev | 2014-10-30 |
+| Stack-up | 2014-11-02 |
+| Scalingo | 2014-11-03 |
+| Covario | 2014-11-12 |
+| Xpanxion | 2014-11-12 |
+| Schrodinger, Inc. | 2014-11-14 |
+| SK Planet | 2014-11-18 |
+| jubianchi | 2014-11-18 |
+| Médiamétrie | 2014-11-19 |
+| Terra Firma Engineering, LLC | 2014-11-19 |
+| Pantheon | 2014-11-20 |
+| Clogeny Technologies | 2014-11-21 |
+| DigiTar Inc. | 2014-11-22 |
+| Second Mind | 2014-11-25 |
+| Localytics | 2014-11-25 |
+| Anerhan | 2014-12-05 |
+| Treasure Data, Inc. | 2014-12-06 |
+| Yakara Ltd | 2014-12-09 |
+| CirrusMio | 2014-12-10 |
+| phutchins.com | 2014-12-12 |
+| removeme | 2014-12-15 |
+| Sumo Logic | 2014-12-18 |
+| Conjur Inc | 2014-12-31 |
+| PMSIpilot | 2015-01-05 |
+| INSANEWORKS,LLC | 2015-01-06 |
+| Mad13 Entertainment | 2015-01-06 |
+| Tautt | 2015-01-10 |
+| AddThis | 2015-01-12 |
+| DreamBox Learning, Inc. | 2015-01-14 |
+| Intel | 2015-01-15 |
+| OptiShot Golf | 2015-01-16 |
+| RightScale | 2015-01-16 |
+| Yahoo Japan | 2015-01-19 |
+| My company | 2015-01-19 |
+| OSC Technologies, LLC | 2015-01-21 |
+| Aniessh Sethh | 2015-01-28 |
+| Motorcycle Industry Council | 2015-01-30 |
+| Icinga | 2015-02-02 |
+| pseudomuto | 2015-02-03 |
+| CloudOps Inc. | 2015-02-07 |
+| Magemaven | 2015-02-09 |
+| eVision Industry Software | 2015-02-09 |
+| AIRFRANCE / KLM | 2015-02-12 |
+| Elyxor, Inc | 2015-02-17 |
+| Coderico | 2015-02-19 |
+| PagerDuty | 2015-02-23 |
+| Stratalux | 2015-02-25 |
+| Barricade Security Systems, Ltd. | 2015-02-25 |
+| TpT | 2015-02-27 |
+| RevelOps Inc | 2015-03-04 |
+| Standard Edge | 2015-03-04 |
+| Qwinix Technologies | 2015-03-05 |
+| CREATIONLINE,INC. | 2015-03-09 |
+| Google, Inc. | 2015-03-09 |
+| yetu AG | 2015-03-10 |
+| Target | 2015-03-11 |
+| Cisco Systems | 2015-03-16 |
+| sshilarnav | 2015-03-16 |
+| Vista Higher Learning, Inc. | 2015-03-19 |
+| Dynamic Network Services Inc. | 2015-03-19 |
+| Cars.com | 2015-03-20 |
+| left-left-lemma Oy | 2015-03-22 |
+| Carbonite | 2015-03-23 |
+| Sum Labs, Inc | 2015-03-24 |
+| Batch.com | 2015-03-24 |
+| Mindera | 2015-03-30 |
+| ClouDesire | 2015-03-31 |
+| Mudbug Media | 2015-04-02 |
+| Oregon State University Open Source Lab | 2015-04-03 |
+| SPINEN | 2015-04-08 |
+| Needle, Inc. | 2015-04-10 |
+| Calastone Ltd | 2015-04-15 |
+| Pixid Design | 2015-04-17 |
+| Devloft Solutions, Inc. | 2015-04-27 |
+| Mingalar SAS | 2015-04-28 |
+| Latta Partners | 2015-04-30 |
+| HPE | 2015-05-01 |
+| acquantia.me | 2015-05-01 |
+| ViaSat, Inc. | 2015-05-04 |
+| Light Side | 2015-05-05 |
+| Ingenium Group, LTD | 2015-05-11 |
+| Linux-Help.org | 2015-05-13 |
+| Alert Logic, Inc. | 2015-05-22 |
+| Resivaa Technologies Pvt LTD | 2015-05-22 |
+| Gap Inc | 2015-05-22 |
+| PayByPhone Technologies | 2015-05-25 |
+| University of Derby | 2015-05-27 |
+| Blue Spurs | 2015-06-02 |
+| Citrix Systems, Inc. | 2015-06-02 |
+| hevnly | 2015-06-10 |
+| Cvent | 2015-06-10 |
+| Jonathan Serafini | 2015-06-16 |
+| ExecOnline | 2015-06-18 |
+| NCR | 2015-06-18 |
+| ServeBox SAS | 2015-06-19 |
+| neowiz games | 2015-06-22 |
+| newsdict | 2015-06-25 |
+| Loom Technology | 2015-06-30 |
+| Spanning Cloud Apps | 2015-06-30 |
+| DualSpark | 2015-07-01 |
+| MariaDB USA | 2015-07-07 |
+| Shopify Inc | 2015-07-08 |
+| Yola | 2015-07-09 |
+| Divergent Logic | 2015-07-11 |
+| Roboheart, Inc. | 2015-07-14 |
+| Scoutapp | 2015-07-20 |
+| Continuous S.A. | 2015-07-20 |
+| Room 118 Solutions, Inc. | 2015-07-21 |
+| Devialab | 2015-07-28 |
+| Asana | 2015-07-29 |
+| CSAA IG | 2015-08-05 |
+| Ed Powell | 2015-08-06 |
+| Expedia Affiliate Network | 2015-08-07 |
+| Tacit Knowledge | 2015-08-11 |
+| Layer7 Cache | 2015-08-13 |
+| The Buckle, Inc | 2015-08-13 |
+| Serveraptor | 2015-08-27 |
+| Imagination Technologies Ltd. | 2015-08-27 |
+| Ecosia GmbH | 2015-08-28 |
+| Aeode | 2015-08-31 |
+| Forever Oceans Corporation | 2015-09-01 |
+| Flugel.it | 2015-09-04 |
+| TechM | 2015-09-08 |
+| Diego | 2015-09-08 |
+| Tarak Blah | 2015-09-10 |
+| CustomInk | 2015-09-15 |
+| LightSpeed Retail | 2015-09-20 |
+| Humance AG | 2015-09-21 |
+| Crossroads Foundation Limited | 2015-09-29 |
+| Cyber Dev Team | 2015-10-02 |
+| FCL | 2015-10-02 |
+| Marchex, Inc. | 2015-10-06 |
+| S4M | 2015-10-07 |
+| PhishMe | 2015-10-13 |
+| Travis CI GmbH | 2015-10-17 |
+| mikeroySoft | 2015-10-23 |
+| NDP, LLC | 2015-10-30 |
+| None | 2015-11-02 |
+| Lodestone Technologies, LLC | 2015-11-04 |
+| LimePoint Pty Ltd | 2015-11-05 |
+| echohack | 2015-11-12 |
+| AIB | 2015-11-16 |
+| NativeX Holdings LLC | 2015-11-17 |
+| Cloudrifles | 2015-11-20 |
+| Secureworks | 2015-11-20 |
+| Taufek | 2015-11-22 |
+| BuyerQuest, Inc. | 2015-11-25 |
+| The Weather Companies | 2015-12-03 |
+| Monsenso | 2015-12-07 |
+| Formatron | 2015-12-10 |
+| Fidelity Investments | 2015-12-10 |
+| DataCite | 2015-12-14 |
+| Parallels | 2015-12-17 |
+| Wavefront, Inc. | 2015-12-30 |
+| Trust1Team | 2016-01-01 |
+| Nulogy Corporation | 2016-01-07 |
+| Salmon | 2016-01-08 |
+| Flexiant | 2016-01-12 |
+| COSProfessionals, LLC | 2016-01-14 |
+| Bellacross Technologies, LLC | 2016-01-18 |
+| Relenz | 2016-01-18 |
+| REĀN Cloud Solutions LLC | 2016-01-18 |
+| LyraPhase | 2016-01-18 |
+| Tikal Knowledge, SAP | 2016-01-19 |
+| CloudPassage | 2016-01-20 |
+| Topota | 2016-01-21 |
+| Sebastian Trebitz - ICT Consultancy Services | 2016-01-21 |
+| AOL | 2016-01-22 |
+| Dennis Vink | 2016-01-22 |
+| Datadog Inc. | 2016-01-26 |
+| Jason Barnett | 2016-02-02 |
+| Built by Robots | 2016-02-02 |
+| 7digital | 2016-02-05 |
+| NewVoiceMedia Ltd | 2016-02-05 |
+| dkd Internet Service GmbH | 2016-02-08 |
+| Dylan Ledbetter | 2016-02-09 |
+| Mikroways | 2016-02-13 |
+| Grassmuck.co | 2016-02-13 |
+| Postmodern Solutions | 2016-02-14 |
+| Rally Health | 2016-02-19 |
+| PolicyStat LLC | 2016-02-19 |
+| Pivotal Labs | 2016-02-22 |
+| Code.org | 2016-02-23 |
+| Underground Media Group | 2016-02-23 |
+| Rakuten Inc. | 2016-02-26 |
+| ENC4U | 2016-03-01 |
+| Mediarithmics | 2016-03-07 |
+| Planalytics | 2016-03-11 |
+| IT Projections Limited | 2016-03-14 |
+| ShoreTel Inc. | 2016-03-17 |
+| Apparently | 2016-03-18 |
+| TraSec GmbH | 2016-03-21 |
+| B1 Systems GmbH | 2016-03-22 |
+| Beyah Solutions, LLC | 2016-03-24 |
+| Inetsys SL | 2016-03-30 |
+| Instabug | 2016-04-07 |
+| Anaplan | 2016-04-25 |
+| ForeFlight LLC | 2016-04-25 |
+| Hydra Technologies, Inc | 2016-04-28 |
+| Gengo | 2016-05-11 |
+| Crifkin Amalgamated LLC | 2016-05-11 |
+| Crifkin Amalgamated LLC | 2016-05-11 |
+| Vision Critical | 2016-05-16 |
+| iJet Technologies | 2016-05-17 |
+| SendGrid, Inc. | 2016-05-18 |
+| Mulesoft | 2016-05-29 |
+| Shopify | 2016-06-13 |
+| The Regents of the University of Colorado | 2016-06-28 |
+| Tampere University of Technology | 2016-07-01 |
+| Altiscale | 2016-07-01 |
+| Atlas Digital | 2016-07-05 |
+| Fastly | 2016-07-12 |
+| cloud.ee OÜ | 2016-07-26 |
+| Hostelworld | 2016-07-28 |
+| Wazuh | 2016-07-28 |
+| Arista Networks | 2016-08-10 |
+| Totem Power, Inc. | 2016-08-13 |
+| Piratenpartei Deutschland | 2016-08-20 |
+| Testfabrik AG | 2016-09-01 |
+| B7Interactive, LLC | 2016-09-06 |
+| PolyHat | 2016-09-07 |
+| Aaron's, Inc. | 2016-09-07 |
+| SWB company | 2016-09-09 |
+| Nclouds Inc. | 2016-09-12 |
+| DataMetis | 2016-09-12 |
+| nclouds | 2016-09-13 |
+| Personnal | 2016-09-15 |
+| Soltius NZ | 2016-09-16 |
+| Primar Group | 2016-09-26 |
+| Gannett | 2016-09-26 |
+| etracker GmbH | 2016-09-27 |
+| Freelance | 2016-09-28 |
-Allowed Contributors
+# Allowed Contributors 2009 - 2016-10-93
-The list of allowed contributors to Opscode projects. Persons listed as associated with a company may also be individual contributors as well.
+The list of allowed contributors from 2009 through 3 October, 2016 prior to Chef
+projects [moving to a Developer Certificate of Origin
+process](https://blog.chef.io/2016/09/19/introducing-developer-certificate-of-origin/).
+See [CONTRIBUTING](https://github.com/chef/chef/blob/master/CONTRIBUTING.md) for
+how to contribute to Chef today.
-To get on the list, check out our instructions on how to contribute.
+Persons listed as associated with a company may also be individual contributors
+as well.
1. Adam Jacob Opscode
1. Andy Delcambre Engineyard 1/7/09
@@ -2508,3 +2860,2340 @@ To get on the list, check out our instructions on how to contribute.
1. Stafford Brunk 6/25/14
1. Sumit Gupta 6/26/14
1. Jan Mara 6/27/14
+1. Christopher Webber Chef Software, Inc. 2008-03-05
+1. Douglas Knight 2010-09-09
+1. Alma Chao Facebook 2012-05-08
+1. Pim Berger Schuberg Philis B.V. 2012-08-02
+1. Melanie Howe Ontario Systems 2013-01-23
+1. Michael Curry IBM 2013-05-14
+1. Sean Knapp Ooyala, Inc 2013-10-09
+1. Franklin Webber 2014-06-12
+1. Sean OMeara 2014-06-12
+1. Mike Splain 2014-06-12
+1. Joshua Timberman 2014-06-12
+1. Mike Splain PayPal, Inc. 2014-06-12
+1. Franklin Webber 2014-06-12
+1. Christopher Webber 2014-06-13
+1. Nathen Harvey 2014-06-13
+1. Julian Dunn 2014-06-13
+1. Seth Vargo 2014-06-13
+1. James Scott 2014-06-13
+1. James Scott 2014-06-13
+1. Steven Danna 2014-06-13
+1. Paul Mooring 2014-06-13
+1. Tristan O'Neil 2014-06-13
+1. Brian Cobb 2014-06-13
+1. Soo Choi 2014-06-13
+1. Soo Choi 2014-06-13
+1. Josh Brand 2014-06-13
+1. Mike Biang 2014-06-13
+1. Larry Eichenbaum 2014-06-13
+1. Josh Black 2014-06-16
+1. Peter Fern 2014-06-16
+1. Brian Flad 2014-06-17
+1. James Walker 2014-06-17
+1. Karl Shrubb 2014-06-17
+1. Jonathan Asghar 2014-06-17
+1. Brett Chalupa FullStack 2014-06-17
+1. Greg Albrecht OnBeep, Inc. 2014-06-17
+1. Richard Harvey Ngineered 2014-06-17
+1. Brett Chalupa 2014-06-17
+1. Mike Fiedler 2014-06-17
+1. Jonathan Asghar 2014-06-17
+1. Yves Laroche 2014-06-17
+1. Greg Albrecht 2014-06-17
+1. Darron Froese 2014-06-17
+1. Darron Froese 2014-06-17
+1. Jorge Espada 2014-06-17
+1. Richard Harvey 2014-06-17
+1. Jorge Espada 2014-06-17
+1. Jonathan Hartman 2014-06-17
+1. Allan Espinosa 2014-06-18
+1. Troy Ready 2014-06-18
+1. Jeremy Olliver 2014-06-18
+1. Arjuna Christensen 2014-06-18
+1. Jameson Lee 2014-06-18
+1. Jameson Lee 2014-06-18
+1. Gavin Reynolds 2014-06-18
+1. Fletcher Nichol 2014-06-18
+1. Pete Cheslock 2014-06-18
+1. Pete Cheslock 2014-06-18
+1. Michael Ivey 2014-06-18
+1. Matt Ray 2014-06-18
+1. Seth Chisamore 2014-06-18
+1. Alex Vinyar 2014-06-18
+1. Christian Vozar 2014-06-19
+1. Christian Vozar Belly, Inc. 2014-06-19
+1. David Radcliffe 2014-06-19
+1. Sander van Zoest OneHealth Solutions, Inc. 2014-06-19
+1. Brint O'Hearn 2014-06-19
+1. Sander van Zoest 2014-06-19
+1. Jay Perry Brightcove 2014-06-19
+1. Jon Cowie 2014-06-19
+1. Jake Champlin 2014-06-19
+1. Karl Shrubb 2014-06-19
+1. Panagiotis Papadomitsos 2014-06-20
+1. Curtis Stewart 2014-06-20
+1. Chaz Ruhl 2014-06-21
+1. Chaz Ruhl 2014-06-21
+1. Jesse Nelson 2014-06-22
+1. Kristian Vlaardingerbroek 2014-06-22
+1. Daniel Searles 2014-06-23
+1. Serdar Sutay 2014-06-23
+1. Jeff Byrnes 2014-06-23
+1. Eric Herot 2014-06-23
+1. William Young 2014-06-23
+1. William Young 2014-06-23
+1. Cassiano Leal 2014-06-23
+1. Joseph Nuspl 2014-06-23
+1. Joseph Nuspl 2014-06-23
+1. Daniel Searles 2014-06-23
+1. Ryan Hass 2014-06-23
+1. Eric Herot EverTrue, Inc. 2014-06-23
+1. Craig Lewis Central Desktop 2014-06-23
+1. James Le Cuirot 2014-06-24
+1. Ian Meyer 2014-06-24
+1. Paul MacDougall 2014-06-24
+1. Jeff Blaine 2014-06-24
+1. John Martin 2014-06-24
+1. Matt Mencel 2014-06-24
+1. Mike Thibodeau 2014-06-24
+1. Phong Ha 2014-06-24
+1. Michael de Man 2014-06-24
+1. Larry Zarou 2014-06-24
+1. John Martin 2014-06-24
+1. Kishore Kumar 2014-06-24
+1. James Rosser 2014-06-24
+1. Mathieu Sauve-Frankel 2014-06-24
+1. Mathieu Sauve-Frankel 2014-06-24
+1. Scott Likens 2014-06-24
+1. Scott Likens 2014-06-24
+1. Tensibai Zahoying 2014-06-24
+1. Tensibai Zahoying 2014-06-24
+1. Brian Clark 2014-06-24
+1. Jordan Evans 2014-06-25
+1. Brian Scott 2014-06-25
+1. Stafford Brunk 2014-06-25
+1. Hugo Lopes Tavares 2014-06-25
+1. Ivan Larionov 2014-06-25
+1. Harlan Barnes 2014-06-26
+1. Jan Klare 2014-06-26
+1. Yukihiko Sawanobori HiganWorks LLC 2014-06-26
+1. Jessica Mink 2014-06-26
+1. Thomas Robison 2014-06-26
+1. Andreas Thielen adesso mobile solutions GmbH 2014-06-27
+1. Alban Diguer 2014-06-27
+1. Andreas Thielen 2014-06-27
+1. Hugo Trippaers 2014-06-27
+1. Torben Knerr 2014-06-27
+1. Kirill Shirinkin 2014-06-28
+1. Hendrik Schaeidt 2014-06-28
+1. Nikita Borzykh Express 42 2014-06-29
+1. Alex Zepeda 2014-06-29
+1. Mohit Sethi 2014-06-30
+1. Florian Holzhauer 2014-06-30
+1. Jesse Hu 2014-06-30
+1. Richard Banks 2014-07-01
+1. Richard Banks Codaptive (Pty) Ltd 2014-07-01
+1. Teemu Matilainen 2014-07-01
+1. Steffen Gebert 2014-07-01
+1. Xabier de Zuazo Onddo Labs 2014-07-01
+1. Vijay Bheemineni 2014-07-01
+1. Richard Banks 2014-07-01
+1. Sean Porter 2014-07-02
+1. Michael Heijmans 2014-07-02
+1. Patrick Collins 2014-07-02
+1. Sean Porter Sean Porter Consulting 2014-07-02
+1. BK Box 2014-07-02
+1. Sean Walberg 2014-07-02
+1. Erkan Yilmaz 2014-07-02
+1. Jesse Davis 2014-07-02
+1. Florin Stan 2014-07-03
+1. Olle Lundberg Klarna 2014-07-03
+1. Tim Dysinger 2014-07-03
+1. Elan Ruusamäe 2014-07-03
+1. Tristan Keen 2014-07-03
+1. Olle Lundberg 2014-07-03
+1. Simeon Willbanks 2014-07-03
+1. Ryan Cragun 2014-07-03
+1. Ranjib Dey 2014-07-03
+1. Brian Dwyer 2014-07-03
+1. Christopher Read 2014-07-03
+1. Jerry Jackson a Committee of Lunatics, LLC 2014-07-03
+1. David Petzel 2014-07-04
+1. Per Hasselström 2014-07-04
+1. Timothy Smith 2014-07-06
+1. Evan Sosenko 2014-07-06
+1. Mike Morris 2014-07-07
+1. Paul Welch 2014-07-07
+1. Andrei Neculau 2014-07-07
+1. Scott Ford 2014-07-07
+1. Tyler Fitch 2014-07-07
+1. Zachary Stevens 2014-07-07
+1. Nacer Laradji 2014-07-07
+1. Jeffrey Carapetyan 2014-07-07
+1. Joshua Zitting 2014-07-07
+1. Jonathan Serafini 2014-07-07
+1. Mark Ayers 2014-07-08
+1. Justin Campbell 2014-07-08
+1. Roland Moriz Moriz GmbH 2014-07-08
+1. Mischa Taylor 2014-07-08
+1. Hannes Van De Vreken 2014-07-08
+1. Roland Moriz 2014-07-08
+1. Daniel Webb 2014-07-08
+1. Luke Bradbury 2014-07-08
+1. Eric Wolfe 2014-07-08
+1. Peter Jönsson 2014-07-08
+1. Paul Czarkowski 2014-07-08
+1. Tucker DeWitt 2014-07-09
+1. Marcin Sawicki 2014-07-09
+1. Stanislav Bogatyrev 2014-07-09
+1. Chris Aumann 2014-07-09
+1. Ramya Kailas 2014-07-09
+1. Peter Fox 2014-07-09
+1. Giovanni Toraldo 2014-07-09
+1. Elijah Buck 2014-07-09
+1. Dinu Arateanu 2014-07-09
+1. Christian Höltje 2014-07-09
+1. Roman Chukh 2014-07-09
+1. Jared Everett 2014-07-09
+1. George Miranda 2014-07-09
+1. Michael Nikitochkin 2014-07-09
+1. Lloyd Chan 2014-07-09
+1. Florin Stan TCN Inc 2014-07-09
+1. Michael Nikitochkin JetThoughts LLC 2014-07-09
+1. John Alberts 2014-07-10
+1. Jonathan Tron 2014-07-10
+1. Ron Cuirle 2014-07-10
+1. Christopher Grinnan grincom 2014-07-10
+1. Christopher Grinnan 2014-07-10
+1. Greg Hellings 2014-07-10
+1. Jonathan Tron Openhood "Halter & Tron" S.E.N.C. 2014-07-10
+1. Egor Medvedev 2014-07-10
+1. Ishtiaq Ahmed 2014-07-10
+1. Egor Medvedev Clodo 2014-07-10
+1. Leo Gallucci 2014-07-11
+1. Tobias Johnson 2014-07-11
+1. Andrey Linko 2014-07-11
+1. Michael Bumann 2014-07-11
+1. Sam Kottler 2014-07-11
+1. Joseph Stump 2014-07-11
+1. Fernando Honig 2014-07-11
+1. Emmanuel Sciara 2014-07-11
+1. Federico Gimenez Nieto 2014-07-11
+1. Ryan Trauntvein 2014-07-11
+1. Melinda Moran 2014-07-11
+1. Wayne Huang 2014-07-11
+1. Daniel Sweeting 2014-07-11
+1. Federico Gimenez 2014-07-11
+1. Aneel Nazareth 2014-07-11
+1. Wilson Felipe Pereira 2014-07-11
+1. Christoph Krämer 2014-07-11
+1. Matt Wormley 2014-07-11
+1. John Bellone 2014-07-11
+1. Stephen Figgins 2014-07-11
+1. Kent Spillner 2014-07-11
+1. Aaron Baer 2014-07-11
+1. Cameron Johnston 2014-07-11
+1. Clayton Barber 2014-07-11
+1. David Schlenk Spanlink Communications 2014-07-11
+1. John Bellone Bloomberg Finance L.P. 2014-07-11
+1. John Bellone Bluedog Operations 2014-07-11
+1. Drew Rothstein 2014-07-11
+1. Nick Downs 2014-07-11
+1. Phil Kates 2014-07-12
+1. Marc Climent 2014-07-12
+1. Gregor Zurowski 2014-07-12
+1. Masatoshi Iwasaki 2014-07-12
+1. Kristian Van Der Vliet 2014-07-12
+1. Martin Walton 2014-07-12
+1. Christophe Arguel 2014-07-12
+1. Michael de Bruin 2014-07-12
+1. Tomohito Kanno 2014-07-12
+1. Christopher Laco 2014-07-12
+1. Angelo Olivera 2014-07-12
+1. Joel Moss 2014-07-12
+1. Nolan Davidson 2014-07-12
+1. Ian Delahorne 2014-07-12
+1. Joseph Holsten 2014-07-12
+1. Vsevolod Polyakov 2014-07-12
+1. Anthony Scalisi 2014-07-12
+1. Alexander Simonov 2014-07-12
+1. Jean-Francois Theroux 2014-07-12
+1. Andrea Granata 2014-07-12
+1. Jordan Wesolowski 2014-07-12
+1. Andy Thompson 2014-07-12
+1. Philip Hutchins 2014-07-12
+1. Phil Cohen 2014-07-12
+1. jason walker 2014-07-12
+1. Drew Sonne 2014-07-12
+1. Ben Tisdall 2014-07-13
+1. Venkat Naidu 2014-07-13
+1. Kyle Boorky 2014-07-13
+1. 莫 測 2014-07-13
+1. Martin Smith 2014-07-13
+1. Maciej Nowak 2014-07-13
+1. Sean Fleming 2014-07-13
+1. Kristofer Sommestad 2014-07-13
+1. Josh Blancett 2014-07-13
+1. fang duan 2014-07-13
+1. Oleksandr Slynko 2014-07-13
+1. Jose Luis Salas 2014-07-13
+1. Barrie Bremner 2014-07-13
+1. Jonty Wareing 2014-07-13
+1. Venkat Naidu Heartland Payment Systems 2014-07-13
+1. Steve Carman 2014-07-14
+1. Matt Whiteley Engine Yard, Inc. 2014-07-14
+1. Ilan Rabinovitch 2014-07-14
+1. Marcus Morris 2014-07-14
+1. Ilmari Kontulainen Deveo 2014-07-14
+1. Jason Vanderhoof 2014-07-14
+1. Gert Kremer 2014-07-14
+1. Jake Plimack 2014-07-14
+1. Vincent Kenney 2014-07-14
+1. Sam Dunne 2014-07-14
+1. Alexander Meng 2014-07-14
+1. James Herdman 2014-07-14
+1. Ahmed Ibrahim 2014-07-14
+1. Thomas Meeus 2014-07-14
+1. NagaLakshmi N 2014-07-14
+1. Bao Nguyen Ooyala 2014-07-14
+1. Adrian Moisey 2014-07-14
+1. Reid Beels 2014-07-14
+1. Satoshi Tanaka 2014-07-14
+1. Rodolfo Castellanos 2014-07-15
+1. Ben Klang Mojo Lingo LLC 2014-07-15
+1. Matt Wrock 2014-07-15
+1. Steve Buzonas FancyGuy Technologies 2014-07-15
+1. Ralf Haferkamp 2014-07-15
+1. James Pic 2014-07-15
+1. Ben Longden 2014-07-15
+1. Chris Fortier 2014-07-15
+1. Steve Buzonas 2014-07-15
+1. Robby Dyer 2014-07-15
+1. Greg Fitzgerald 2014-07-15
+1. Brian Dupras 2014-07-15
+1. Christian Fischer 2014-07-15
+1. Nathan Huff 2014-07-15
+1. Ben Langfeld 2014-07-15
+1. Brian Stajkowski 2014-07-15
+1. Sean Escriva Heavy Water Operations 2014-07-16
+1. Brian Stajkowski Bally Technologies 2014-07-16
+1. Darren Birkett Rackspace 2014-07-16
+1. Blake Irvin 2014-07-16
+1. Aaron Kalin 2014-07-16
+1. Olivier Brisse 2014-07-16
+1. Sean Escriva Caleb Hailey 2014-07-16
+1. Justin Shepherd Rackspace 2014-07-16
+1. Andrew Goktepe 2014-07-16
+1. Oleg Rekutin 2014-07-16
+1. Vic Iglesias 2014-07-16
+1. Adam Leff 2014-07-16
+1. Brett Cavé Jemstep Inc. 2014-07-16
+1. Max Lincoln 2014-07-17
+1. Anthony Goddard 2014-07-17
+1. Kane Rogers 2014-07-17
+1. Matthieu Ouellette-Vachon 2014-07-17
+1. Andrew Brown BlackBerry Ltd. 2014-07-17
+1. Alastair Firth 2014-07-17
+1. James Cuzella 2014-07-17
+1. Kenneth Vestergaard 2014-07-17
+1. Fred Clausen 2014-07-17
+1. Sameer Arora 2014-07-17
+1. Haggai Zagury 2014-07-17
+1. John Barney 2014-07-17
+1. Chad Barraford 2014-07-17
+1. David Barbarisi 2014-07-17
+1. Bill Findley 2014-07-17
+1. Chad Barraford RStudio, Inc. 2014-07-17
+1. Dustin Collins 2014-07-18
+1. Bernie Durfee 2014-07-18
+1. Cam Cope 2014-07-18
+1. Jean Rouge 2014-07-18
+1. Guilhem Lettron 2014-07-18
+1. Achim Rosenhagen ffuenf 2014-07-19
+1. Jeremy Murray 2014-07-19
+1. Achim Rosenhagen 2014-07-19
+1. Yusuke Murata 2014-07-20
+1. Jose Luis Ferrer Riera 2014-07-20
+1. Pavel Yudin 2014-07-20
+1. Bryan Stenson 2014-07-21
+1. Lyndon Washington 2014-07-21
+1. Alexandre Garnier 2014-07-21
+1. Sten Spans 2014-07-21
+1. Tim Rha 2014-07-21
+1. Grant Heffernan 2014-07-21
+1. Derek Groh 2014-07-21
+1. Justin Reagor 2014-07-21
+1. Nathaniel Eliot 2014-07-21
+1. Grant Bowman 2014-07-22
+1. Justin Schuhmann 2014-07-22
+1. David Kinzer 2014-07-22
+1. Grant Bowman Grantbow Consulting 2014-07-22
+1. Anshul Sharma 2014-07-22
+1. Matthew Riddle 2014-07-22
+1. Amruta Krishna Ravavarapu 2014-07-22
+1. Thomas Berger 2014-07-23
+1. Graham Weldon 2014-07-23
+1. Olivier Dolbeau 2014-07-23
+1. Ryan Uber 2014-07-23
+1. Peter Burkholder 2014-07-23
+1. Rune Madsen 2014-07-23
+1. Nick Gerakines 2014-07-23
+1. Isaac Mungai 2014-07-23
+1. Gabor Nagy 2014-07-23
+1. Matt Clifton 2014-07-23
+1. Rune Madsen O'Reilly Media 2014-07-23
+1. Patrick Birt AdRem Systems Corporation 2014-07-24
+1. Jeroen Jacobs 2014-07-24
+1. Ram Akuka 2014-07-24
+1. Jason Rust 2014-07-24
+1. Philip Hutchins PHutchins 2014-07-24
+1. Wayne Huang Revinate, Inc. 2014-07-24
+1. Ram Akuka Deutsche Telekom HBS 2014-07-24
+1. Steven Murawski 2014-07-25
+1. Alex Brehm 2014-07-25
+1. Shawn Edwards Bloomberg LP 2014-07-25
+1. Eugene Alekseev 2014-07-25
+1. Nicolas DUPEUX 2014-07-25
+1. Heath Snow 2014-07-25
+1. Alex Brehm 2014-07-25
+1. Scott Lavender 2014-07-25
+1. Paul McCallick 2014-07-25
+1. Dimitri Aivaliotis 2014-07-26
+1. Kraig Amador 2014-07-26
+1. Kraig Amador Ticketmaster 2014-07-26
+1. James FitzGibbon 2014-07-27
+1. Andrew DuFour 2014-07-27
+1. Adrian Petrescu 2014-07-28
+1. Tyler Bird Engine Yard 2014-07-28
+1. Thomas Petchel 2014-07-28
+1. Tyler Bird 2014-07-28
+1. Dmitry Tsoy 2014-07-28
+1. Mikhail Zholobov 2014-07-28
+1. Dmitry Tsoy chewbacco 2014-07-28
+1. Braulio Bhavamitra 2014-07-29
+1. Daniel Spilker CoreMedia AG 2014-07-29
+1. Eric Helgeson 2014-07-29
+1. Wes Morgan Democracy Works, Inc. 2014-07-29
+1. Bráulio Bhavamitra EITA 2014-07-29
+1. Alexey Mochkin 2014-07-29
+1. Jesse Adams 2014-07-29
+1. Djuri Baars 2014-07-29
+1. Wes Morgan 2014-07-29
+1. Alex Shkurko 2014-07-30
+1. Sytse Sijbrandij GitLab B.V. 2014-07-30
+1. James Hebden 2014-07-30
+1. Yukio Goto 2014-07-30
+1. Matt Thomson 2014-07-30
+1. Andy Fraley 2014-07-30
+1. Nathan Williams 2014-07-30
+1. Michael Fischer 2014-07-30
+1. Sean Clemmer 2014-07-30
+1. Brian Celenza 2014-07-31
+1. Sándor Ács MTA SZTAKI 2014-07-31
+1. Joe Rocklin 2014-07-31
+1. Arthur Huggard Cerner Corporation 2014-07-31
+1. Victor Lowther 2014-07-31
+1. Yoway Buorn 2014-07-31
+1. Travis Warlick 2014-07-31
+1. Nathan Cerny 2014-07-31
+1. Travis Warlick Travis Warlick LLC 2014-07-31
+1. Andrew Bennett 2014-07-31
+1. Matt Kasa 2014-08-01
+1. Mark Luntzel 2014-08-01
+1. Matt Kasa Granicus, Inc. 2014-08-01
+1. Derek Groh Texas A&M University 2014-08-01
+1. Raul Rangel 2014-08-01
+1. Sean Fisk 2014-08-01
+1. Nick Shemonsky 2014-08-01
+1. Jeremiah Snapp 2014-08-01
+1. Aaron Lane 2014-08-02
+1. Pieter Lazzaro 2014-08-03
+1. Aubrey Holland 2014-08-03
+1. Daniel O'Connor 2014-08-03
+1. Boris Komraz 2014-08-04
+1. H. "Waldo" Grunenwald 2014-08-04
+1. Peter Donald 2014-08-04
+1. Panagiotis Papadomitsos Splunk Inc. 2014-08-05
+1. Evan Sosenko OurTownRentals.com 2014-08-05
+1. Andrew Williams Intoximeters, Inc 2014-08-05
+1. Hart Hoover 2014-08-05
+1. Mike Tyler 2014-08-05
+1. Jennifer Davis 2014-08-05
+1. Andrew Williams 2014-08-05
+1. Christine Draper ThirdWave Insights LLC 2014-08-05
+1. Sam Marx 2014-08-05
+1. Jay Mundrawala 2014-08-06
+1. Daryl Robbins 2014-08-06
+1. Pierre Rambaud 2014-08-06
+1. Maciej Majewski 2014-08-06
+1. Patrick Wright 2014-08-06
+1. Bill Warner 2014-08-06
+1. John Ewart 2014-08-07
+1. Gabriel Mazetto 2014-08-07
+1. Ladislav Gazo Seges 2014-08-07
+1. Ricardo Signes 2014-08-07
+1. Christoph Mertz 2014-08-07
+1. Davide Cavarretta 2014-08-07
+1. Dominic Watson 2014-08-08
+1. Bradley Wangia 2014-08-08
+1. Dominic Watson Pixl8 Interactive Ltd 2014-08-08
+1. Russell Parks 2014-08-08
+1. Jason Vanderhoof Gazelle 2014-08-08
+1. Kazuki Saito 2014-08-09
+1. David Severski 2014-08-09
+1. Robert Northard 2014-08-09
+1. Virender Khatri 2014-08-09
+1. Aaron Wagoner 2014-08-10
+1. Eisuke Kuwahata 2014-08-11
+1. Oh Young Jooung 2014-08-11
+1. Alan Willis 2014-08-11
+1. Baptiste Courtois 2014-08-11
+1. Matthew Thode Rackspace Hosting, Inc. 2014-08-11
+1. Egor Medvedev 2014-08-11
+1. Matthew Thode 2014-08-11
+1. Joshua Kwan 2014-08-11
+1. lance lance.zhou 2014-08-11
+1. Paul Thomas 2014-08-11
+1. Mike Dillion 2014-08-12
+1. Ringo De Smet 2014-08-12
+1. Grzegorz Kołodziejczyk 2014-08-12
+1. Paul Turner 2014-08-12
+1. Chen Harel Takipi 2014-08-12
+1. Chen Harel 2014-08-12
+1. Thom May 2014-08-13
+1. Rick Saenz 2014-08-13
+1. Brendan O'Donnell 2014-08-13
+1. Jamie Winsor 2014-08-13
+1. Greg Palmier 2014-08-13
+1. Patrick Moore 2014-08-13
+1. Roman Kulayev 2014-08-13
+1. Brian DeFeyter 2014-08-13
+1. Brandon Burton 2014-08-14
+1. Jon San Miguel 2014-08-14
+1. Chase Bolt 2014-08-14
+1. Jean Mertz 2014-08-14
+1. David O Neill 2014-08-14
+1. Joel Smith 2014-08-14
+1. Capen Brinkley 2014-08-14
+1. Kevin Lamontagne De Marque 2014-08-14
+1. Andre Elizondo 2014-08-15
+1. Mathias Lafeldt 2014-08-15
+1. Miguel Ferreira 2014-08-15
+1. Michael Mior 2014-08-15
+1. Stan Chan 2014-08-15
+1. Koji Tanaka 2014-08-16
+1. Lennart Brinkmann 2014-08-16
+1. Mario Baricevic Web Man d.o.o. 2014-08-17
+1. Tom Robison 2014-08-17
+1. Mario Baricevic 2014-08-17
+1. Barthelemy Vessemont 2014-08-18
+1. Heavy Water Operations Heavy Water Operations, LLC. 2014-08-18
+1. Heavy Water Operations 2014-08-18
+1. Dave Eddy 2014-08-18
+1. Malte Swart 2014-08-18
+1. Giorgos Saslis 2014-08-18
+1. Brian Bianco 2014-08-19
+1. Thomas Massmann 2014-08-19
+1. Thomas Massmann 2014-08-19
+1. Noah Kantrowitz 2014-08-19
+1. Darshana Runwal Cerner Corporation 2014-08-19
+1. Stephen Sugden 2014-08-20
+1. Ben Snape 2014-08-20
+1. John Coleman 2014-08-21
+1. Miles Miller 2014-08-21
+1. Martin Forssen 2014-08-21
+1. Serhii Balbieko 2014-08-21
+1. Matt Barlow Rackspace 2014-08-22
+1. Dmitry Ryobryshkin 2014-08-22
+1. Peter Abbott 2014-08-24
+1. Cory Stephenson 2014-08-24
+1. Christopher Jerdonek 2014-08-25
+1. Nicolas Blanc 2014-08-26
+1. Jonathan Mickle 2014-08-26
+1. Simon Detheridge Widgit Software 2014-08-26
+1. Dieter Blomme 2014-08-26
+1. Seth Thomas 2014-08-26
+1. david karapetyan 2014-08-26
+1. Pete Crossley 2014-08-26
+1. Justin Locsei 2014-08-26
+1. Moshe Zvi 2014-08-26
+1. Ben Hartshorne 2014-08-26
+1. Dieter Blomme Picabit 2014-08-26
+1. Fabio Napoleoni 2014-08-27
+1. Charles Guenther Yelp Inc 2014-08-27
+1. Brian Cottingham 2014-08-27
+1. Sho Sawada Bitcellar, Inc. 2014-08-27
+1. Steve Jansen 2014-08-27
+1. Maurice Kherlakian 2014-08-27
+1. Peter Halliday 2014-08-27
+1. Amruta Krishna Ravavarapu 2014-08-27
+1. Craig Lewis 2014-08-27
+1. Rory Savage 2014-08-28
+1. Dave Molinari McKesson 2014-08-28
+1. Dave Molinari 2014-08-28
+1. Lennart Koopmann TORCH GmbH 2014-08-28
+1. Romain Cambien 2014-08-29
+1. Quentin Madec 2014-08-29
+1. Mevan Samaratunga 2014-08-29
+1. Sebastián Otaegui 2014-08-30
+1. Jose Ortiz 2014-08-31
+1. Doc Walker 2014-08-31
+1. Mitja Kleider 2014-08-31
+1. D flatterlight 2014-08-31
+1. Scott Bonds 2014-09-01
+1. Eugenio Marzo pronetics 2014-09-01
+1. Eugenio Marzo 2014-09-01
+1. David Aronsohn 2014-09-02
+1. Suleyman Kutlu 2014-09-02
+1. Petar Koraca 2014-09-02
+1. Mark deVilliers 2014-09-03
+1. Patrick Debus-Pesquet Numergy 2014-09-03
+1. David Workman 2014-09-03
+1. Tom Alexandrowicz 2014-09-03
+1. Scott Severtson 2014-09-03
+1. Brian Begy 2014-09-04
+1. Jay Zhu 2014-09-04
+1. David Chauviere 2014-09-04
+1. Jason Kulatunga 2014-09-04
+1. Yvo Vandoorn 2014-09-05
+1. Ben Scott 2014-09-05
+1. Christoph Maser 2014-09-05
+1. Lucas Mariani 2014-09-07
+1. Stanisław Tuszyński 2014-09-08
+1. Brandon Raabe 2014-09-08
+1. Chris Richard 2014-09-09
+1. Nick Dobson 2014-09-09
+1. Nick Dobson 2014-09-09
+1. Kieren Hynd 2014-09-09
+1. Christopher McClimans 2014-09-09
+1. Scott Marshall 2014-09-09
+1. Tim George EveryPay AS 2014-09-09
+1. Eric Pullen Appriss, Inc. 2014-09-09
+1. Jeffrey Baird 2014-09-10
+1. Guillaume Turri 2014-09-10
+1. Daniel Perez 2014-09-10
+1. Nathaniel Hoag 2014-09-10
+1. Daniel Perez Claude Tech 2014-09-10
+1. Brandon Robinson 2014-09-10
+1. Tom Scott TelVue Corporation 2014-09-11
+1. Frederico Araujo Omise.co 2014-09-11
+1. Frederico Araujo 2014-09-11
+1. Mars Ma 2014-09-11
+1. Nabeel Shahzad 2014-09-11
+1. Tom Scott 2014-09-11
+1. Romain Niccoli Criteo S.A. 2014-09-11
+1. Paul Ruiz 2014-09-11
+1. Alexander Myasnikov 2014-09-12
+1. Mark Luntzel 2014-09-12
+1. Taylor Ludwig 2014-09-13
+1. Todd Pigram 2014-09-13
+1. Nickolas Montgomery 2014-09-13
+1. Eric Shamow 2014-09-13
+1. Aaron Wartner 2014-09-14
+1. Tomokazu Takada 2014-09-14
+1. Karl Shrubb Expedia, Inc. 2014-09-15
+1. Felix Wong 2014-09-15
+1. Robert Berger 2014-09-16
+1. George Secillano 2014-09-16
+1. Johannes Plunien 2014-09-16
+1. Drew Oliner 2014-09-16
+1. Eric Heydrick 2014-09-17
+1. Jared Szechy 2014-09-17
+1. Petr Michalec 2014-09-17
+1. Benjamin Marsteau 2014-09-17
+1. James Bence 2014-09-17
+1. Stephan Linz 2014-09-19
+1. Endre Hirling 2014-09-19
+1. Paul Krohn 2014-09-19
+1. shaun mouton 2014-09-22
+1. Mike Tavares EBSCO 2014-09-22
+1. Raul Naveiras 2014-09-22
+1. Neven Burica 2014-09-22
+1. Antonio Peña 2014-09-22
+1. Olivier Bazoud 2014-09-23
+1. G Arends 2014-09-23
+1. Eric Saxby 2014-09-23
+1. Greg Osuri 2014-09-23
+1. Andrew Gale 2014-09-23
+1. William Watson 2014-09-24
+1. Jeremy Shipman 2014-09-24
+1. Pavel Danelian 2014-09-25
+1. Ryan Bowlby 2014-09-25
+1. Rob Chekaluk 2014-09-25
+1. Jonas Kongslund 2014-09-25
+1. Baptiste Courtois Criteo 2014-09-25
+1. George Secillano SAP 2014-09-26
+1. Greg Poirier 2014-09-26
+1. Muthukumar Jayaraman 2014-09-26
+1. Garett Shulman 2014-09-26
+1. George Secillano 2014-09-26
+1. Jonas Kongslund 2014-09-27
+1. James Hu 2014-09-27
+1. Shobhit Srivastava 2014-09-28
+1. Alex Glover 2014-09-29
+1. Trevor North 2014-09-29
+1. Christopher Gunther 2014-09-29
+1. Matias De Carli 2014-09-29
+1. Andrew Coulton inGenerator Ltd 2014-09-29
+1. Paul Dunnavant 2014-09-29
+1. Deni Chekirda 2014-09-30
+1. Justin Mazzi WWWH LLC 2014-09-30
+1. Karol Szuster 2014-09-30
+1. Ho-Sheng Hsiao 2014-09-30
+1. Dennis Hoer 2014-09-30
+1. Denis Chekirda 2014-09-30
+1. Christian Kaspar 2014-10-01
+1. Léo Unbekandt 2014-10-01
+1. Mal Graty 2014-10-01
+1. Simas Čepaitis 2014-10-02
+1. Daniel Hagerty 2014-10-02
+1. Michael Burns 2014-10-02
+1. Nellie Shamrell-Harrington 2014-10-03
+1. Ryan Larson 2014-10-03
+1. Greg Palmier RapidWorkspace 2014-10-04
+1. Alex Pop 2014-10-04
+1. Justin Witrick 2014-10-06
+1. Oleg Levy 2014-10-06
+1. Krzysztof Szarek 2014-10-06
+1. Peter Niederlag 2014-10-06
+1. Justin Witrick Rackspace 2014-10-06
+1. Joe Bergantine 2014-10-07
+1. Nicolas DUPEUX 2014-10-07
+1. Aurélien Léger 2014-10-07
+1. Benjamin Carpenter 2014-10-08
+1. Jun Nishikawa Monomono 2014-10-08
+1. Shinya Furuwata 2014-10-08
+1. Roger North-Row 2014-10-08
+1. Chris Fordham 2014-10-08
+1. Jonathan Hall 2014-10-09
+1. Nawaid Shamim 2014-10-09
+1. Richard Manyanza 2014-10-09
+1. Bernhard Weisshuhn 2014-10-09
+1. Doug Ireton Nordstrom, Inc. 2014-10-10
+1. Nowell Strite 2014-10-10
+1. Mathew Kamkar 2014-10-10
+1. Ryan Hass 2014-10-10
+1. Lemuel Formacil 2014-10-10
+1. Christopher Luciano 2014-10-12
+1. Takahiro Horikawa InfoLens, Inc. 2014-10-13
+1. John Brodie 2014-10-13
+1. Fernando Monserrat 2014-10-13
+1. Takahiro Horikawa 2014-10-13
+1. Thomas Boerger Webhippie 2014-10-13
+1. Takahiro Horikawa InfoLens, Inc. 2014-10-13
+1. Thomas Boerger 2014-10-13
+1. Tsukasa Sato 2014-10-13
+1. Isa Farnik 2014-10-14
+1. Fahd Sultan 2014-10-14
+1. Chris Fordham Xhost Australia 2014-10-14
+1. Fahd Sultan Simulmedia, Inc 2014-10-14
+1. Chris Fordham 2014-10-14
+1. Shinya Furuwata 2014-10-15
+1. Paul Dunnavant Mypaulie 2014-10-15
+1. Ciaran Farrell SUSE LINUX Products GmbH 2014-10-15
+1. Nicolas Szalay 2014-10-15
+1. masaki yamada 2014-10-15
+1. Shinya Furuwata None 2014-10-15
+1. Peter West 2014-10-15
+1. Shinya Furuwata 2014-10-15
+1. Maciej Pasternacki 2014-10-16
+1. Krzysztof Wilczynski 2014-10-16
+1. Shinya Furuwata 2014-10-16
+1. Anne Moroney 2014-10-16
+1. Marc Paradise 2014-10-16
+1. Colby Olson 2014-10-16
+1. Shawn Weitzel 2014-10-16
+1. Don Becker 2014-10-17
+1. Jordan Dea-Mattson Yahoo 2014-10-17
+1. Michael Schmidt 2014-10-17
+1. Matt Greensmith 2014-10-17
+1. John Schwinghammer 2014-10-17
+1. Jeff Goldschrafe 2014-10-18
+1. John Deatherage Juniper Networks, Inc. 2014-10-19
+1. Miguel Landaeta 2014-10-19
+1. John Deatherage 2014-10-19
+1. Kevin Lee 2014-10-20
+1. Jonathan Whitaker 2014-10-20
+1. Brian Stajkowski Bulletproof Networks 2014-10-20
+1. Brian Stajkowski 2014-10-20
+1. a a fd 2014-10-20
+1. a a 2014-10-20
+1. Mandeep Bal 2014-10-21
+1. Mandeep Bal 2014-10-21
+1. Norman Joyner Norman Joyner 2014-10-21
+1. Norman Joyner 2014-10-21
+1. Don L. 2014-10-21
+1. Jesus Gutierrez 2014-10-21
+1. Jesus Gutierrez 2014-10-21
+1. Misha Soliterman 2014-10-21
+1. Terry Fundak TJSoftworks 2014-10-21
+1. Terry Fundak 2014-10-21
+1. Prakash Palanisamy 2014-10-22
+1. Gavin Sandie Sneacho 2014-10-22
+1. Kevin Burnett 2014-10-22
+1. Ben Wright 2014-10-22
+1. Jan Mara 2014-10-22
+1. Sean Clemmer 2014-10-22
+1. Eugene Narciso 2014-10-23
+1. TADOKORO Saneyuki 2014-10-23
+1. Arne Sund 2014-10-24
+1. Dennis Konert 2014-10-24
+1. Alexander Merkulov 2014-10-24
+1. Mikael Henriksson 2014-10-24
+1. Clark Everetts 2014-10-25
+1. Christopher Coffey 2014-10-26
+1. Zhang zlcolin 2014-10-26
+1. Dan Fruehauf 2014-10-26
+1. cybermerc industries 2014-10-26
+1. Christoph Hartmann 2014-10-26
+1. Anuj Biyani 2014-10-28
+1. Chris Lundquist 2014-10-28
+1. Alex Falkowski 2014-10-28
+1. Lopaka Delp 2014-10-28
+1. Fabian Ruff 2014-10-29
+1. Javier Rodríguez 2014-10-29
+1. Justin Kolberg 2014-10-29
+1. Sean Cribbs 2014-10-29
+1. JD Isaacks 2014-10-29
+1. Dennis Klein 2014-10-29
+1. Tyler Cloke 2014-10-29
+1. Sylvain Tissot Ecodev 2014-10-30
+1. Jason Perry Brightcove, Inc 2014-10-30
+1. Deepak Sihag 2014-10-30
+1. Eric Feldhusen 2014-10-30
+1. Dustin Van Buskirk 2014-10-30
+1. Cloudrifles Chotiwattanapol 2014-10-30
+1. Deepak Sihag NA 2014-10-30
+1. Vincent Van Driessche 2014-10-31
+1. Nicholas Thompson 2014-10-31
+1. Steven De Coeyer 2014-11-02
+1. Steven De Coeyer Stack-up 2014-11-02
+1. Leo Unbekandt Scalingo 2014-11-03
+1. Amir Kadivar 2014-11-03
+1. Peter Kringle 2014-11-03
+1. Jeremiah Dabney 2014-11-04
+1. Dave Parfitt 2014-11-04
+1. Gary McLean Hall 2014-11-04
+1. Mark Anderson 2014-11-05
+1. Andreas Rayo Kniep 2014-11-05
+1. Christian Rodriguez 2014-11-05
+1. Noam Ben Ari 2014-11-05
+1. Mark Pimentel 2014-11-05
+1. Damian Gerow 2014-11-06
+1. Carlos Diaz 2014-11-10
+1. Michael Burns 2014-11-11
+1. Prajakta Purohit 2014-11-11
+1. Lance Andersen 2014-11-11
+1. Brent Walker 2014-11-11
+1. Vasiliy Tolstov 2014-11-11
+1. Ravi Bhure 2014-11-12
+1. Rahul Janghel Xpanxion 2014-11-12
+1. Carlos Diaz Covario 2014-11-12
+1. Norberto Lopes 2014-11-12
+1. Hiroki Mizuma 2014-11-12
+1. Jun Heider 2014-11-12
+1. Rahul Janghel 2014-11-12
+1. Biju Nair 2014-11-13
+1. Paolo Sechi 2014-11-13
+1. Daniel Barker 2014-11-13
+1. John Dewey 2014-11-13
+1. Joshua Kwan Schrodinger, Inc. 2014-11-14
+1. Krzysztof Krzyżaniak 2014-11-14
+1. Eric Swenson 2014-11-14
+1. Pradeep Mishra 2014-11-15
+1. Aditya Bhardwaj 2014-11-15
+1. Sam Martin 2014-11-15
+1. Ranjib Dey 2014-11-15
+1. Steve Clark 2014-11-16
+1. Alexander De Leon 2014-11-16
+1. Edmund Dipple 2014-11-17
+1. Emanuel Evans 2014-11-17
+1. Thomas Guinther 2014-11-17
+1. Sean Nolen 2014-11-17
+1. Sarah Kowalik 2014-11-17
+1. Kei Hirata 2014-11-17
+1. Yulian Kuncheff 2014-11-18
+1. Jihyung Song SK Planet 2014-11-18
+1. Lucy Wyman 2014-11-18
+1. Jason Potkanski 2014-11-18
+1. Julien Bianchi 2014-11-18
+1. Jihyung Song 2014-11-18
+1. Jeremy Miller Heartland Payment Systems 2014-11-18
+1. Julien Bianchi jubianchi.fr 2014-11-18
+1. Eric Citaire 2014-11-19
+1. Joseph Ryan 2014-11-19
+1. Eric Citaire Médiamétrie 2014-11-19
+1. Lane Maxwell Terra Firma Engineering, LLC 2014-11-19
+1. David Strauss Pantheon Systems, Inc. 2014-11-20
+1. David Strauss 2014-11-20
+1. Brian Felton 2014-11-21
+1. Chirag Jog Clogeny Technologies 2014-11-21
+1. Daniel Dreier 2014-11-21
+1. Andres Franco 2014-11-21
+1. Jason Williams DigiTar Inc. 2014-11-22
+1. Jason Williams 2014-11-22
+1. adam enger 2014-11-23
+1. william pink 2014-11-24
+1. Anduin Withers 2014-11-24
+1. Maciej Strzelecki 2014-11-24
+1. Geoffrey Tran 2014-11-24
+1. Michael Gilliland 2014-11-24
+1. Jacob McCann 2014-11-24
+1. Grant Ridder 2014-11-25
+1. Eric Greer Second Mind 2014-11-25
+1. Eric Greer 2014-11-25
+1. Mohit Dilawari Localytics 2014-11-25
+1. Pablo Gutiérrez del Castillo 2014-11-25
+1. Tarak Blah 2014-11-25
+1. Tigran Kavanosyan 2014-11-25
+1. Willington Vega 2014-11-26
+1. Timur Batyrshin 2014-11-26
+1. Moshe Katz 2014-11-26
+1. shuo zhang 2014-11-27
+1. l r 2014-11-29
+1. Luca Rovinetti 2014-11-29
+1. Yuu Yamashita 2014-11-29
+1. Scott Kinney 2014-12-02
+1. Benjamin Pillet 2014-12-02
+1. Peter Adkins 2014-12-03
+1. Roy Ratcliffe 2014-12-03
+1. Nitzan Raz 2014-12-04
+1. Lee Skillen 2014-12-04
+1. Dmitriy Bielorusov 2014-12-05
+1. Dmitriy Bielorusov Anerhan 2014-12-05
+1. Jamie Alessio 2014-12-06
+1. Kazuki Ota Treasure Data, Inc. 2014-12-06
+1. Sander van Zoest 2014-12-07
+1. Nikolay Murga 2014-12-08
+1. Jason Schadel 2014-12-08
+1. Brian Cajes 2014-12-08
+1. James Lovelace Yakara Ltd 2014-12-09
+1. Anjaneya Raju 2014-12-09
+1. sofer Athlan-Guyot 2014-12-09
+1. Cesare Bellini Yakara Ltd 2014-12-09
+1. R Southard CirrusMio 2014-12-10
+1. Christopher Coffey 2014-12-11
+1. Blair Hamilton 2014-12-12
+1. Steve Nolen 2014-12-13
+1. Szabolcs Zajdo 2014-12-13
+1. Martha Greenberg 2014-12-14
+1. denny dennyzhang 2014-12-14
+1. Connor Beckett 2014-12-15
+1. Grégoire Seux Criteo 2014-12-15
+1. Manea Eugen 2014-12-15
+1. Andrew Fleener 2014-12-16
+1. Nico Kadel-Garcia 2014-12-16
+1. Dmitri Toubelis 2014-12-16
+1. Michael Lewis 2014-12-17
+1. Yoway Buorn Sumo Logic 2014-12-18
+1. Roberto Moutinho 2014-12-19
+1. John Collier 2014-12-23
+1. Tom Parker 2014-12-25
+1. Sergiu Ionescu 2014-12-27
+1. Jaroslav Gorjatsev 2014-12-28
+1. Tatyana Arenburg 2014-12-29
+1. Dustin Collins Conjur 2014-12-31
+1. Dustin Collins 2014-12-31
+1. Trevor Bramwell 2014-12-31
+1. Nathaniel Michael 2015-01-02
+1. Andrew Christianson 2015-01-02
+1. Evangelos Pappas 2015-01-02
+1. Michael Mittelstadt 2015-01-02
+1. Lance Albertson 2015-01-04
+1. Jesse Hauf 2015-01-05
+1. PORTE Loïc PMSIpilot 2015-01-05
+1. Timothy Quinn Mad13 Entertainment 2015-01-06
+1. Rasmus Praestholm 2015-01-06
+1. Kota Shiratsuka INSANEWORKS,LLC 2015-01-06
+1. Edmund Haselwanter 2015-01-06
+1. Timothy Quinn 2015-01-06
+1. Alfonso Bocanegra de Luis 2015-01-07
+1. Yusuke Tanaka 2015-01-08
+1. Naotoshi Seo 2015-01-09
+1. Shinichi Morita 2015-01-09
+1. Adam Renberg 2015-01-09
+1. Diego Plentz 2015-01-10
+1. Diego Plentz Tautt 2015-01-10
+1. Jeffrey Hutchison 2015-01-11
+1. Jason Brody-Stewart-Stroeh 2015-01-11
+1. Chris Burroughs AddThis 2015-01-12
+1. Gerald Hevener 2015-01-14
+1. Chris Pernicano 2015-01-14
+1. Ben Vidulich 2015-01-14
+1. Brandon O'Connor 2015-01-14
+1. Brandon O'Connor DreamBox Learning, Inc. 2015-01-14
+1. Uchit Vyas 2015-01-15
+1. David O Neill Intel 2015-01-15
+1. Tim Nicholas 2015-01-15
+1. IT Adminstrator OptiShot Golf 2015-01-16
+1. Richard Henning 2015-01-16
+1. Thomas Orozco 2015-01-16
+1. IT Adminstrator 2015-01-16
+1. Tim Miller RightScale 2015-01-16
+1. Kai Sasaki 2015-01-18
+1. Richard Nixon 2015-01-18
+1. Nick Silkey 2015-01-19
+1. Daniel Givens 2015-01-19
+1. Kai Sasaki Yahoo Japan 2015-01-19
+1. Ovais Tariq 2015-01-19
+1. Ivan Suftin 2015-01-19
+1. Zachary Pallin 2015-01-19
+1. Matt Vollrath 2015-01-20
+1. I-Ming Chen 2015-01-20
+1. denny dennyzhang001 2015-01-21
+1. Barry Allard 2015-01-21
+1. William Albenzi 2015-01-21
+1. denny dennyzhang OSC Technologies, LLC 2015-01-21
+1. Ning Hin Lincoln Lee 2015-01-22
+1. Phil Schuler 2015-01-23
+1. Bill Moritz 2015-01-23
+1. Van Thoai Nguyen 2015-01-23
+1. Weihang Jian 2015-01-23
+1. Florian Breisch 2015-01-24
+1. Romain Vrignaud 2015-01-24
+1. Michael Proctor-Smith 2015-01-25
+1. Yi Ming Yin 2015-01-26
+1. Aniessh Sethh Aniessh Sethh 2015-01-28
+1. Mark jonson 2015-01-28
+1. Aniessh Sethh 2015-01-28
+1. Jonathan Bogaty 2015-01-30
+1. Neven Burica Motorcycle Industry Council 2015-01-30
+1. Andrew Kerr 2015-01-31
+1. Dan Herman 2015-01-31
+1. Aaron Lane 2015-02-02
+1. Helgi Thorbjoernsson Engine Yard, Inc. 2015-02-02
+1. Icinga Team Icinga 2015-02-02
+1. Icinga Team 2015-02-02
+1. William Salt 2015-02-02
+1. Stuart Preston 2015-02-02
+1. David Muto 2015-02-02
+1. Evan Gilman 2015-02-03
+1. Chris Kacerguis 2015-02-03
+1. David Muto pseudomuto 2015-02-03
+1. Joris van Lieshout 2015-02-04
+1. Matt Whiteley 2015-02-04
+1. Antoni Baranski 2015-02-04
+1. Igor Shpakov 2015-02-04
+1. Michael Glenney 2015-02-05
+1. Claudiu SONEL 2015-02-05
+1. Pierre-Luc Dion CloudOps Inc. 2015-02-07
+1. Sam Martin N/A 2015-02-07
+1. Ricardo Lupo 2015-02-07
+1. Pierre-Luc Dion 2015-02-07
+1. Christopher Lambacher 2015-02-08
+1. Sergey Storchay Magemaven 2015-02-09
+1. Erik van Brakel eVision Industry Software 2015-02-09
+1. Sergey Storchay PE 2015-02-09
+1. Sergey Storchay 2015-02-09
+1. Richard Grasshoff 2015-02-10
+1. Claudio Sanchez Tejeda 2015-02-10
+1. Cyril Scetbon 2015-02-11
+1. takaaki furukawa 2015-02-11
+1. Martijn van der Kleijn AIRFRANCE / KLM 2015-02-12
+1. Stefan Staudenmeyer 2015-02-12
+1. Seb Patane 2015-02-13
+1. Andy Glick 2015-02-13
+1. Dale Hui 2015-02-13
+1. Surya Avirneni 2015-02-14
+1. Zi Kang Gan 2015-02-17
+1. Pete Buletza Elyxor, Inc 2015-02-17
+1. Cedric Lamalle 2015-02-17
+1. Adolfo Pérez Álvarez 2015-02-17
+1. Scott Smith 2015-02-17
+1. Matt Williams 2015-02-18
+1. Nickolay Kovalev 2015-02-19
+1. Nickolay Kovalev 2015-02-19
+1. FedeX FedeX 2015-02-19
+1. Nickolay Kovalev Coderico 2015-02-19
+1. Nickolay Kovalev Coderico 2015-02-19
+1. kay tani-olu 2015-02-20
+1. Hemal Varambhia 2015-02-20
+1. Christopher Auston 2015-02-20
+1. Anthony Miller 2015-02-20
+1. Denis Barishev 2015-02-20
+1. kourosh parsa 2015-02-20
+1. Tatsuya Suzuki 2015-02-21
+1. Federico Castagnini 2015-02-21
+1. Jacob Minshall 2015-02-22
+1. Ranjib Dey PagerDuty 2015-02-23
+1. Drew Rapenchuk AdREM Systems 2015-02-24
+1. Drew Rapenchuk 2015-02-24
+1. Mike Juarez 2015-02-24
+1. Gavin Sandie 2015-02-24
+1. Ross Duggan Barricade Security Systems, Ltd. 2015-02-25
+1. Jeremy Przygode Stratalux, Inc. 2015-02-25
+1. Jeremy Przygode 2015-02-25
+1. Matt Eldridge 2015-02-25
+1. Alexander Dupuy 2015-02-26
+1. Zia Rahman 2015-02-26
+1. Sky Eyes 2015-02-27
+1. Julian Tabel 2015-02-27
+1. Vandad Chaharlengi 2015-02-27
+1. Sky Eyes TpT 2015-02-27
+1. Nellie Shamrell-Harrington Chef 2015-02-27
+1. Drew Blessing 2015-02-28
+1. Sunggun Yu 2015-02-28
+1. Christopher Zeeb 2015-03-01
+1. Michael Belt 2015-03-03
+1. Matthew Clifton Standard Edge 2015-03-04
+1. Rilindo Foster 2015-03-04
+1. Rudger Gravestein 2015-03-04
+1. Joe Heung 2015-03-04
+1. Ray Rodriguez 2015-03-04
+1. Surya Avirneni 2015-03-04
+1. Chris Gianelloni 2015-03-04
+1. Kyleen MacGugan 2015-03-04
+1. Joe Heung RevelOps Inc 2015-03-04
+1. Jean-Francois Bibeau 2015-03-05
+1. sai kiran skmothe Qwinix Technologies 2015-03-05
+1. Dennis Pattmann 2015-03-05
+1. Brian Smith 2015-03-05
+1. Josh Bradley 2015-03-05
+1. Ganesh Ramnarine 2015-03-05
+1. Martín Buceta 2015-03-06
+1. Nick Willever 2015-03-06
+1. Henrique Zambon 2015-03-06
+1. Bradley Steinfeld 2015-03-06
+1. Zhong YU Wu 2015-03-06
+1. Leons Petrazickis 2015-03-06
+1. Nilesh Londhe 2015-03-07
+1. Brian Goad 2015-03-07
+1. David Brown 2015-03-08
+1. Pavlo Dotsulenko 2015-03-08
+1. Max Sills Google, Inc. 2015-03-09
+1. Eiki Kanai 2015-03-09
+1. Maurizio PIllitu 2015-03-09
+1. Isao SHIMIZU 2015-03-09
+1. Holger Just 2015-03-09
+1. Daniel Paulus 2015-03-09
+1. Jason Musits 2015-03-09
+1. Nicholas Slowes 2015-03-09
+1. Kirt Fitzpatrick 2015-03-09
+1. Ann Brady 2015-03-09
+1. Daisuke HIGUCHI CREATIONLINE,INC. 2015-03-09
+1. Josh Dolitsky 2015-03-10
+1. HIROSE Masaaki 2015-03-10
+1. Blake Irvin yetu AG 2015-03-10
+1. Alex Derzhi 2015-03-10
+1. Simon Kaluza 2015-03-10
+1. General Counsel Dynamic Network Services Inc. 2015-03-11
+1. Pawan Verma Target Corporation 2015-03-11
+1. Sean Chon 2015-03-12
+1. Kirt Fitzpatrick 2015-03-12
+1. Michael Hedgpeth 2015-03-12
+1. Kim Dowling 2015-03-12
+1. Steve Lowe 2015-03-12
+1. Wenjun Huang 2015-03-13
+1. Benjamin Staffin 2015-03-13
+1. Jonathan Chapman 2015-03-13
+1. Jerrel Blankenship 2015-03-13
+1. William Drew 2015-03-13
+1. Andy Smith 2015-03-14
+1. Dave Parfitt 2015-03-15
+1. Shashi Shilarnav 2015-03-16
+1. Jose Palafox Cisco 2015-03-16
+1. Rob Pierucci 2015-03-16
+1. Shashi Shilarnav sshilarnav 2015-03-16
+1. Enrico Stahn 2015-03-17
+1. Benoit Plessis 2015-03-18
+1. sai kiran Mothe Qwinix Technologies 2015-03-18
+1. Tim Sogard 2015-03-18
+1. Rocky Olsen 2015-03-19
+1. Gene Wood 2015-03-19
+1. Kevin Van Wilder 2015-03-19
+1. Ryan Lee 2015-03-19
+1. Federico Castagnini Self 2015-03-19
+1. Adam Alboyadjian Vista Higher Learning, Inc. 2015-03-19
+1. Kaustubh Deorukhkar 2015-03-20
+1. Stephen Wright 2015-03-20
+1. Stephen Wright Cars.com 2015-03-20
+1. Ross McKinley 2015-03-20
+1. Alain Al 2015-03-21
+1. Zackery Nunez 2015-03-21
+1. Dominique Poulain left-left-lemma Oy 2015-03-22
+1. Megan Oliver Carbonite 2015-03-23
+1. Gary Waters 2015-03-23
+1. Mike Tougeron 2015-03-24
+1. Allan Beaufour Sum Labs, Inc 2015-03-24
+1. Thomas Hughes 2015-03-24
+1. Lloyd Dewolf 2015-03-24
+1. Jean-Baptiste Dalido Batch.com 2015-03-24
+1. Jean-Baptiste Dalido 2015-03-24
+1. Guillaume Plessis 2015-03-24
+1. Ilan Rabinovitch 2015-03-25
+1. Slava Kardakov 2015-03-26
+1. Bailey Belvis 2015-03-26
+1. Slava Roussakov 2015-03-26
+1. Jacob Ingram 2015-03-26
+1. Lingfu Zhang 2015-03-27
+1. William Jimenez 2015-03-27
+1. Anju Tiwari 2015-03-27
+1. Yusuke Goto 2015-03-28
+1. Maksim Chizhov 2015-03-28
+1. Miguel Fonseca Mindera 2015-03-30
+1. Dmytro Ilchenko 2015-03-30
+1. Dmytro Ilchenko 2015-03-30
+1. Miguel Fonseca 2015-03-30
+1. Szymon Szypulski 2015-03-30
+1. Aaron Walker 2015-03-31
+1. Shai Rosenfeld 2015-03-31
+1. Pedro Carriço 2015-03-31
+1. Pedro Carriço 2015-03-31
+1. Manuel Mazzuola 2015-03-31
+1. Matt Stanford 2015-03-31
+1. Igor Kanyuka 2015-03-31
+1. Rebekah Cha 2015-03-31
+1. Manuel Mazzuola ClouDesire SRL 2015-03-31
+1. vipul sharma self 2015-04-01
+1. Gerrit Tamboer 2015-04-01
+1. Rebekah Cha 2015-04-01
+1. Ian Kronquist 2015-04-02
+1. Tony ptQa 2015-04-02
+1. Topher Cullen 2015-04-02
+1. Michael Misshore 2015-04-02
+1. Yanick Duchesne 2015-04-02
+1. Volodymyr Babchynskyy 2015-04-02
+1. Michael Misshore Mudbug Media 2015-04-02
+1. Chris Chambers 2015-04-02
+1. William Fisher 2015-04-03
+1. Daniel Steen 2015-04-03
+1. Lance Albertson Oregon State University Open Source Lab 2015-04-03
+1. Chris Hammer 2015-04-04
+1. Brian Hays 2015-04-04
+1. Yoshiyuki Kinjo 2015-04-05
+1. Samuel Cassiba 2015-04-05
+1. Luke Reimer 2015-04-06
+1. Jeffrey Borcean 2015-04-06
+1. Kirill Kouznetsov 2015-04-06
+1. Timothy Heckman 2015-04-07
+1. Kristian Järvenpää 2015-04-07
+1. Christopher Potts 2015-04-07
+1. Jimmy Puckett SPINEN 2015-04-08
+1. Hrusikesh Panda 2015-04-08
+1. Francis Nicholas 2015-04-08
+1. Jimmy Puckett 2015-04-08
+1. Jerrel Blankenship 2015-04-08
+1. Florent Vilmart 2015-04-08
+1. Damian Pantano 2015-04-08
+1. Anthony Burns 2015-04-09
+1. Yves Blusseau 2015-04-10
+1. David Thornton Needle, Inc. 2015-04-10
+1. Vincent Theron 2015-04-10
+1. David Thornton 2015-04-10
+1. Tony Pitale 2015-04-11
+1. Claudiu Vădean 2015-04-11
+1. David Joos 2015-04-11
+1. WonGoo Lee 2015-04-12
+1. Jeff LaPlante 2015-04-12
+1. Sonny Garcia 2015-04-13
+1. John Mickey 2015-04-13
+1. Anthony Acquanita 2015-04-13
+1. Matt Jones 2015-04-13
+1. Simonas Kareiva 2015-04-14
+1. Shane Auckland 2015-04-15
+1. Jolyon Griffiths Calastone Ltd 2015-04-15
+1. Derek Rosenzweig 2015-04-15
+1. Andy Theuninck 2015-04-15
+1. Artur Melo 2015-04-15
+1. Kieren Evans 2015-04-15
+1. Rodrigo Avila 2015-04-16
+1. Brendan Germain 2015-04-16
+1. YOSUKE INOUE 2015-04-17
+1. Peter Gallagher 2015-04-17
+1. Andrew Bennett Pixid Design 2015-04-17
+1. Travis Davis 2015-04-17
+1. Richard Abdill 2015-04-21
+1. Tom Dooner 2015-04-22
+1. Thomas Geirhovd 2015-04-23
+1. Chris Van Heuveln 2015-04-23
+1. Daniel Bjorge 2015-04-23
+1. Trevor Hess 2015-04-23
+1. Charles Wimmer 2015-04-23
+1. Naoya Yoshizawa 2015-04-24
+1. Roman Vakulchik 2015-04-24
+1. Kavita Sachdeva 2015-04-24
+1. Tomasz Sętkowski 2015-04-24
+1. Trent Petersen 2015-04-24
+1. Alex Naumchenko Devloft Solutions Inc. 2015-04-27
+1. Alex Naumchenko 2015-04-27
+1. Johnathan Kupferer 2015-04-27
+1. Julien Pervillé Mingalar SAS 2015-04-28
+1. Craig keyrage 2015-04-28
+1. Remi Hakim 2015-04-28
+1. Rachel Adler 2015-04-28
+1. Joel Handwell 2015-04-28
+1. Barthelemy Vessemont 2015-04-28
+1. Andrey Rotchev 2015-04-29
+1. James Denman 2015-04-29
+1. Robert Castelle 2015-04-29
+1. James Denman Latta Partners 2015-04-30
+1. John Smyth 2015-04-30
+1. Rex Cerbas 2015-04-30
+1. Greg Karékinian 2015-04-30
+1. James Denman 2015-04-30
+1. Richard Guin 2015-04-30
+1. James Denman 2015-04-30
+1. Scott Saad 2015-04-30
+1. Giovanni Toraldo ClouDesire 2015-04-30
+1. John Frykland 2015-05-01
+1. Seth Chisamore CHEF 2015-05-01
+1. Kristin Triolo Hewlett-Packard 2015-05-01
+1. Eiki Kanai 2015-05-01
+1. Pascale-Andrée Audet 2015-05-01
+1. Anthony Acquanita acquantia.me 2015-05-01
+1. Dhawal Patel Nordstrom 2015-05-01
+1. Dian Hansen Hewlett-Packard 2015-05-01
+1. Radu Munteanu 2015-05-03
+1. Stephan Kemper ViaSat, Inc. 2015-05-04
+1. Alex Dunn 2015-05-04
+1. Jonathan Lassoff 2015-05-04
+1. Nikolay Kuzin Light Side 2015-05-05
+1. Michael Burns 2015-05-05
+1. Michael Burns 2015-05-05
+1. Nikolay Kuzin 2015-05-05
+1. Cheng-Dae Choe 2015-05-07
+1. Eric Rutherford 2015-05-07
+1. Daniel Condomitti 2015-05-07
+1. John Lemp 2015-05-07
+1. Joshua Glass Chef 2015-05-08
+1. Marcus Nilsson 2015-05-08
+1. Stefan Novak 2015-05-08
+1. James Johnson 2015-05-09
+1. Philipp Hellmich 2015-05-10
+1. Phillip Roberts 2015-05-11
+1. Fraser Scott 2015-05-11
+1. Phillip Roberts Ingenium Group, LTD 2015-05-11
+1. Simao Mata 2015-05-12
+1. Derek Belrose 2015-05-12
+1. Tobin Quadros 2015-05-12
+1. Tobin Quadros 2015-05-12
+1. Daniel Ben-Zvi 2015-05-12
+1. Kavita Sachdeva self 2015-05-12
+1. Jo Rhett 2015-05-12
+1. Gabriel Rosendorf 2015-05-12
+1. Biswajit Das 2015-05-12
+1. Eric Renfro Linux-Help.org 2015-05-13
+1. Eric Renfro 2015-05-13
+1. Mark Keisler 2015-05-13
+1. joe miller 2015-05-13
+1. Dan Baggott 2015-05-14
+1. John Maguire 2015-05-14
+1. Alaa Qutaish 2015-05-14
+1. Taylor Price 2015-05-14
+1. Justin Seubert 2015-05-18
+1. Jeremy Fleischman 2015-05-18
+1. Vianney Foucault 2015-05-19
+1. Leonardo Lima 2015-05-20
+1. Richard Barkley 2015-05-20
+1. ChangZhuo Chen 2015-05-21
+1. Yoshi Spendiff 2015-05-21
+1. Davida Marion 2015-05-21
+1. Vladimir Skubriev 2015-05-21
+1. Kouhei Sutou 2015-05-21
+1. Eric Reeves Alert Logic, Inc. 2015-05-22
+1. Jeff Arcuri Gap Inc 2015-05-22
+1. Vianney Foucault 2015-05-22
+1. spencer owen 2015-05-22
+1. Rushin Panchal Resivaa Technologies Pvt LTD 2015-05-22
+1. John Norden Alert Logic 2015-05-23
+1. Franklin Hanson 2015-05-23
+1. Chris Marchesi PayByPhone Technologies 2015-05-25
+1. Michael Michael 2015-05-26
+1. Brad Ison 2015-05-26
+1. Richard Lock University of Derby 2015-05-27
+1. Richard Lock 2015-05-27
+1. take ookubo 2015-05-27
+1. David Macpherson 2015-05-28
+1. Dave Parfitt 2015-05-28
+1. Greg Durham Chef 2015-05-28
+1. Karen Bruner 2015-05-29
+1. Scott McGillivray 2015-05-29
+1. Mikhail Priver 2015-05-29
+1. Tim Terhorst 2015-05-31
+1. Olivier Lemasle 2015-05-31
+1. Edward Bartholomew 2015-05-31
+1. Larry Wright 2015-06-01
+1. Jacob Bell 2015-06-01
+1. Yeung Siu Citrix Systems, Inc. 2015-06-02
+1. Eric Tucker Blue Spurs 2015-06-02
+1. Aiman Alsari 2015-06-03
+1. Eduardas Jarusevicius 2015-06-03
+1. PaiCheng Tao 2015-06-03
+1. Martin Chlumsky 2015-06-04
+1. Jonathan Amiez 2015-06-04
+1. Zach Malone 2015-06-05
+1. Ishu Gupta 2015-06-05
+1. Christopher Dickey 2015-06-05
+1. pingdecopong pingdecopong 2015-06-05
+1. Michael O'Connor 2015-06-07
+1. Anirban Saha 2015-06-07
+1. Brent Montague 2015-06-08
+1. Jacob Royal 2015-06-08
+1. Craig Bookwalter 2015-06-08
+1. Anton Koldaev 2015-06-08
+1. Miroslav Svoboda 2015-06-08
+1. Adam Paxton 2015-06-08
+1. Kaushik Chandrashekar 2015-06-09
+1. Dan Varga 2015-06-09
+1. Matt Wormley Myself 2015-06-09
+1. Chris Knight hevnly 2015-06-10
+1. Ramez Mourad Cvent 2015-06-10
+1. Chris Knight 2015-06-10
+1. Etienne Lafarge 2015-06-10
+1. Chris Knight 2015-06-10
+1. Nick Hudacin 2015-06-10
+1. Ganesh Nalawade 2015-06-11
+1. Simar Kalra 2015-06-11
+1. Benjamin Pellittieri 2015-06-12
+1. Patrick Connolly 2015-06-12
+1. Torsten Schmits 2015-06-12
+1. Lance Powell 2015-06-13
+1. Antonio Terceiro 2015-06-14
+1. Uldis Sturms 2015-06-14
+1. John Kerry 2015-06-14
+1. Paul Brown 2015-06-15
+1. Matt Kantor 2015-06-15
+1. Benjie Godfrey 2015-06-15
+1. Adam Kerr 2015-06-16
+1. Jonathan Serafini Jonathan Serafini 2015-06-16
+1. Michael Villis 2015-06-16
+1. Adam Linkous 2015-06-17
+1. Michael Wheeler 2015-06-17
+1. David Bernick 2015-06-18
+1. Ilya Katz 2015-06-18
+1. Matthew Johnson 2015-06-18
+1. Ilya Katz ExecOnline 2015-06-18
+1. Maximilian Fischer 2015-06-18
+1. Michael Hedgpeth NCR 2015-06-18
+1. Jean-Francois Mathiot ServeBox SAS 2015-06-19
+1. Abhijit Hiremagalur 2015-06-19
+1. Sebastian Boehm 2015-06-22
+1. dongha kim neowiz games 2015-06-22
+1. Christopher Chow 2015-06-22
+1. Matthew Campbell 2015-06-22
+1. Adam DePue 2015-06-23
+1. Nick Ramirez 2015-06-23
+1. Mark Cornick 2015-06-23
+1. Ryan Lewon 2015-06-23
+1. Daniel Piessens 2015-06-24
+1. Bernhard Ott 2015-06-24
+1. Maxime FOUILLEUL 2015-06-24
+1. Wesley Schwengle 2015-06-24
+1. Jeff Wood 2015-06-24
+1. yusuke abe newsdict 2015-06-25
+1. Igor Kurochkin Express42 2015-06-25
+1. Emmanuel Iturbide 2015-06-25
+1. Kevin Kennedy 2015-06-25
+1. Igor Kurochkin 2015-06-25
+1. yusuke abe 2015-06-25
+1. Emmanuel Iturbide SAP 2015-06-25
+1. Jean Baptiste Favre 2015-06-26
+1. Peer Allan 2015-06-26
+1. Mat Davies 2015-06-26
+1. Kevin Dickerson 2015-06-30
+1. Kevin Dickerson Loom Technology 2015-06-30
+1. Nirmal Jonnalagedda Spanning Cloud Apps 2015-06-30
+1. jay hendren 2015-06-30
+1. Nirmal Jonnalagedda 2015-06-30
+1. Matt Darby 2015-06-30
+1. John Ramos DualSpark 2015-07-01
+1. Andy Paroff 2015-07-02
+1. Pavlos Ratis 2015-07-02
+1. Quentin de Metz 2015-07-02
+1. Nobuhiro Nikushi 2015-07-03
+1. Andrei Ivanov 2015-07-03
+1. Pavel Bobov 2015-07-05
+1. Michael Klishin 2015-07-06
+1. Roger Levy MariaDB USA 2015-07-07
+1. Antoni Baranski 2015-07-08
+1. Dale Neufeld Shopify Inc 2015-07-08
+1. Sarath kumar 2015-07-08
+1. Russell Whitaker 2015-07-08
+1. Marek Dwulit 2015-07-09
+1. Michael Thomas 2015-07-09
+1. Christopher Bourez 2015-07-09
+1. David Bresson 2015-07-09
+1. Stefano Rivera Yola 2015-07-09
+1. Wolf Noble 2015-07-10
+1. Hector Castro 2015-07-10
+1. Robert Rehberg 2015-07-10
+1. Chitrang Jain 2015-07-10
+1. Aeri Iamsirithaworn 2015-07-10
+1. Ceaser Larry Divergent Logic 2015-07-11
+1. Ceaser Larry 2015-07-11
+1. Pieter Vogelaar 2015-07-12
+1. Eric Internicola 2015-07-12
+1. DalHo Park 2015-07-12
+1. Jacob Rosenberg 2015-07-12
+1. Kamesh Sampath 2015-07-13
+1. Lachlan Munro 2015-07-13
+1. James Hu Roboheart, Inc. 2015-07-14
+1. Andreas Lappe 2015-07-14
+1. Nuo Yan 2015-07-14
+1. Ben Barnett 2015-07-14
+1. Marek Dwulit 2015-07-15
+1. Jeff Dawes 2015-07-15
+1. Sriram Rajan 2015-07-15
+1. Josh Schneider 2015-07-15
+1. Katherine Rossiter 2015-07-16
+1. Dan Stark 2015-07-17
+1. Ben Dang 2015-07-18
+1. Michal Knapik 2015-07-18
+1. Anthony Moore 2015-07-19
+1. Patrick Freeman 2015-07-19
+1. Jimmy McCrory 2015-07-19
+1. Derek Haynes Scoutapp 2015-07-20
+1. Oswald De Riemaecker Continuous S.A. 2015-07-20
+1. Nuno Pereira 2015-07-20
+1. Martin Samuel 2015-07-20
+1. Jorge Quilcate 2015-07-20
+1. Shahul Hameed Khajamohideen 2015-07-20
+1. Jim Ryan Room 118 Solutions, Inc. 2015-07-21
+1. Jim Ryan 2015-07-21
+1. Adam Balali 2015-07-23
+1. Francesco Stradiotti 2015-07-23
+1. Eddy Boscolo 2015-07-24
+1. Mohit Gupta 2015-07-24
+1. Justin Ryan 2015-07-24
+1. Patrick Schnetger 2015-07-27
+1. Benoît Créau 2015-07-27
+1. Marshall Levin 2015-07-27
+1. Alexander De Leon Devialab 2015-07-28
+1. Charles Lynch 2015-07-28
+1. Alexander De Leon 2015-07-28
+1. Heather Mickman Target Corporation 2015-07-29
+1. Marco Gallotta Asana 2015-07-29
+1. Washington Botelho 2015-07-29
+1. Salim Alam Chef Inc. 2015-07-29
+1. Alvaro Morales 2015-07-29
+1. J.R. Mash 2015-07-30
+1. Jason Barnett 2015-07-30
+1. Michael Kelley 2015-07-30
+1. Jacob Dearing 2015-08-01
+1. Lennart Weijl 2015-08-01
+1. Giovanni Del Valle 2015-08-01
+1. Tommy Odom 2015-08-02
+1. Dale Cook 2015-08-04
+1. Zack Zondlo 2015-08-04
+1. Vincent VAN HOLLEBEKE 2015-08-04
+1. Vincent Stammegna CSAA IG 2015-08-05
+1. Vincent Stammegna 2015-08-05
+1. Ed Powell Digital River 2015-08-06
+1. matthew patton 2015-08-06
+1. Ed Powell 2015-08-06
+1. Ed Powell Ed Powell 2015-08-06
+1. Sean Hardy Expedia Affiliate Network 2015-08-07
+1. Amulya Sharma 2015-08-09
+1. Luc Francesca 2015-08-09
+1. ABE Satoru 2015-08-10
+1. Francois-Joseph GRIMAULT 2015-08-10
+1. Renan Vicente Gomes da Silva 2015-08-11
+1. Alex Dunn Tacit Knowledge 2015-08-11
+1. Steven Swor 2015-08-11
+1. Ron Russell 2015-08-12
+1. Alain Chiasson 2015-08-12
+1. Gabor Szelcsanyi Layer7 Cache 2015-08-13
+1. Felipe Bessa Coelho 2015-08-13
+1. Kurt Langrud The Buckle, Inc 2015-08-13
+1. Gabor Szelcsanyi 2015-08-13
+1. Leandro Di Tommaso 2015-08-13
+1. Sergey Tuchkin 2015-08-17
+1. David Giesberg 2015-08-17
+1. Francis Nicholas Expedia 2015-08-18
+1. Rafael Fonseca 2015-08-18
+1. Scott Gorsuch 2015-08-18
+1. Dmitry Grigorenko 2015-08-18
+1. Federico Castagnini 2015-08-19
+1. Peter Mooshammer 2015-08-19
+1. Ian Duffy 2015-08-19
+1. David Echols 2015-08-19
+1. James Augustine 2015-08-20
+1. Juan Manuel Rodulfo Salcedo 2015-08-20
+1. Sarah Michaelson 2015-08-20
+1. St. Isidore Seville 2015-08-21
+1. Matthieu Roisil 2015-08-21
+1. Benjamin Jansen 2015-08-21
+1. Chris Jones 2015-08-22
+1. Tarun Jangra 2015-08-22
+1. Thibaut Notteboom 2015-08-24
+1. Alain Dejoux 2015-08-24
+1. Carl Perry 2015-08-24
+1. Andrés F Vargas 2015-08-24
+1. Mark Matten 2015-08-24
+1. Mihai Petracovici 2015-08-25
+1. Simon Thompson Imagination Technologies Ltd. 2015-08-27
+1. Mateusz Kwiatkowski Serveraptor 2015-08-27
+1. Jeff Zohrab 2015-08-28
+1. Gregory McCue Ecosia GmbH 2015-08-28
+1. Jeff Zohrab 2015-08-31
+1. Alex Howells Aeode 2015-08-31
+1. Alex Howells 2015-08-31
+1. Mathew Goldsborough Forever Oceans Corporation 2015-09-01
+1. Mathew Goldsborough 2015-09-01
+1. james dupont 2015-09-03
+1. Damian Pantano 2015-09-03
+1. Anton Yushkov Flugel.it 2015-09-04
+1. Marky Jackson 2015-09-04
+1. Shawn Neal 2015-09-05
+1. Leo Popov 2015-09-07
+1. Diego Felix de Almeida 2015-09-08
+1. Diego Felix de Almeida Diego 2015-09-08
+1. Ruchi Goyal 2015-09-08
+1. Vassilis Rizopoulos 2015-09-08
+1. Sarath kumar TechM 2015-09-08
+1. Sergey Permyakov 2015-09-09
+1. Tarak Blah Tarak Blah 2015-09-10
+1. Thorsten Kahler 2015-09-10
+1. Jonathan Hutchins 2015-09-10
+1. Stanislav Voroniy 2015-09-10
+1. Justin Early 2015-09-11
+1. Justin Early Alert Logic 2015-09-11
+1. Daniel Wendorf 2015-09-13
+1. Tony Flint 2015-09-14
+1. Salvatore Poliandro 2015-09-15
+1. Mikhail Zholobov 2015-09-15
+1. Vandad Chaharlengi CustomInk 2015-09-15
+1. Jason Boeshart 2015-09-15
+1. Morgan Nelson 2015-09-17
+1. David Chaiken 2015-09-18
+1. Roger Delph 2015-09-19
+1. Florent Flament 2015-09-20
+1. Elad Dolev 2015-09-20
+1. Nate Schmoll 2015-09-20
+1. Matt Jones 2015-09-21
+1. Soeren Unruh Humance AG 2015-09-21
+1. Mikko Caldara 2015-09-21
+1. Nathan Sullivan 2015-09-21
+1. Joshua Hudson 2015-09-21
+1. Kevin MA 2015-09-22
+1. MA Goacid 2015-09-22
+1. Karsten Richter 2015-09-22
+1. Jose Olcese 2015-09-22
+1. Greg Swallow 2015-09-23
+1. Paul McCarthy 2015-09-23
+1. Jordan Tardif 2015-09-23
+1. Mikhail Bautin 2015-09-24
+1. Andrew Mulholland 2015-09-24
+1. Michael Pereira 2015-09-25
+1. J. Lieberthal 2015-09-26
+1. Jonathan Serafini 2015-09-26
+1. Edmund Rhudy 2015-09-26
+1. Adam Edwards 2015-09-26
+1. Makoto Terada 2015-09-26
+1. Glen Mailer 2015-09-28
+1. Francisco Alcantara 2015-09-28
+1. Joshua Burt 2015-09-28
+1. Thomas Pike Crossroads Foundation Limited 2015-09-29
+1. Robert Kidd 2015-09-29
+1. Thomas Pike 2015-09-29
+1. Hugo Sato 2015-09-29
+1. Michael Nairn 2015-09-30
+1. Greg Albrecht Orion Labs, Inc. 2015-09-30
+1. david raistrick 2015-09-30
+1. Brett Gailey 2015-10-01
+1. David Kinzer 2015-10-02
+1. Ketan Padegaonkar 2015-10-02
+1. Derek Ditch Cyber Dev Team 2015-10-02
+1. Derek Ditch 2015-10-02
+1. Thiago Dias de Francisco 2015-10-02
+1. Andrew Jones 2015-10-02
+1. Thiago Francisco FCL 2015-10-02
+1. Paul Welch 2015-10-05
+1. Andrey Scopenco 2015-10-05
+1. Adam Wilbraham 2015-10-06
+1. Patrick Wright Chef 2015-10-06
+1. Tony Flint Marchex, Inc. 2015-10-06
+1. Samuel Bernard S4M 2015-10-07
+1. Jeff Harvey-Smith 2015-10-07
+1. Bill St. Clair 2015-10-07
+1. G.J. Moed 2015-10-07
+1. Patrick McMorran 2015-10-07
+1. Jeremy Maziarz 2015-10-08
+1. Pete Navarra 2015-10-09
+1. Peter Abbott Myself 2015-10-10
+1. Henry Finucane 2015-10-11
+1. Michael Leyshon 2015-10-12
+1. Cameron Walker 2015-10-12
+1. Tyler Ball 2015-10-12
+1. Jos Backus 2015-10-13
+1. Nolan Davidson PhishMe 2015-10-13
+1. Joshua Timberman 2015-10-13
+1. Anthony Robinson 2015-10-13
+1. Ronald Chmara 2015-10-14
+1. Mike Morraye 2015-10-14
+1. Yossi Gottlieb 2015-10-14
+1. Wade Peacock 2015-10-15
+1. Dayne Broderson 2015-10-16
+1. Kyle Burckhard 2015-10-16
+1. Eugene Bolshakov 2015-10-16
+1. Arnoud Vermeer 2015-10-16
+1. Vito Laurenza 2015-10-16
+1. Dan Buch 2015-10-17
+1. Clinton Wolfe 2015-10-17
+1. Dan Buch Travis CI GmbH 2015-10-17
+1. Mike Simons Hewlett Packard Enterprise 2015-10-19
+1. Andy Cui 2015-10-19
+1. Ryan Frantz 2015-10-19
+1. Rocky Olsen 2015-10-20
+1. Ken Gotoh 2015-10-21
+1. Erik Rogneby 2015-10-21
+1. Michael Roy 2015-10-23
+1. Jason Harley 2015-10-23
+1. Ryan Gerstenkorn 2015-10-23
+1. Michael Roy mikeroySoft 2015-10-23
+1. Jude Job 2015-10-23
+1. Olivier Dolbeau 2015-10-26
+1. Dmitry Batiievskyi 2015-10-26
+1. Eric Guinois 2015-10-27
+1. Jason McIntosh 2015-10-27
+1. Jon A 2015-10-27
+1. Rami Abdulhussein 2015-10-27
+1. Hleb Valoshka 2015-10-27
+1. Michael Miko 2015-10-28
+1. James Flemer NDP, LLC 2015-10-28
+1. Jaycee Bernardino 2015-10-28
+1. James Flemer 2015-10-28
+1. jez wain 2015-10-28
+1. Priyal Jain 2015-10-28
+1. Alex Vinyar chef 2015-10-29
+1. Vipin V Gopal 2015-10-29
+1. Bryant Lippert 2015-10-29
+1. John Mickey 2015-10-30
+1. John Manero 2015-10-30
+1. Arnold Visser None 2015-11-02
+1. Arnold Visser 2015-11-02
+1. Jens Segers 2015-11-03
+1. David Tagarro 2015-11-03
+1. Emmanuel Guérin 2015-11-03
+1. alex vinyar chef 2015-11-04
+1. Adam Sulik 2015-11-04
+1. Saibal Dey 2015-11-04
+1. Rui Covelo 2015-11-04
+1. Robert Perry Lodestone Technologies, LLC 2015-11-04
+1. Kyle West 2015-11-04
+1. Taliesin Sisson 2015-11-05
+1. Goran Stankovski LimePoint Pty Ltd 2015-11-05
+1. Bryan Crossland 2015-11-07
+1. Phil Ruffwind 2015-11-08
+1. Sam Rudge 2015-11-08
+1. Evan Tschuy 2015-11-11
+1. Timothy Schroeder 2015-11-11
+1. Louis GOUNOT 2015-11-11
+1. James Pruetting 2015-11-11
+1. David Echols 2015-11-12
+1. Tom Wilson 2015-11-12
+1. echohack . echohack 2015-11-12
+1. Chris Jones Bloomberg 2015-11-14
+1. Roger Micone 2015-11-14
+1. Sasha Gerrand 2015-11-15
+1. Chris Marchesi 2015-11-15
+1. Heinz Gies 2015-11-16
+1. Sam Dunne AIB 2015-11-16
+1. Mario Harvey 2015-11-16
+1. Michael Miko 2015-11-17
+1. Corey Hemminger NativeX Holdings LLC 2015-11-17
+1. Gary Bright 2015-11-17
+1. Michael Miko 2015-11-17
+1. Chris Campbell 2015-11-17
+1. Corey Hemminger 2015-11-17
+1. Derek Bromenshenkel 2015-11-17
+1. Phil Smith 2015-11-17
+1. William Rowe 2015-11-17
+1. Simon Gate 2015-11-18
+1. David Meekin 2015-11-18
+1. Justin Seubert 2015-11-19
+1. Robert Moody 2015-11-19
+1. cristiano balducci 2015-11-19
+1. Piyatad Chotiwattanapol Cloudrifles 2015-11-20
+1. Alain De Carolis 2015-11-20
+1. Luke Young 2015-11-20
+1. Piyatad Chotiwattanapol 2015-11-20
+1. Piyatad Chotiwattanapol 2015-11-20
+1. Cloudrifles Chotiwattanapol 2015-11-20
+1. Rocky Olsen Dell SecureWorks 2015-11-20
+1. Taufek Johar 2015-11-22
+1. Taufek Johar Taufek 2015-11-22
+1. Justin Ellison 2015-11-23
+1. Edward Bosher 2015-11-24
+1. Jose Ventura 2015-11-24
+1. Jennifer Brown 2015-11-24
+1. William Drew 2015-11-25
+1. Danny Roberts 2015-11-25
+1. Guillaume Jacquet 2015-11-25
+1. Justin Spies BuyerQuest, Inc. 2015-11-25
+1. Justin Spies 2015-11-25
+1. John Dewey Cisco Systems 2015-11-28
+1. Kamil Forys 2015-11-30
+1. Federico Bucchi 2015-11-30
+1. Yuu Yamashita 2015-12-01
+1. Jesse Lee 2015-12-01
+1. Steve Marshall 2015-12-02
+1. Artem Sidorenko 2015-12-02
+1. Gregory Symons 2015-12-02
+1. Nerijus Bendziunas 2015-12-03
+1. Jason Summerour The Weather Companies 2015-12-03
+1. Robert Tarrall 2015-12-04
+1. Chad Brown 2015-12-04
+1. Richard Morrisey 2015-12-04
+1. Mat Schaffer 2015-12-05
+1. Jeff Powell 2015-12-07
+1. Eiki Kanai 2015-12-07
+1. Alexei Smirnov 2015-12-07
+1. Simon Bang Terkildsen Monsenso 2015-12-07
+1. Simon Terkildsen Monsenso 2015-12-07
+1. Simon Terkildsen 2015-12-07
+1. Ryan Chipman 2015-12-08
+1. Julian Tabel 2015-12-08
+1. Tim B 2015-12-08
+1. James Legg 2015-12-09
+1. Stephan Linz 2015-12-10
+1. Peter Halliday Formatron 2015-12-10
+1. Peter Halliday 2015-12-10
+1. Paul Bernard Fidelity Investments 2015-12-10
+1. Xavier Krantz 2015-12-10
+1. Adam Phillabaum 2015-12-11
+1. Fabricio Correa Duarte 2015-12-12
+1. Wojciech Sciesinski 2015-12-14
+1. Martin Fenner DataCite 2015-12-14
+1. Michal Taborsky 2015-12-14
+1. sadfsafsda thomas 2015-12-15
+1. Justin Nauman 2015-12-15
+1. Stephan Renatus 2015-12-16
+1. Sean Walberg 2015-12-17
+1. didier Belot 2015-12-17
+1. Mikhail Zholobov Parallels 2015-12-17
+1. Andrei Skopenko Parallels 2015-12-17
+1. Ioannis Christodoulou 2015-12-17
+1. Mikhail Zholobov 2015-12-17
+1. Phil Grayson 2015-12-18
+1. Robert Ulejczyk 2015-12-18
+1. Benoît Créau 2015-12-18
+1. Emir Ibrahimbegovic 2015-12-19
+1. Robert Vežnaver 2015-12-20
+1. Cash Weaver 2015-12-21
+1. Stefan Hojer 2015-12-21
+1. Robert Kidd 2015-12-22
+1. Ryan Hitchcock 2015-12-22
+1. Thomas Cate 2015-12-22
+1. Bakh Inamov 2015-12-24
+1. Ian Henry 2015-12-24
+1. Karl Norby 2015-12-30
+1. Kevin Park Wavefront, Inc. 2015-12-30
+1. Matteo Giaccone 2015-12-30
+1. Scott Weldon 2015-12-30
+1. Steven Smith 2015-12-31
+1. Charlie Andrews 2016-01-01
+1. Michallis Pashidis Trust1Team 2016-01-01
+1. Greg Nilchee 2016-01-04
+1. Jose Mora 2016-01-04
+1. Durant Damien 2016-01-05
+1. Wojciech Guszpit 2016-01-05
+1. Matthieu Mota 2016-01-05
+1. Jeffrey Hulten 2016-01-05
+1. Viktor LOOSZ 2016-01-06
+1. Ben Dang Yahoo 2016-01-07
+1. Ian Penney Nulogy Corporation 2016-01-07
+1. Joe Armstrong 2016-01-07
+1. Andrei Krasnitski 2016-01-08
+1. Levi Smith 2016-01-08
+1. James Reichley 2016-01-08
+1. Aiman Alsari Salmon 2016-01-08
+1. Brian Menges 2016-01-08
+1. Jun Sakata 2016-01-08
+1. Robert Szebtmihalyi 2016-01-08
+1. Sebastian Cruz 2016-01-09
+1. Brian Walker 2016-01-10
+1. Mercado pmercado 2016-01-10
+1. Gaurish Sharma 2016-01-10
+1. Julien Berard 2016-01-11
+1. John Bartko 2016-01-11
+1. Javi Perez-Griffo 2016-01-12
+1. Alex Bligh Flexiant Ltd 2016-01-12
+1. Mercado pmercado Flexiant 2016-01-12
+1. Alexey Melezhik 2016-01-12
+1. Joe McIlvain 2016-01-13
+1. Ross Kusler 2016-01-13
+1. Jeffrey James 2016-01-14
+1. Daniel Rich 2016-01-14
+1. Jeffrey James COSProfessionals, LLC 2016-01-14
+1. Kevin Bonner 2016-01-15
+1. Andrew Burns 2016-01-15
+1. René Martín Rodríguez 2016-01-16
+1. Philip Watts 2016-01-18
+1. Philip Watts REĀN Cloud Solutions LLC 2016-01-18
+1. Fabricio Correa Duarte Bellacross Technologies, LLC 2016-01-18
+1. Thomas Vincent 2016-01-18
+1. Thomas Vincent Relenz 2016-01-18
+1. Shaun Martin 2016-01-18
+1. James Cuzella LyraPhase 2016-01-18
+1. James Cuzella ReturnPath 2016-01-18
+1. Riley Spicer 2016-01-19
+1. Haggai Zagury Tikal Knowledge, SAP 2016-01-19
+1. Enrico Stahn 2016-01-19
+1. Ash Wilson CloudPassage 2016-01-20
+1. Ben Bradshaw 2016-01-20
+1. Nayan Hajratwala 2016-01-20
+1. Sebastian Trebitz 2016-01-21
+1. James Quinn 2016-01-21
+1. Jose Luis Salas Topota 2016-01-21
+1. joe miller Pantheon 2016-01-21
+1. Sebastian Trebitz Sebastian Trebitz - ICT Consultancy Services 2016-01-21
+1. Jade Chu AOL 2016-01-22
+1. Dennis Vink 2016-01-22
+1. Christopher Nielsen 2016-01-22
+1. Dennis Vink Dennis Vink 2016-01-22
+1. Hiroki Ohtsuka 2016-01-23
+1. Eric Robitaille 2016-01-23
+1. Jason Capriotti 2016-01-25
+1. Artur Sitarski 2016-01-26
+1. Darron Froese Datadog Inc. 2016-01-26
+1. Steven Haddox 2016-01-26
+1. Vipin Thomas 2016-01-27
+1. Alexander Wagner 2016-01-27
+1. Andrew Miller 2016-01-28
+1. Drew Rapenchuk 2016-01-29
+1. Drew Rapenchuk Bloomberg LP 2016-01-29
+1. Sean Rettig 2016-01-29
+1. Sumit Murari 2016-02-01
+1. Jason Barnett NA 2016-02-02
+1. Dave Rodecap 2016-02-02
+1. James Pruetting Built by Robots 2016-02-02
+1. Daniel Reznicek 2016-02-02
+1. Yusuke Hagihara 2016-02-02
+1. Joey Bates 2016-02-03
+1. Chris Hayes 2016-02-03
+1. Jarrett Hawrylak 2016-02-03
+1. Demitri Swan 2016-02-04
+1. Keith Swett 2016-02-04
+1. Felix Yan 2016-02-04
+1. RJ Martins 2016-02-04
+1. Ashley Unitt NewVoiceMedia Ltd 2016-02-05
+1. Craig Menning 2016-02-05
+1. John Nye 7digital 2016-02-05
+1. Geoffrey Rucker 2016-02-06
+1. Abdelkrim Dib 2016-02-06
+1. Christoffer Reijer 2016-02-07
+1. Michael Skrynski dkd Internet Service GmbH 2016-02-08
+1. David Newman 2016-02-08
+1. John Northrup 2016-02-08
+1. Dylan Ledbetter Dylan Ledbetter 2016-02-09
+1. Richard Lee 2016-02-09
+1. Maksim Horbul 2016-02-09
+1. David Marshall 2016-02-09
+1. Dylan Ledbetter 2016-02-09
+1. Luuk van den Broek 2016-02-10
+1. Luuk van den Broek 2016-02-10
+1. Sergey Bahchissaraitsev 2016-02-11
+1. Brad Parks 2016-02-11
+1. Aoi Kadoya 2016-02-12
+1. Bryan Gay 2016-02-12
+1. Christopher Soyars 2016-02-12
+1. Leandro Di Tommaso Mikroways 2016-02-13
+1. Ricky Grassmuck 2016-02-13
+1. Ricky Grassmuck Grassmuck.co 2016-02-13
+1. Chris Allen 2016-02-14
+1. Chris Allen Postmodern Solutions 2016-02-14
+1. Luis Sagastume 2016-02-14
+1. Andrii Melnyk 2016-02-16
+1. Brad Parks 2016-02-16
+1. Dang Nguyen 2016-02-16
+1. Christian Willman 2016-02-16
+1. Joshua Smith 2016-02-17
+1. Owen Tuz 2016-02-18
+1. A Moto Ohno! 2016-02-19
+1. A Moto Ohno! Rally Health 2016-02-19
+1. Wes Winham PolicyStat LLC 2016-02-19
+1. Nic Waller 2016-02-19
+1. Robert Scott-Buccluech 2016-02-19
+1. Satish Puranam 2016-02-21
+1. Ron Herardian 2016-02-21
+1. Joel Freedman Pivotal Labs 2016-02-22
+1. Gary Bright 2016-02-23
+1. Paul Mitchell 2016-02-23
+1. Will Jordan Code.org 2016-02-23
+1. James Coppens Underground Media Group 2016-02-23
+1. Thomas Robinson-Gore 2016-02-24
+1. Bradley Corner 2016-02-25
+1. Christophe Courtaut 2016-02-26
+1. Rex Cerbas 2016-02-26
+1. Juan Castillo Cano 2016-02-26
+1. Jared Kauppila 2016-02-29
+1. William Burton 2016-03-01
+1. William Burton ENC4U 2016-03-01
+1. Josh Beauregard 2016-03-01
+1. Michael D'Auria 2016-03-01
+1. Elijah Wright 2016-03-01
+1. Anna Tikhonova 2016-03-02
+1. James Bunch 2016-03-05
+1. Harish Hareendra Babu 2016-03-07
+1. mohanbabu balasubramaniam 2016-03-07
+1. David Duponchel Mediarithmics 2016-03-07
+1. Shane Ramey 2016-03-07
+1. Zach Morgan Secureworks 2016-03-08
+1. Victoria Jeffrey 2016-03-09
+1. Jesse Cotton 2016-03-09
+1. Christopher Gerber 2016-03-10
+1. Pradeep Chhetri 2016-03-10
+1. Dylan-Daniel Page 2016-03-10
+1. Dmytro Slupytskyi 2016-03-10
+1. Michael Weinberg Heavy Water Operations 2016-03-10
+1. Steve Carman Planalytics 2016-03-11
+1. Steve Carman 2016-03-11
+1. Jonathan Serafini LightSpeed Retail 2016-03-13
+1. Andrew Cornies 2016-03-13
+1. Joshua Burt 2016-03-13
+1. Dmytro Slupytskyi 2016-03-14
+1. Julian Tabel 2016-03-14
+1. Sergio Rua IT Projections Limited 2016-03-14
+1. Sergio Rua 2016-03-14
+1. Phil Spencer n/a 2016-03-15
+1. Ben Abrams 2016-03-15
+1. Dylan Canfield 2016-03-15
+1. Nick Pjetrovic 2016-03-15
+1. Phil Spencer 2016-03-15
+1. Matt Kulka 2016-03-15
+1. Ben Kochie 2016-03-16
+1. Aaron Lippold 2016-03-16
+1. Andy Brezinsky ShoreTel Inc. 2016-03-17
+1. Michael Miko Apparently 2016-03-18
+1. Marat Vyshegorodtsev 2016-03-18
+1. Harald Krämer 2016-03-18
+1. Marat Vyshegorodtsev 2016-03-19
+1. Julian Tabel 2016-03-21
+1. Christian Tramnitz TraSec GmbH 2016-03-21
+1. Eike Waldt B1 Systems GmbH 2016-03-22
+1. Thorsten Klein 2016-03-22
+1. Andrew Stoltz 2016-03-22
+1. Justin Schuhmann 2016-03-22
+1. Duc Ha 2016-03-22
+1. Jonathan Sokolowski 2016-03-22
+1. Sean Coyne 2016-03-23
+1. Antonio Beyah Beyah Solutions, LLC 2016-03-24
+1. Graeme Fawcett 2016-03-24
+1. Trey Tomlinson 2016-03-24
+1. Onno van der Straaten 2016-03-25
+1. Joshua Bussdieker 2016-03-26
+1. Jean-Pierre Matsumoto 2016-03-29
+1. Nicholas Waller 2016-03-29
+1. Denny Pruitt 2016-03-29
+1. Darryl Bresland 2016-03-30
+1. Claus Beerta 2016-03-30
+1. Vincenzo Rivello 2016-03-30
+1. Pablo Banderas Inetsys SL 2016-03-30
+1. Brandon Sharitt 2016-03-31
+1. Eric Hartmann 2016-03-31
+1. Steve Collins 2016-03-31
+1. Greg Ziskind 2016-03-31
+1. Cristiano Balducci 2016-03-31
+1. Travis Thompson 2016-03-31
+1. Martin Arndt 2016-03-31
+1. Matthew Endsley 2016-04-01
+1. Federico Gimenez 2016-04-01
+1. Alex Pop 2016-04-01
+1. Michael Fyffe 2016-04-01
+1. Austin Heiman 2016-04-01
+1. Tamas Szasz 2016-04-03
+1. Jason Morgan 2016-04-04
+1. Chris Trott 2016-04-05
+1. Riley Shott 2016-04-06
+1. Robert Ottaway 2016-04-06
+1. Danny Webb 2016-04-06
+1. Alexander Zubkov 2016-04-07
+1. Maxwillian Miorim 2016-04-07
+1. Alert Logic Inc. 2016-04-07
+1. Mohammad AbuShady 2016-04-07
+1. Mohammad AbuShady Instabug 2016-04-07
+1. Ash Wilson 2016-04-07
+1. Joe Sørensen 2016-04-07
+1. Benny Chandra 2016-04-10
+1. Brett Langdon 2016-04-10
+1. Andrew Turner 2016-04-10
+1. Stéphane Crivisier 2016-04-11
+1. Adam Westhusing 2016-04-12
+1. MARK AMERY 2016-04-12
+1. Eric Olsen 2016-04-12
+1. Cameron Straka 2016-04-13
+1. Shuji Yamada 2016-04-14
+1. Anirudh Gupta HPE 2016-04-14
+1. Jack Naglieri 2016-04-16
+1. Fabien Delpierre 2016-04-17
+1. Florian Philippon S4M 2016-04-18
+1. Michael Reeves 2016-04-18
+1. Florian Philippon 2016-04-18
+1. Logan McDonald 2016-04-18
+1. Ferenc Kovács 2016-04-19
+1. William Ruddock 2016-04-20
+1. Anirudh Gupta 2016-04-21
+1. Justin Dossey 2016-04-21
+1. Daniel Jimenez ForeFlight LLC 2016-04-25
+1. 3rd Felipe De Siqueira 2016-04-25
+1. 3rd Felipe De Siqueira Anaplan 2016-04-25
+1. Wilfried JEANNIARD 2016-04-26
+1. Jeremiah Njoroge 2016-04-26
+1. Tristan Morgan 2016-04-26
+1. Hervé Commowick 2016-04-26
+1. David Sieciński 2016-04-26
+1. David Felix 2016-04-27
+1. Travis Johnson 2016-04-27
+1. Christopher Campo 2016-04-27
+1. Frédéric Nowak Hydra Technologies, Inc 2016-04-28
+1. Alan Thatcher 2016-04-28
+1. Frédéric Nowak 2016-04-28
+1. Frédéric Nowak 2016-04-28
+1. Bastian Roden 2016-04-28
+1. Seth Larson 2016-04-29
+1. John Snow 2016-04-29
+1. Tyler Vann-Campbell 2016-05-01
+1. Carl Johnston 2016-05-02
+1. Ben Weissmann 2016-05-02
+1. Ankit Shah 2016-05-03
+1. Grant Shively 2016-05-03
+1. Apollo Catlin 2016-05-04
+1. Chad Williamson 2016-05-04
+1. Daniel Barrell 2016-05-05
+1. Marcos Wright-Kuhns 2016-05-06
+1. Chris Sullivan 2016-05-08
+1. Ameir Abdeldayem 2016-05-08
+1. Chris Sullivan 2016-05-09
+1. Andrew Marwood 2016-05-09
+1. Tejay Cardon 2016-05-10
+1. Bryan Stone 2016-05-10
+1. Rhea Ghosh 2016-05-10
+1. Craig McEldowney Crifkin Amalgamated LLC 2016-05-11
+1. Yuki Sonoda Gengo 2016-05-11
+1. Ted Wang 2016-05-11
+1. Craig McEldowney 2016-05-11
+1. Taylor Benson 2016-05-11
+1. Uwe Stuehler 2016-05-12
+1. Joe Miller 2016-05-12
+1. Franklin Webber 2016-05-13
+1. Steohen Purr 2016-05-13
+1. Jirka Fajfr 2016-05-16
+1. Ming Xie 2016-05-16
+1. Riley Shott Vision Critical 2016-05-16
+1. Julia Tkachova 2016-05-17
+1. Prashant Sharma 2016-05-17
+1. Samuel Darwin 2016-05-17
+1. Prajakta Purohit Chef 2016-05-17
+1. Jessica Rosch iJet Technologies 2016-05-17
+1. Michael Tognetti SendGrid, Inc. 2016-05-18
+1. Christoph Blecker 2016-05-18
+1. Stephen Breyer-Menke 2016-05-18
+1. Reed McCartney 2016-05-20
+1. Shane da Silva 2016-05-21
+1. Administrator System 2016-05-21
+1. Harley Alaniz 2016-05-22
+1. Christopher Rigor 2016-05-24
+1. Donald Guy 2016-05-25
+1. Ajay Chand 2016-05-25
+1. Timothy Cyrus 2016-05-26
+1. JD Goins 2016-05-26
+1. Jameson Goins 2016-05-26
+1. Lajos Veres 2016-05-26
+1. Justyn Shull 2016-05-27
+1. MIURA Toru 2016-05-28
+1. Adib Saad 2016-05-28
+1. Uri Sarid Mulesoft 2016-05-29
+1. Milad Rastian 2016-05-31
+1. Denys Slipetskyy 2016-05-31
+1. Trevor Lauder 2016-06-01
+1. Ryan Punt 2016-06-01
+1. Evan Machnic 2016-06-02
+1. Leonid Ishimnikov 2016-06-03
+1. Andrew Cooper 2016-06-03
+1. Jose Ruiz 2016-06-03
+1. Chris Morris 2016-06-04
+1. Michael Vershinin 2016-06-04
+1. Michael Vershinin SAP 2016-06-04
+1. Winston Durand 2016-06-05
+1. Bob Bob 2016-06-05
+1. Mathieu Allaire 2016-06-05
+1. pankaj khali 2016-06-06
+1. Natacha Springer 2016-06-07
+1. David Aronsohn 2016-06-07
+1. Jason Clark 2016-06-08
+1. Alexander Harder 2016-06-08
+1. Timothy Bassett 2016-06-08
+1. Annie Hedgpeth 2016-06-08
+1. Emil Sit 2016-06-08
+1. Alex Romanov 2016-06-09
+1. Sebastian Geidies 2016-06-09
+1. Todd Bushnell 2016-06-09
+1. Nate Flood 2016-06-10
+1. Eugene Slesarchuk 2016-06-10
+1. Arnaldo Garat 2016-06-10
+1. Matthew Walter 2016-06-12
+1. Vlad Gorodetsky 2016-06-13
+1. Ash Alam 2016-06-13
+1. Vlad Gorodetsky Shopify 2016-06-13
+1. Joseph Sokol-Margolis 2016-06-15
+1. Jerry Aldrich Chef Software 2016-06-15
+1. Jerry Aldrich 2016-06-15
+1. Austin Vecchio 2016-06-17
+1. Stephen Sadowski 2016-06-17
+1. Cobus Bernard 2016-06-20
+1. Rex Cerbas Rakuten Inc. 2016-06-21
+1. Andy Allan 2016-06-21
+1. Michael Gutteridge 2016-06-22
+1. Michael Smith 2016-06-22
+1. Michael Smith None 2016-06-22
+1. Yuki Sonoda Gengo 2016-06-24
+1. Jonathon Marshall 2016-06-24
+1. Mike Sgarbossa 2016-06-25
+1. Ryan Conrad 2016-06-25
+1. Mark Bell 2016-06-28
+1. Brynmor Rees The Regents of the University of Colorado 2016-06-28
+1. Hugo Cisneiros 2016-06-28
+1. Azat Khadiev 2016-06-29
+1. Rob Reynolds 2016-06-29
+1. Abhishek Kumar 2016-06-30
+1. Travis Thompson Altiscale 2016-07-01
+1. Jussi-Pekka Pispa Tampere University of Technology 2016-07-01
+1. Henry Muru Paenga 2016-07-01
+1. Alexander Dvorkovyy 2016-07-02
+1. Pavel Jeloudovski 2016-07-03
+1. Kyle Sexton 2016-07-05
+1. Timothy Kachler Atlas Digital 2016-07-05
+1. Patrick Münch 2016-07-05
+1. Stanley Hu 2016-07-06
+1. Juan Cruz 2016-07-06
+1. Jon Sime 2016-07-06
+1. Erin Kolp 2016-07-07
+1. Radoslaw Jaros 2016-07-07
+1. Genadii Kruglenko 2016-07-08
+1. Joseph Yaworski 2016-07-08
+1. Randy Rue 2016-07-08
+1. Jennifer Taylor 2016-07-09
+1. max maxn 2016-07-09
+1. Hank Ehly 2016-07-10
+1. Jason McNew 2016-07-11
+1. Luuk van den Broek 2016-07-11
+1. Asher Yanich 2016-07-12
+1. Asher Yanich Fastly 2016-07-12
+1. Elkin Sierra 2016-07-12
+1. Scott Nelson Windels 2016-07-13
+1. Anton Markelov 2016-07-14
+1. chris evett 2016-07-16
+1. Kierran McPherson 2016-07-16
+1. sathish mr 2016-07-18
+1. Ryan LeViseur 2016-07-19
+1. Andrew M. Kirschke 2016-07-19
+1. Alexis Sellier 2016-07-21
+1. Alex Trull 2016-07-21
+1. Alex Trull 2016-07-22
+1. Michael Weinberg 2016-07-25
+1. Mathias Söderberg 2016-07-25
+1. Ofir Petrushka 2016-07-25
+1. Kevin Reedy 2016-07-26
+1. Rein Remmel cloud.ee OÜ 2016-07-26
+1. Jakub Jarosz 2016-07-26
+1. James Krewson DreamBox Learning, Inc. 2016-07-27
+1. Rein Remmel 2016-07-27
+1. Matthew Odille 2016-07-28
+1. Marcin Skurski 2016-07-28
+1. Marcin Skurski Hostelworld 2016-07-28
+1. Jose Luis Wazuh 2016-07-28
+1. Oleksii Talamanov 2016-07-29
+1. Robert Ressl 2016-07-31
+1. David Aronsohn 2016-08-02
+1. David Aronsohn 2016-08-02
+1. Robert Jerzak 2016-08-02
+1. Paulo Tada 2016-08-02
+1. Martin Matatko 2016-08-03
+1. Hugh Brown 2016-08-03
+1. Achim Heger 2016-08-03
+1. David McGrath 2016-08-05
+1. Darwin Wu 2016-08-08
+1. Gabor Boros 2016-08-08
+1. David Pell 2016-08-08
+1. Jere Julian Arista Networks 2016-08-10
+1. Michael Schlies 2016-08-10
+1. Libert Schmidt 2016-08-12
+1. Rob Marano Totem Power, Inc. 2016-08-13
+1. Rob Marano 2016-08-13
+1. Anna Tikhonova 2016-08-15
+1. Erik Gomez 2016-08-15
+1. Evan Borgstrom 2016-08-15
+1. Don Don 2016-08-16
+1. Petre Ghita 2016-08-16
+1. Jeff Mathe 2016-08-16
+1. Josh McCleery 2016-08-16
+1. Catalin Balan 2016-08-17
+1. Sebastian Boschert 2016-08-17
+1. Gemini Agaloos 2016-08-18
+1. Georgi Lambov 2016-08-19
+1. Baurzhan Konurbayev 2016-08-19
+1. Thomas Berger Piratenpartei Deutschland 2016-08-20
+1. Pavel Forkert 2016-08-20
+1. Bipin Bachhao 2016-08-20
+1. Janith Perera 2016-08-23
+1. Artur Smolarek 2016-08-23
+1. Joshua Wester 2016-08-24
+1. Austin Simmons 2016-08-25
+1. Nick Shemonsky 2016-08-25
+1. Michał Sochoń 2016-08-25
+1. Sathyajith Bhat 2016-08-26
+1. Wesley Staples 2016-08-26
+1. Mikhail kvokka 2016-08-27
+1. Thajudheen Rajak 2016-08-28
+1. Paul Maddocks 2016-08-28
+1. Wisen Tanasa 2016-08-28
+1. John Kerry 2016-08-29
+1. Cem Guresci 2016-08-29
+1. Juan Martinez 2016-08-30
+1. Mohammad Sedighi 2016-08-30
+1. Thiago Vinhas 2016-08-30
+1. John Dewey 2016-08-30
+1. John Harrison 2016-09-01
+1. Jose Asuncion 2016-09-01
+1. Valentin Dallmeier Testfabrik AG 2016-09-01
+1. Greg Hellings B7Interactive, LLC 2016-09-06
+1. Brian Bohanon Aaron's, Inc. 2016-09-07
+1. Jeremy Miller 2016-09-07
+1. Martin Mosegaard Amdisen 2016-09-07
+1. Jeremy Miller PolyHat 2016-09-07
+1. Joseph Block 2016-09-08
+1. Stefan Wessels Beljaars Schuberg Philis 2016-09-09
+1. Ben Dang 2016-09-09
+1. Stefan Wessels Beljaars 2016-09-12
+1. Steve Johnson 2016-09-12
+1. Sudhanshu Thakur 2016-09-12
+1. Ananda Babu 2016-09-12
+1. enrico b 2016-09-12
+1. enrico b 2016-09-12
+1. Sudhanshu Thakur DataMetis 2016-09-12
+1. Ananda Babu Nclouds Inc. 2016-09-12
+1. Jeff Taylor 2016-09-13
+1. JT Giri nclouds 2016-09-13
+1. Jacob Wilkins 2016-09-13
+1. Marques Johansson 2016-09-14
+1. Stefan Wessels Beljaars SWB company 2016-09-14
+1. Jordi Molina 2016-09-14
+1. Michal Gebauer 2016-09-14
+1. Joris van Lieshout 2016-09-14
+1. Jean-Bernard Damiano Personnal 2016-09-15
+1. Jean-Bernard Damiano 2016-09-15
+1. Andrei Nakagawa Soltius NZ 2016-09-16
+1. Jake Gage 2016-09-18
+1. Karol Drazek 2016-09-19
+1. Jye Lee 2016-09-19
+1. Margaret Walker Chef Software, Inc 2016-09-20
+1. Brian Bohanon 2016-09-20
+1. Margaret Walker 2016-09-20
+1. Roberto Mardeni 2016-09-21
+1. Oleksii Vlasov 2016-09-21
+1. Takuya Seki 2016-09-21
+1. Brittany Smith 2016-09-24
+1. Darren Campbell Primar Group 2016-09-26
+1. Kevin Reedy 2016-09-26
+1. Brian Lieberman 2016-09-26
+1. Brian Lieberman Gannett 2016-09-26
+1. Olaf Brandt etracker GmbH 2016-09-27
+1. Chris Gerke 2016-09-28
+1. Alex Pop 2016-09-28
+1. Jean-Pierre Matsumoto Freelance 2016-09-28
+1. Marc Arndt 2016-10-03
+1. Nenad P 2016-10-03
+1. Andrew Heller 2016-10-03
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b2f9ece975..723c7e0ec8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,208 +1,151 @@
-# Contributing to Chef
+# Contributing to Chef Projects
-We are glad you want to contribute to Chef!
+We're glad you want to contribute to a Chef project! This document will help answer common questions you may have during your first contribution.
-We utilize **Github Issues** for issue tracking and contributions. You can contribute in two ways:
+## Submitting Issues
-1. Reporting an issue or making a feature request [here](#issues).
-2. Adding features or fixing bugs yourself and contributing your code to Chef.
+Not every contribution comes in the form of code. Submitting, confirming, and triaging issues is an important task for any project. At Chef we use Github to track all project issues.
-## Contribution Process
-
-We have a 3 step process that utilizes **Github Issues**:
-
-1. Sign or be added to an existing [Contributor License Agreement (CLA)](https://supermarket.chef.io/become-a-contributor).
-2. Create a Github Pull Request.
-3. Do [Code Review](#cr) with the **Chef Engineering Team** or **Chef Core Committers** on the pull request.
-
-### <a name="pulls"></a> Chef Pull Requests
+If you are familiar with Chef and know the component, that is causing you a problem, you can file an issue in the corresponding Github project. All of our Open Source Software can be found in our [Github organization](https://github.com/chef/). All projects include Github issue templates to help gather information needed for a thorough review.
-Chef is built to last. We strive to ensure high quality throughout the Chef experience. In order to ensure
- this, we require a couple of things for all pull requests to Chef:
+We ask you not to submit security concerns via Github. For details on submitting potential security issues please see <https://www.chef.io/security/>
-1. **Tests:** To ensure high quality code and protect against future regressions, we require all the
- code in Chef to have at least unit test coverage. See the [spec/unit](https://github.com/chef/chef/tree/master/spec/unit)
- directory for the existing tests and use ```bundle exec rake spec``` to run them.
-2. **Green Travis Run:** We use [Travis CI](https://travis-ci.org/) in order to run our tests
- continuously on all the pull requests. We require the Travis runs to succeed on every pull
- request before being merged.
+In addition to Github issues, we also utilize a feedback site that helps our product team track and rank feature requests. If you have a feature request, this is an excellent place to start <https://feedback.chef.io>
-In addition to this it would be nice to include the description of the problem you are solving
- with your change. You can use [Chef Issue Template](#issuetemplate) in the description section
- of the pull request.
+## Contribution Process
-### <a name="cr"></a> Chef Code Review Process
+We have a 3 step process for contributions:
-The Chef Code Review process happens on Github pull requests. See
- [this article](https://help.github.com/articles/using-pull-requests) if you're not
- familiar with Github Pull Requests.
+1. Commit changes to a git branch, making sure to sign-off those changes for the [Developer Certificate of Origin](#developer-certification-of-origin-dco).
+2. Create a Github Pull Request for your change, following the instructions in the pull request template.
+3. Perform a [Code Review](#code-review-process) with the project maintainers on the pull request.
-Once you a pull request, the **Chef Engineering Team** or **Chef Core Committers** will review your code
- and respond to you with any feedback they might have. The process at this point is as follows:
+### Pull Request Requirements
-1. 2 thumbs-ups are required from the **Chef Engineering Team** or **Chef Core Committers** for all merges.
-2. When ready, your pull request will be tagged with label `Ready For Merge`.
-3. Your patch will be merged into `master` including necessary documentation updates
- and you will be included in `CHANGELOG.md`. Our goal is to have patches merged in 2 weeks
- after they are marked to be merged.
+Chef Projects are built to last. We strive to ensure high quality throughout the experience. In order to ensure this, we require that all pull requests to Chef projects meet these specifications:
-If you would like to learn about when your code will be available in a release of Chef, read more about
- [Chef Release Process](#release).
+1. **Tests:** To ensure high quality code and protect against future regressions, we require all the code in Chef Projects to have at least unit test coverage. We use [RSpec](http://rspec.info/) for unit testing.
+2. **Green CI Tests:** We use [Travis CI](https://travis-ci.org/) and/or [AppVeyor](https://www.appveyor.com/) CI systems to test all pull requests. We require these test runs to succeed on every pull request before being merged.
-### <a name="oh"></a> Developer Office Hours
+### Code Review Process
-We hold regular "office hours" on Google Hangouts On-The-Air that you can join to review contributions together,
-ask questions about contributing, or just hang out with Chef Software employees. The regularly scheduled Chef hangouts occur on Mondays and Wednesdays at 3pm Eastern / Noon Pacific.
+Code review takes place in Github pull requests. See [this article](https://help.github.com/articles/about-pull-requests/) if you're not familiar with Github Pull Requests.
-The link to join the Hangout or watch it live is usually tweeted from [@ChefOfficeHours](https://twitter.com/ChefOfficeHours)
-and posted in the #chef IRC channel on irc.freenode.net when the hangout is about to start.
+Once you open a pull request, project maintainers will review your code and respond to your pull request with any feedback they might have. The process at this point is as follows:
-You can watch the recordings of the old Code Review hangouts on the [opscodebtm](http://www.youtube.com/opscodebtm) youtube account.
+1. Two thumbs-up (:+1:) are required from project maintainers. See the master maintainers document for Chef projects at <https://github.com/chef/chef/blob/master/MAINTAINERS.md>.
+2. When ready, your pull request will be tagged with label `Ready For Merge`.
+3. Your change will be merged into the project's `master` branch and will be noted in the project's `CHANGELOG.md` at the time of release.
-### Contributor License Agreement (CLA)
-Licensing is very important to open source projects. It helps ensure the
- software continues to be available under the terms that the author desired.
+If you would like to learn about when your code will be available in a release of Chef, read more about [Chef Release Cycles](#release-cycles).
-Chef uses [the Apache 2.0 license](https://github.com/chef/chef/blob/master/LICENSE)
- to strike a balance between open contribution and allowing you to use the
- software however you would like to.
+### Developer Certification of Origin (DCO)
-The license tells you what rights you have that are provided by the copyright holder.
- It is important that the contributor fully understands what rights they are
- licensing and agrees to them. Sometimes the copyright holder isn't the contributor,
- such as when the contributor is doing work for a company.
+Licensing is very important to open source projects. It helps ensure the software continues to be available under the terms that the author desired.
-To make a good faith effort to ensure these criteria are met, Chef requires an Individual CLA
- or a Corporate CLA for contributions. This agreement helps ensure you are aware of the
- terms of the license you are contributing your copyrighted works under, which helps to
- prevent the inclusion of works in the projects that the contributor does not hold the rights
- to share.
+Chef uses [the Apache 2.0 license](https://github.com/chef/chef/blob/master/LICENSE) to strike a balance between open contribution and allowing you to use the software however you would like to.
-It only takes a few minutes to complete a CLA, and you retain the copyright to your contribution.
+The license tells you what rights you have that are provided by the copyright holder. It is important that the contributor fully understands what rights they are licensing and agrees to them. Sometimes the copyright holder isn't the contributor, such as when the contributor is doing work on behalf of a company.
-You can complete our
- [Individual CLA](https://supermarket.chef.io/icla-signatures/new) online.
- If you're contributing on behalf of your employer and they retain the copyright for your works,
- have your employer fill out our
- [Corporate CLA](https://supermarket.chef.io/ccla-signatures/new) instead.
+To make a good faith effort to ensure these criteria are met, Chef requires the Developer Certificate of Origin (DCO) process to be followed.
-### Chef Obvious Fix Policy
+The DCO is an attestation attached to every contribution made by every developer. In the commit message of the contribution, the developer simply adds a Signed-off-by statement and thereby agrees to the DCO, which you can find below or at <http://developercertificate.org/>.
-Small contributions such as fixing spelling errors, where the content is small enough
- to not be considered intellectual property, can be submitted by a contributor as a patch,
- without a CLA.
+```
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(a) The contribution was created in whole or in part by me and I
+ have the right to submit it under the open source license
+ indicated in the file; or
+
+(b) The contribution is based upon previous work that, to the
+ best of my knowledge, is covered under an appropriate open
+ source license and I have the right under that license to
+ submit that work with modifications, whether created in whole
+ or in part by me, under the same open source license (unless
+ I am permitted to submit under a different license), as
+ Indicated in the file; or
+
+(c) The contribution was provided directly to me by some other
+ person who certified (a), (b) or (c) and I have not modified
+ it.
+
+(d) I understand and agree that this project and the contribution
+ are public and that a record of the contribution (including
+ all personal information I submit with it, including my
+ sign-off) is maintained indefinitely and may be redistributed
+ consistent with this project or the open source license(s)
+ involved.
+```
-As a rule of thumb, changes are obvious fixes if they do not introduce any new functionality
- or creative thinking. As long as the change does not affect functionality, some likely
- examples include the following:
+For more information on the change see the Chef Blog post [Introducing Developer Certificate of Origin](https://blog.chef.io/2016/09/19/introducing-developer-certificate-of-origin/)
-* Spelling / grammar fixes
-* Typo correction, white space and formatting changes
-* Comment clean up
-* Bug fixes that change default return values or error codes stored in constants
-* Adding logging messages or debugging output
-* Changes to ‘metadata’ files like Gemfile, .gitignore, build scripts, etc.
-* Moving source files from one directory or package to another
+#### DCO Sign-Off Methods
-**Whenever you invoke the “obvious fix” rule, please say so in your commit message:**
+The DCO requires a sign-off message in the following format appear on each commit in the pull request:
```
-------------------------------------------------------------------------
-commit 370adb3f82d55d912b0cf9c1d1e99b132a8ed3b5
-Author: danielsdeleo <dan@chef.io>
-Date: Wed Sep 18 11:44:40 2013 -0700
-
- Fix typo in config file docs.
-
- Obvious fix.
-
-------------------------------------------------------------------------
+Signed-off-by: Julia Child <juliachild@chef.io>
```
-## <a name="issues"></a> Chef Issue Tracking
+The DCO text can either be manually added to your commit body, or you can add either **-s** or **--signoff** to your usual git commit commands. If you forget to add the sign-off you can also amend a previous commit with the sign-off by running **git commit --amend -s**. If you've pushed your changes to Github already you'll need to force push your branch after this with **git push -f**.
-Chef Issue Tracking is handled using Github Issues.
+### Chef Obvious Fix Policy
-If you are familiar with Chef and know the component that is causing you a problem or if you
- have a feature request on a specific component you can file an issue in the corresponding
- Github project. All of our Open Source Software can be found in our
- [Github organization](https://github.com/chef/).
+Small contributions, such as fixing spelling errors, where the content is small enough to not be considered intellectual property, can be submitted without signing the contribution for the DCO.
-There is also a listing of the various Chef products and where to file issues that can be
- found in the Chef docs in the [community contributions section](https://docs.chef.io/community_contributions.html#issues-and-bug-reports).
+As a rule of thumb, changes are obvious fixes if they do not introduce any new functionality or creative thinking. Assuming the change does not affect functionality, some common obvious fix examples include the following:
-Otherwise you can file your issue in the [Chef project](https://github.com/chef/chef/issues)
- and we will make sure it gets filed against the appropriate project.
+- Spelling / grammar fixes
+- Typo correction, white space and formatting changes
+- Comment clean up
+- Bug fixes that change default return values or error codes stored in constants
+- Adding logging messages or debugging output
+- Changes to 'metadata' files like Gemfile, .gitignore, build scripts, etc.
+- Moving source files from one directory or package to another
-In order to decrease the back and forth in issues, and to help us get to the bottom of them quickly
- we use the below issue template. You can copy/paste this template into the issue you are opening and
- edit it accordingly.
+**Whenever you invoke the "obvious fix" rule, please say so in your commit message:**
-<a name="issuetemplate"></a>
```
-### Version:
-[Version of the project installed]
-
-### Environment: [Details about the environment such as the Operating System, cookbook details, etc...]
-
-### Scenario:
-[What you are trying to achieve and you can't?]
+------------------------------------------------------------------------
+commit 370adb3f82d55d912b0cf9c1d1e99b132a8ed3b5
+Author: Julia Child <juliachild@chef.io>
+Date: Wed Sep 18 11:44:40 2015 -0700
-### Steps to Reproduce:
-[If you are filing an issue what are the things we need to do in order to repro your problem?]
+ Fix typo in the README.
-### Expected Result:
-[What are you expecting to happen as the consequence of above reproduction steps?]
+ Obvious fix.
-### Actual Result:
-[What actually happens after the reproduction steps?]
+------------------------------------------------------------------------
```
-### Useful Github Queries
-
-Contributions go through a review process to improve code quality and avoid regressions. Managing a large number of contributions requires a workflow to provide queues for work such as triage, code review, and merging. A semi-formal process has evolved over the life of the project. Chef maintains this process pending community development and acceptance of an [RFC](https://github.com/chef/chef-rfc). These queries will help track contributions through this process:
-
-* [Issues that are not assigned to a team](https://github.com/chef/chef/issues?q=is%3Aopen+-label%3AAIX+-label%3ABSD+-label%3Awindows+-label%3A%22Chef+Core%22++-label%3A%22Dev+Tools%22+-label%3AUbuntu+-label%3A%22Enterprise+Linux%22+-label%3A%22Ready+For+Merge%22+-label%3AMac+-label%3ASolaris+)
-* [Untriaged Issues](https://github.com/chef/chef/issues?q=is%3Aopen+is%3Aissue+-label%3ABug+-label%3AEnhancement+-label%3A%22Tech+Cleanup%22+-label%3A%22Ready+For+Merge%22)
-* [PRs to be Reviewed](https://github.com/chef/chef/labels/Pending%20Maintainer%20Review)
-* [Suitable for First Contribution](https://github.com/chef/chef/labels/Easy)
-
-## <a name="release"></a> Chef Release Cycles
-
-Our primary shipping vehicle is operating system specific packages that includes
- all the requirements of Chef. We call these [Omnibus packages](https://github.com/chef/omnibus)
+## Release Cycles
-We also release our software as gems to [Rubygems](https://rubygems.org/) but we strongly
- recommend using Chef packages since they are the only combination of native libraries &
- gems required by Chef that we test throughly.
+Our primary shipping vehicle is operating system specific packages that includes all the requirements of Chef. We call these [Omnibus packages](https://github.com/chef/omnibus)
-Our version numbering closely follows [Semantic Versioning](http://semver.org/) standard. Our
- standard version numbers look like X.Y.Z which mean:
+We also release our software as gems to [Rubygems](https://rubygems.org/) but we strongly recommend using Chef packages since they are the only combination of native libraries & gems required by Chef that we test throughly.
-* X is a major release, which may not be fully compatible with prior major releases
-* Y is a minor release, which adds both new features and bug fixes
-* Z is a patch release, which adds just bug fixes
+Our version numbering roughly follows [Semantic Versioning](http://semver.org/) standard. Our standard version numbers look like X.Y.Z which mean:
-We frequently make `alpha` and `beta` releases with version numbers that look like
- `X.Y.Z.alpha.0` or `X.Y.Z.beta.1`. These releases are still well tested but not as
- throughly as **Minor** or **Patch** releases.
+- X is a major release, which may not be fully compatible with prior major releases
+- Y is a minor release, which adds both new features and bug fixes
+- Z is a patch release, which adds just bug fixes
-We do a `Minor` release approximately every 3 months and `Patch` releases on a when-needed
- basis for regressions, significant bugs, and security issues.
+After shipping a release of Chef we bump the `Minor` version by one to start development of the next minor release. All merges to master trigger an increment of the `Patch` version, and a build through our internal testing pipeline. We do a `Minor` release approximately every month, which consist of shipping one of the already auto-incremented and tested `Patch` versions. For example after shiping 12.10.24, we incremented Chef to 12.11.0\. From there 18 commits where merged bringing the version to 12.11.18, which we shipped as an omnibus package.
-Announcements of releases are available on [Chef Blog](http://www.chef.io/blog) when they are
- available.
+Announcements of releases are made to the [chef mailing list](https://discourse.chef.io/c/chef-release) when they are available.
## Chef Community
-Chef is made possible by a strong community of developers and system administrators. If you have
- any questions or if you would like to get involved in the Chef community you can check out:
+Chef is made possible by a strong community of developers and system administrators. If you have any questions or if you would like to get involved in the Chef community you can check out:
-* [chef](http://lists.opscode.com/sympa/info/chef) and [chef-dev](http://lists.opscode.com/sympa/info/chef-dev) mailing lists
-* [\#chef](https://botbot.me/freenode/chef) and [\#chef-hacking](https://botbot.me/freenode/chef-hacking) IRC channels on irc.freenode.net
+- [Chef Mailing List](https://discourse.chef.io/)
+- [Chef Community Slack](https://community-slack.chef.io/)
Also here are some additional pointers to some awesome Chef content:
-* [Chef Docs](https://docs.chef.io/)
-* [Learn Chef](https://learn.chef.io/)
-* [Chef Inc](https://www.chef.io/)
+- [Chef Docs](https://docs.chef.io/)
+- [Learn Chef](https://learn.chef.io/)
+- [Chef Website](https://www.chef.io/)
diff --git a/DOC_CHANGES.md b/DOC_CHANGES.md
deleted file mode 100644
index 90b57c7e34..0000000000
--- a/DOC_CHANGES.md
+++ /dev/null
@@ -1,22 +0,0 @@
-<!---
-This file is reset every time a new release is done. This file describes changes that have not yet been released.
-
-Example Doc Change:
-### Headline for the required change
-Description of the required change.
--->
-
-## Doc changes for Chef 12.9
-
-### New timeout option added to `knife ssh`
-
-When doing a `knife ssh` call, if a connection to a host is not able
-to succeed due to host unreachable or down, the entire call can hang. In
-order to prevent this from happening, a new timeout option has been added
-to allow a connection timeout to be passed to the underlying SSH call
-(see ConnectTimeout setting in http://linux.die.net/man/5/ssh_config)
-
-The timeout setting can be passed in via a command line parameter
-(`-t` or `--ssh-timeout`) or via a knife config
-(`Chef::Config[:knife][:ssh_timeout]`). The value of the timeout is set
-in seconds.
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000..610739eab6
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,11 @@
+FROM busybox
+MAINTAINER Chef Software, Inc. <docker@chef.io>
+
+ARG CHANNEL=stable
+ARG VERSION=14.1.1
+
+RUN wget "http://packages.chef.io/files/${CHANNEL}/chef/${VERSION}/el/6/chef-${VERSION}-1.el6.x86_64.rpm" -O /tmp/chef-client.rpm && \
+ rpm2cpio /tmp/chef-client.rpm | cpio -idmv && \
+ rm -rf /tmp/chef-client.rpm
+
+VOLUME [ "/opt/chef" ]
diff --git a/Gemfile b/Gemfile
index 803e1115d5..527199f80f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,50 +1,33 @@
-# This buys us the ability to be included in other Gemfiles
-require_relative "tasks/gemfile_util"
-extend GemfileUtil
-
source "https://rubygems.org"
-# Pick the gemspec for our platform
-gemspec_name = "chef"
-Dir.glob("chef-*.gemspec").each do |gemspec_filename|
- gemspec_filename =~ /^chef-(.+).gemspec/
- gemspec_platform = $1
- if Gem::Platform.match(Gem::Platform.new(gemspec_platform))
- Bundler.ui.info "Using gemspec #{gemspec_filename} for current platform."
- gemspec_name = "chef-#{gemspec_platform}"
- end
-end
-gemspec name: gemspec_name
+# Note we do not use the gemspec DSL which restricts to the
+# gemspec for the current platform and filters out other platforms
+# during a bundle lock operation. We actually want dependencies from
+# both of our gemspecs. Also note this this mimics gemspec behavior
+# of bundler versions prior to 1.12.0 (https://github.com/bundler/bundler/commit/193a14fe5e0d56294c7b370a0e59f93b2c216eed)
+gem "chef", path: "."
gem "chef-config", path: File.expand_path("../chef-config", __FILE__) if File.exist?(File.expand_path("../chef-config", __FILE__))
-# Ensure that we can always install rake, regardless of gem groups
-gem "rake"
-gem "bundler"
-gem "cheffish"
+gem "cheffish", "~> 14"
group(:omnibus_package) do
+ # override for unf_ext for inspec 2 until
+ # https://github.com/knu/ruby-unf_ext/pull/39
+ # is merged and released
+ gem "unf_ext", "=0.0.7.6", :git => "https://github.com/jquick/ruby-unf_ext.git"
+
gem "appbundler"
gem "rb-readline"
- gem "nokogiri"
+ gem "inspec", "~> 2"
+ gem "chef-vault"
end
+
group(:omnibus_package, :pry) do
gem "pry"
gem "pry-byebug"
gem "pry-remote"
gem "pry-stack_explorer"
end
-# These are used for external tests
-group(:integration) do
- gem "chef-provisioning"
- gem "chef-provisioning-aws"
- gem "chef-rewind"
- gem "chef-sugar"
- gem "chefspec"
- gem "halite"
- gem "poise"
- gem "knife-windows"
- gem "foodcritic"
-end
group(:docgen) do
gem "yard"
@@ -59,35 +42,31 @@ group(:maintenance) do
end
# Everything except AIX
-group(:linux, :bsd, :mac_os_x, :solaris, :windows) do
- # may need to disable this in insolation on fussy builds like AIX, RHEL4, etc
+group(:ruby_prof) do
gem "ruby-prof"
end
+
# Everything except AIX and Windows
-group(:linux, :bsd, :mac_os_x, :solaris) do
+group(:ruby_shadow) do
gem "ruby-shadow", platforms: :ruby
end
group(:development, :test) do
+ gem "rake"
gem "simplecov"
- gem "rack"
+ gem "webmock"
# for testing new chefstyle rules
# gem 'chefstyle', github: 'chef/chefstyle'
gem "chefstyle", git: "https://github.com/chef/chefstyle.git", branch: "master"
end
-group(:changelog) do
- gem "github_changelog_generator"
-end
-
group(:travis) do
- # See `bundler-audit` in .travis.yml
- gem "bundler-audit", git: "https://github.com/rubysec/bundler-audit.git", ref: "4e32fca"
+ gem "travis"
end
instance_eval(ENV["GEMFILE_MOD"]) if ENV["GEMFILE_MOD"]
# If you want to load debugging tools into the bundle exec sandbox,
-# add these additional dependencies into chef/Gemfile.local
-eval(IO.read(__FILE__ + ".local"), binding) if File.exist?(__FILE__ + ".local")
+# add these additional dependencies into Gemfile.local
+eval_gemfile(__FILE__ + ".local") if File.exist?(__FILE__ + ".local")
diff --git a/Gemfile.lock b/Gemfile.lock
index b0ab7b11db..797bfeec6e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,73 +1,77 @@
GIT
remote: https://github.com/chef/chefstyle.git
- revision: 52a0d55a9e8fb30d067c0a66371db4ae781a0269
+ revision: 297373563c6177a90c2f5f07cb2a68f31a57dc2b
branch: master
specs:
- chefstyle (0.3.1)
- rubocop (= 0.39.0)
+ chefstyle (0.10.0)
+ rubocop (= 0.55.0)
GIT
- remote: https://github.com/rubysec/bundler-audit.git
- revision: 4e32fca89d75f0e249671431ff38aadc02bfb28b
- ref: 4e32fca
+ remote: https://github.com/jquick/ruby-unf_ext.git
+ revision: c0b3bd922214a172976f6f368c0b4e4fbf91ed78
specs:
- bundler-audit (0.4.0)
- bundler (~> 1.2)
- thor (~> 0.18)
+ unf_ext (0.0.7.6)
PATH
remote: .
specs:
- chef (12.10.27)
+ chef (14.1.8)
+ addressable
bundler (>= 1.10)
- chef-config (= 12.10.27)
- chef-zero (~> 4.5)
+ chef-config (= 14.1.8)
+ chef-zero (>= 13.0)
diff-lcs (~> 1.2, >= 1.2.4)
erubis (~> 2.7)
+ ffi (< 1.9.22)
ffi-yajl (~> 2.2)
highline (~> 1.6, >= 1.6.9)
iniparse (~> 1.4)
- mixlib-authentication (~> 1.4)
- mixlib-cli (~> 1.4)
- mixlib-log (~> 1.3)
+ iso8601 (~> 0.9.1)
+ mixlib-archive (~> 0.4)
+ mixlib-authentication (~> 2.0)
+ mixlib-cli (~> 1.7)
+ mixlib-log (~> 2.0, >= 2.0.3)
mixlib-shellout (~> 2.0)
net-sftp (~> 2.1, >= 2.1.2)
- net-ssh (>= 2.9, < 4.0)
- net-ssh-multi (~> 1.1)
- ohai (>= 8.6.0.alpha.1, < 9)
+ net-ssh (~> 4.2)
+ net-ssh-multi (~> 1.2, >= 1.2.1)
+ ohai (~> 14.0)
plist (~> 3.2)
proxifier (~> 1.0)
- rspec-core (~> 3.4)
- rspec-expectations (~> 3.4)
- rspec-mocks (~> 3.4)
+ rspec-core (~> 3.5)
+ rspec-expectations (~> 3.5)
+ rspec-mocks (~> 3.5)
rspec_junit_formatter (~> 0.2.0)
serverspec (~> 2.7)
specinfra (~> 2.10)
syslog-logger (~> 1.6)
uuidtools (~> 2.1.5)
- chef (12.10.27-universal-mingw32)
+ chef (14.1.8-universal-mingw32)
+ addressable
bundler (>= 1.10)
- chef-config (= 12.10.27)
- chef-zero (~> 4.5)
+ chef-config (= 14.1.8)
+ chef-zero (>= 13.0)
diff-lcs (~> 1.2, >= 1.2.4)
erubis (~> 2.7)
- ffi (~> 1.9)
+ ffi (< 1.9.22)
ffi-yajl (~> 2.2)
highline (~> 1.6, >= 1.6.9)
iniparse (~> 1.4)
- mixlib-authentication (~> 1.4)
- mixlib-cli (~> 1.4)
- mixlib-log (~> 1.3)
+ iso8601 (~> 0.9.1)
+ mixlib-archive (~> 0.4)
+ mixlib-authentication (~> 2.0)
+ mixlib-cli (~> 1.7)
+ mixlib-log (~> 2.0, >= 2.0.3)
mixlib-shellout (~> 2.0)
net-sftp (~> 2.1, >= 2.1.2)
- net-ssh (>= 2.9, < 4.0)
- net-ssh-multi (~> 1.1)
- ohai (>= 8.6.0.alpha.1, < 9)
+ net-ssh (~> 4.2)
+ net-ssh-multi (~> 1.2, >= 1.2.1)
+ ohai (~> 14.0)
plist (~> 3.2)
proxifier (~> 1.0)
- rspec-core (~> 3.4)
- rspec-expectations (~> 3.4)
- rspec-mocks (~> 3.4)
+ rspec-core (~> 3.5)
+ rspec-expectations (~> 3.5)
+ rspec-mocks (~> 3.5)
rspec_junit_formatter (~> 0.2.0)
serverspec (~> 2.7)
specinfra (~> 2.10)
@@ -81,223 +85,198 @@ PATH
win32-mutex (~> 0.4.2)
win32-process (~> 0.8.2)
win32-service (~> 0.8.7)
+ win32-taskscheduler (~> 0.4.0)
windows-api (~> 0.4.4)
wmi-lite (~> 1.0)
PATH
remote: chef-config
specs:
- chef-config (12.10.27)
- fuzzyurl (~> 0.8.0)
+ chef-config (14.1.8)
+ addressable
+ fuzzyurl
mixlib-config (~> 2.0)
mixlib-shellout (~> 2.0)
+ tomlrb (~> 1.2)
GEM
remote: https://rubygems.org/
specs:
addressable (2.4.0)
- appbundler (0.9.0)
+ appbundler (0.11.4)
mixlib-cli (~> 1.4)
- artifactory (2.3.2)
- ast (2.2.0)
- aws-sdk (2.3.0)
- aws-sdk-resources (= 2.3.0)
- aws-sdk-core (2.3.0)
+ mixlib-shellout (~> 2.0)
+ ast (2.4.0)
+ aws-sdk (2.11.42)
+ aws-sdk-resources (= 2.11.42)
+ aws-sdk-core (2.11.42)
+ aws-sigv4 (~> 1.0)
jmespath (~> 1.0)
- aws-sdk-resources (2.3.0)
- aws-sdk-core (= 2.3.0)
- aws-sdk-v1 (1.66.0)
- json (~> 1.4)
- nokogiri (>= 1.4.4)
- binding_of_caller (0.7.2)
+ aws-sdk-resources (2.11.42)
+ aws-sdk-core (= 2.11.42)
+ aws-sigv4 (1.0.2)
+ azure_mgmt_resources (0.16.0)
+ ms_rest_azure (~> 0.10.0)
+ backports (3.11.3)
+ binding_of_caller (0.8.0)
debug_inspector (>= 0.0.1)
- builder (3.2.2)
- byebug (8.2.5)
- chef-api (0.5.0)
- logify (~> 0.1)
- mime-types
- chef-provisioning (1.7.0)
- cheffish (>= 1.3.1, < 3.0)
- inifile (>= 2.0.2)
- mixlib-install (~> 1.0)
- net-scp (~> 1.0)
- net-ssh (>= 2.9, < 4.0)
- net-ssh-gateway (~> 1.2.0)
- winrm (~> 1.3)
- chef-provisioning-aws (1.9.0)
- aws-sdk (>= 2.1.26, < 3.0)
- aws-sdk-v1 (>= 1.59.0)
- chef-provisioning (~> 1.4)
- retryable (~> 2.0, >= 2.0.1)
- ubuntu_ami (~> 0.4, >= 0.4.1)
- chef-rewind (0.0.9)
- chef-sugar (3.3.0)
- chef-zero (4.6.2)
+ builder (3.2.3)
+ byebug (10.0.2)
+ chef-vault (3.3.0)
+ chef-zero (14.0.6)
ffi-yajl (~> 2.2)
hashie (>= 2.0, < 4.0)
- mixlib-log (~> 1.3)
- rack
+ mixlib-log (~> 2.0)
+ rack (~> 2.0)
uuidtools (~> 2.1)
- cheffish (2.0.4)
- chef-zero (~> 4.3)
- compat_resource
- chefspec (4.6.1)
- chef (>= 11.14)
- fauxhai (~> 3.2)
- rspec (~> 3.0)
- childprocess (0.5.9)
- ffi (~> 1.0, >= 1.0.11)
- coderay (1.1.1)
- colorize (0.7.7)
- compat_resource (12.9.1)
- cucumber-core (1.4.0)
- gherkin (~> 3.2.0)
- debug_inspector (0.0.2)
- descendants_tracker (0.0.4)
- thread_safe (~> 0.3, >= 0.3.1)
- diff-lcs (1.2.5)
- docile (1.1.5)
+ cheffish (14.0.1)
+ chef-zero (~> 14.0)
+ net-ssh
+ coderay (1.1.2)
+ concurrent-ruby (1.0.5)
+ crack (0.4.3)
+ safe_yaml (~> 1.0.0)
+ debug_inspector (0.0.3)
+ diff-lcs (1.3)
+ docile (1.3.0)
+ docker-api (1.34.2)
+ excon (>= 0.47.0)
+ multi_json
+ domain_name (0.5.20180417)
+ unf (>= 0.0.5, < 1.0.0)
erubis (2.7.0)
- faraday (0.9.2)
+ ethon (0.11.0)
+ ffi (>= 1.3.0)
+ excon (0.62.0)
+ faraday (0.15.0)
multipart-post (>= 1.2, < 3)
- fauxhai (3.4.0)
- net-ssh
- ffi (1.9.10)
- ffi (1.9.10-x86-mingw32)
- ffi-yajl (2.2.3)
+ faraday-cookie_jar (0.0.6)
+ faraday (>= 0.7.4)
+ http-cookie (~> 1.0.0)
+ faraday_middleware (0.12.2)
+ faraday (>= 0.7.4, < 1.0)
+ ffi (1.9.21)
+ ffi (1.9.21-x64-mingw32)
+ ffi (1.9.21-x86-mingw32)
+ ffi-win32-extensions (1.0.3)
+ ffi
+ ffi-yajl (2.3.1)
libyajl2 (~> 1.2)
- foodcritic (6.2.0)
- cucumber-core (>= 1.3)
- erubis
- nokogiri (>= 1.5, < 2.0)
- rake
- rufus-lru (~> 1.0)
- treetop (~> 1.4)
- yajl-ruby (~> 1.1)
- fuzzyurl (0.8.0)
- gherkin (3.2.0)
- github_api (0.13.1)
+ fuzzyurl (0.9.0)
+ gh (0.15.1)
addressable (~> 2.4.0)
- descendants_tracker (~> 0.0.4)
- faraday (~> 0.8, < 0.10)
- hashie (>= 3.4)
- multi_json (>= 1.7.5, < 2.0)
- oauth2
- github_changelog_generator (1.12.0)
- bundler (>= 1.7)
- colorize (~> 0.7)
- github_api (~> 0.12)
- overcommit (>= 0.31)
- rake (>= 10.0)
- rspec (>= 3.2)
- rubocop (>= 0.31)
+ backports
+ faraday (~> 0.8)
+ multi_json (~> 1.0)
+ net-http-persistent (~> 2.9)
+ net-http-pipeline
gssapi (1.2.0)
ffi (>= 1.0.1)
gyoku (1.3.1)
builder (>= 2.1.2)
- halite (1.2.1)
- bundler
- chef (~> 12.0)
- stove (~> 3.2, >= 3.2.3)
- thor
- hashie (3.4.4)
- highline (1.7.8)
- httpclient (2.8.0)
+ hashdiff (0.3.7)
+ hashie (3.5.7)
+ highline (1.7.10)
+ htmlentities (4.3.4)
+ http-cookie (1.0.3)
+ domain_name (~> 0.5)
+ httpclient (2.8.3)
inifile (3.0.0)
- iniparse (1.4.2)
+ iniparse (1.4.4)
+ inspec (2.1.67)
+ addressable (~> 2.4)
+ faraday (>= 0.9.0)
+ hashie (~> 3.4)
+ htmlentities
+ json (>= 1.8, < 3.0)
+ method_source (~> 0.8)
+ mixlib-log
+ parallel (~> 1.9)
+ parslet (~> 1.5)
+ pry (~> 0)
+ rspec (~> 3)
+ rspec-its (~> 1.2)
+ rubyzip (~> 1.1)
+ semverse
+ sslshake (~> 1.2)
+ thor (~> 0.20)
+ tomlrb (~> 1.2)
+ train (~> 1.4)
ipaddress (0.8.3)
- jmespath (1.2.4)
- json_pure (>= 1.8.1)
- json (1.8.3)
- json_pure (1.8.3)
- jwt (1.5.1)
- knife-windows (1.4.0)
- winrm (~> 1.7)
+ iso8601 (0.9.1)
+ jmespath (1.4.0)
+ json (2.1.0)
+ launchy (2.4.3)
+ addressable (~> 2.3)
libyajl2 (1.2.0)
little-plugger (1.1.4)
- logging (2.1.0)
+ logging (2.2.2)
little-plugger (~> 1.1)
multi_json (~> 1.10)
- logify (0.2.0)
- method_source (0.8.2)
- mime-types (3.0)
- mime-types-data (~> 3.2015)
- mime-types-data (3.2016.0221)
- mini_portile2 (2.0.0)
- mixlib-authentication (1.4.0)
+ method_source (0.9.0)
+ mixlib-archive (0.4.6)
mixlib-log
- rspec-core (~> 3.2)
- rspec-expectations (~> 3.2)
- rspec-mocks (~> 3.2)
- mixlib-cli (1.5.0)
- mixlib-config (2.2.1)
- mixlib-install (1.0.11)
- artifactory
- mixlib-shellout
- mixlib-versioning
- mixlib-log (1.6.0)
- mixlib-shellout (2.2.6)
- mixlib-shellout (2.2.6-universal-mingw32)
+ mixlib-authentication (2.0.0)
+ mixlib-cli (1.7.0)
+ mixlib-config (2.2.6)
+ tomlrb
+ mixlib-log (2.0.4)
+ mixlib-shellout (2.3.2)
+ mixlib-shellout (2.3.2-universal-mingw32)
win32-process (~> 0.8.2)
wmi-lite (~> 1.0)
- mixlib-versioning (1.1.0)
- multi_json (1.11.3)
- multi_xml (0.5.5)
+ ms_rest (0.7.2)
+ concurrent-ruby (~> 1.0)
+ faraday (~> 0.9)
+ timeliness (~> 0.3)
+ ms_rest_azure (0.10.6)
+ concurrent-ruby (~> 1.0)
+ faraday (~> 0.9)
+ faraday-cookie_jar (~> 0.0.6)
+ ms_rest (~> 0.7.2)
+ multi_json (1.13.1)
multipart-post (2.0.0)
+ net-http-persistent (2.9.4)
+ net-http-pipeline (1.0.1)
net-scp (1.2.1)
net-ssh (>= 2.6.5)
net-sftp (2.1.2)
net-ssh (>= 2.6.5)
- net-ssh (3.1.1)
- net-ssh-gateway (1.2.0)
- net-ssh (>= 2.6.5)
+ net-ssh (4.2.0)
+ net-ssh-gateway (2.0.0)
+ net-ssh (>= 4.0.0)
net-ssh-multi (1.2.1)
net-ssh (>= 2.6.5)
net-ssh-gateway (>= 1.2.0)
net-telnet (0.1.1)
netrc (0.11.0)
- nokogiri (1.6.7.2)
- mini_portile2 (~> 2.0.0.rc2)
- nokogiri (1.6.7.2-x86-mingw32)
- mini_portile2 (~> 2.0.0.rc2)
nori (2.6.0)
- oauth2 (1.1.0)
- faraday (>= 0.8, < 0.10)
- jwt (~> 1.0, < 1.5.2)
- multi_json (~> 1.3)
- multi_xml (~> 0.5)
- rack (>= 1.2, < 3)
- octokit (4.3.0)
- sawyer (~> 0.7.0, >= 0.5.3)
- ohai (8.15.1)
- chef-config (>= 12.5.0.alpha.1, < 13)
+ octokit (4.8.0)
+ sawyer (~> 0.8.0, >= 0.5.3)
+ ohai (14.1.0)
+ chef-config (>= 12.8, < 15)
ffi (~> 1.9)
ffi-yajl (~> 2.2)
ipaddress
mixlib-cli
mixlib-config (~> 2.0)
- mixlib-log
+ mixlib-log (~> 2.0, >= 2.0.1)
mixlib-shellout (~> 2.0)
plist (~> 3.1)
systemu (~> 2.6.4)
wmi-lite (~> 1.0)
- overcommit (0.33.0)
- childprocess (~> 0.5.8)
- iniparse (~> 1.4)
- parser (2.3.1.0)
- ast (~> 2.2)
- plist (3.2.0)
- poise (2.7.0)
- halite (~> 1.0)
- polyglot (0.3.5)
+ parallel (1.12.1)
+ parser (2.5.1.0)
+ ast (~> 2.4.0)
+ parslet (1.8.2)
+ plist (3.4.0)
powerpack (0.1.1)
proxifier (1.0.3)
- pry (0.10.3)
+ pry (0.11.3)
coderay (~> 1.1.0)
- method_source (~> 0.8.1)
- slop (~> 3.4)
- pry-byebug (3.3.0)
- byebug (~> 8.0)
+ method_source (~> 0.9.0)
+ pry-byebug (3.6.0)
+ byebug (~> 10.0)
pry (~> 0.10)
pry-remote (0.1.8)
pry (~> 0.9)
@@ -305,74 +284,104 @@ GEM
pry-stack_explorer (0.4.9.2)
binding_of_caller (>= 0.7)
pry (>= 0.9.11)
- rack (1.6.4)
- rainbow (2.1.0)
- rake (11.1.2)
- rb-readline (0.5.3)
- retryable (2.0.3)
- rspec (3.4.0)
- rspec-core (~> 3.4.0)
- rspec-expectations (~> 3.4.0)
- rspec-mocks (~> 3.4.0)
- rspec-core (3.4.4)
- rspec-support (~> 3.4.0)
- rspec-expectations (3.4.0)
+ pusher-client (0.6.2)
+ json
+ websocket (~> 1.0)
+ rack (2.0.5)
+ rainbow (3.0.0)
+ rake (12.3.1)
+ rb-readline (0.5.5)
+ rspec (3.7.0)
+ rspec-core (~> 3.7.0)
+ rspec-expectations (~> 3.7.0)
+ rspec-mocks (~> 3.7.0)
+ rspec-core (3.7.1)
+ rspec-support (~> 3.7.0)
+ rspec-expectations (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.4.0)
+ rspec-support (~> 3.7.0)
rspec-its (1.2.0)
rspec-core (>= 3.0.0)
rspec-expectations (>= 3.0.0)
- rspec-mocks (3.4.1)
+ rspec-mocks (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.4.0)
- rspec-support (3.4.1)
+ rspec-support (~> 3.7.0)
+ rspec-support (3.7.1)
rspec_junit_formatter (0.2.3)
builder (< 4)
rspec-core (>= 2, < 4, != 2.12.0)
- rubocop (0.39.0)
- parser (>= 2.3.0.7, < 3.0)
+ rubocop (0.55.0)
+ parallel (~> 1.10)
+ parser (>= 2.5)
powerpack (~> 0.1)
- rainbow (>= 1.99.1, < 3.0)
+ rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
- ruby-prof (0.15.9)
- ruby-progressbar (1.8.0)
+ ruby-prof (0.17.0)
+ ruby-progressbar (1.9.0)
ruby-shadow (2.5.0)
- rubyntlm (0.6.0)
- rufus-lru (1.0.5)
- sawyer (0.7.0)
- addressable (>= 2.3.5, < 2.5)
- faraday (~> 0.8, < 0.10)
- serverspec (2.33.0)
+ rubyntlm (0.6.2)
+ rubyzip (1.2.1)
+ safe_yaml (1.0.4)
+ sawyer (0.8.1)
+ addressable (>= 2.3.5, < 2.6)
+ faraday (~> 0.8, < 1.0)
+ semverse (2.0.0)
+ serverspec (2.41.3)
multi_json
rspec (~> 3.0)
rspec-its
- specinfra (~> 2.53)
- sfl (2.2)
- simplecov (0.11.2)
- docile (~> 1.1.0)
- json (~> 1.8)
+ specinfra (~> 2.72)
+ sfl (2.3)
+ simplecov (0.16.1)
+ docile (~> 1.1)
+ json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
- simplecov-html (0.10.0)
+ simplecov-html (0.10.2)
slop (3.6.0)
- specinfra (2.57.1)
+ specinfra (2.73.3)
net-scp
- net-ssh (>= 2.7, < 4.0)
+ net-ssh (>= 2.7, < 5.0)
net-telnet
sfl
- stove (3.2.8)
- chef-api (~> 0.5)
- logify (~> 0.2)
+ sslshake (1.2.0)
+ structured_warnings (0.3.0)
syslog-logger (1.6.8)
systemu (2.6.5)
- thor (0.19.1)
- thread_safe (0.3.5)
- tomlrb (1.2.1)
- treetop (1.6.5)
- polyglot (~> 0.3)
- ubuntu_ami (0.4.1)
- unicode-display_width (1.0.5)
+ thor (0.20.0)
+ timeliness (0.3.8)
+ tomlrb (1.2.6)
+ train (1.4.4)
+ aws-sdk (~> 2)
+ azure_mgmt_resources (~> 0.15)
+ docker-api (~> 1.26)
+ inifile
+ json (>= 1.8, < 3.0)
+ mixlib-shellout (~> 2.0)
+ net-scp (~> 1.2)
+ net-ssh (>= 2.9, < 5.0)
+ winrm (~> 2.0)
+ winrm-fs (~> 1.0)
+ travis (1.8.8)
+ backports
+ faraday (~> 0.9)
+ faraday_middleware (~> 0.9, >= 0.9.1)
+ gh (~> 0.13)
+ highline (~> 1.6)
+ launchy (~> 2.1)
+ pusher-client (~> 0.4)
+ typhoeus (~> 0.6, >= 0.6.8)
+ typhoeus (0.8.0)
+ ethon (>= 0.8.0)
+ unf (0.1.4)
+ unf_ext
+ unicode-display_width (1.3.2)
uuidtools (2.1.5)
+ webmock (3.4.1)
+ addressable (>= 2.3.6)
+ crack (>= 0.3.2)
+ hashdiff
+ websocket (1.2.5)
win32-api (1.5.3-universal-mingw32)
win32-dir (0.5.1)
ffi (>= 1.0.0)
@@ -380,7 +389,7 @@ GEM
win32-ipc (>= 0.6.0)
win32-eventlog (0.6.3)
ffi
- win32-ipc (0.6.6)
+ win32-ipc (0.7.0)
ffi
win32-mmap (0.4.2)
ffi
@@ -388,59 +397,60 @@ GEM
win32-ipc (>= 0.6.0)
win32-process (0.8.3)
ffi (>= 1.0.0)
- win32-service (0.8.7)
+ win32-service (0.8.10)
ffi
+ ffi-win32-extensions
+ win32-taskscheduler (0.4.0)
+ ffi
+ structured_warnings
windows-api (0.4.4)
win32-api (>= 1.4.5)
- winrm (1.8.0)
+ winrm (2.2.3)
builder (>= 2.1.2)
+ erubis (~> 2.7)
gssapi (~> 1.2)
gyoku (~> 1.0)
httpclient (~> 2.2, >= 2.2.0.2)
logging (>= 1.6.1, < 3.0)
nori (~> 2.0)
- rubyntlm (~> 0.6.0)
+ rubyntlm (~> 0.6.0, >= 0.6.1)
+ winrm-fs (1.2.0)
+ erubis (~> 2.7)
+ logging (>= 1.6.1, < 3.0)
+ rubyzip (~> 1.1)
+ winrm (~> 2.0)
wmi-lite (1.0.0)
- yajl-ruby (1.2.1)
- yard (0.8.7.6)
+ yard (0.9.12)
PLATFORMS
ruby
+ x64-mingw32
x86-mingw32
DEPENDENCIES
appbundler
- bundler
- bundler-audit!
chef!
chef-config!
- chef-provisioning
- chef-provisioning-aws
- chef-rewind
- chef-sugar
- cheffish
- chefspec
+ chef-vault
+ cheffish (~> 14)
chefstyle!
- foodcritic
- github_changelog_generator
- halite
- knife-windows
+ inspec (~> 2)
netrc
- nokogiri
octokit
- poise
pry
pry-byebug
pry-remote
pry-stack_explorer
- rack
rake
rb-readline
ruby-prof
ruby-shadow
simplecov
tomlrb
+ travis
+ unf_ext (= 0.0.7.6)!
+ webmock
yard
BUNDLED WITH
- 1.11.2
+ 1.16.1
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
deleted file mode 100644
index 562e43e797..0000000000
--- a/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,35 +0,0 @@
-## Description
-
-Briefly describe the issue
-
-## Chef Version
-
-Tell us which version of chef-client you are using (see below for Server+ChefDK bugs).
-
-## Platform Version
-
-Tell us which Operating System distribution and version chef-client is running on.
-
-## Replication Case
-
-Tell us what steps to take to replicate your problem. See [How to create a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve)
-for information on how to create a good replication case.
-
-## Client Output
-
-The relevant output of the chef-client run or a link to a gist of the entire run, if there is one.
-
-The debug output (chef-client -l debug) may be useful, but please link to a gist, or truncate it.
-
-## Stacktrace
-
-Please include the stacktrace.out output or link to a gist of it, if there is one.
-
-### NOTE: CHEF CLIENT BUGS ONLY
-
-This issue tracker is for the code contained within this repo -- `chef-client`, base `knife` functionality (not
-plugins), `chef-apply`, `chef-solo`, `chef-client -z`, etc.
-
-* [Server issues](https://github.com/chef/chef-server/issues/new)
-* [ChefDK issues](https://github.com/chef/chef-dk/issues/new)
-* Cookbook Issues (see the https://github.com/chef-cookbooks repos or search [Supermarket](https://supermarket.chef.io) or GitHub/Google)
diff --git a/MAINTAINERS.md b/MAINTAINERS.md
index b0474b5f09..536d9b6629 100644
--- a/MAINTAINERS.md
+++ b/MAINTAINERS.md
@@ -7,7 +7,7 @@ file tells you who needs to review your patch - you need a simple majority of ma
for the relevant subsystems to provide a :+1: on your pull request. Additionally, you need
to not receive a veto from a Lieutenant or the Project Lead.
-Check out [How Chef is Maintained](https://github.com/opscode/chef-rfc/blob/master/rfc030-maintenance-policy.md#how-the-project-is-maintained) for details on the process, how to become
+Check out [How Chef is Maintained](https://github.com/chef/chef-rfc/blob/master/rfc030-maintenance-policy.md#how-the-project-is-maintained) for details on the process, how to become
a maintainer, lieutenant, or the project lead.
# Project Lead
@@ -18,9 +18,7 @@ a maintainer, lieutenant, or the project lead.
## Chef Core
-Handles the core parts of the Chef DSL, base resource and provider
-infrastructure, the Chef applications and [omnibus-chef](https://github.com/chef/omnibus-chef). Includes anything not covered by
-another component.
+Maintainers for the Chef client, Ohai, mixlibs, ChefDK, ChefSpec, Foodcritic, chefstyle, and sundry others.
To mention the team, use @chef/client-core
@@ -30,59 +28,26 @@ To mention the team, use @chef/client-core
### Maintainers
+* [Adam Leff](https://github.com/adamleff)
* [Bryan McLellan](https://github.com/btm)
* [Noah Kantrowitz](https://github.com/coderanger)
* [Daniel DeLeo](https://github.com/danielsdeleo)
* [AJ Christensen](https://github.com/fujin)
* [Phil Dibowitz](https://github.com/jaymzh)
* [Jay Mundrawala](https://github.com/jaym)
+* [John Keiser](https://github.com/jkeiser)
* [Jon Cowie](https://github.com/jonlives)
+* [Joshua Timberman](https://github.com/jtimberman)
* [Lamont Granquist](https://github.com/lamont-granquist)
* [Claire McQuin](https://github.com/mcquin)
-* [Steven Murawski](https://github.com/smurawski)
-* [Tyler Ball](https://github.com/tyler-ball)
-* [Ranjib Dey](https://github.com/ranjib)
* [Matt Wrock](https://github.com/mwrock)
-
-## Ohai
-
-To mention the team, use @chef/ohai
-
-### Lieutenant
-
-* [Claire McQuin](https://github.com/mcquin)
-
-### Maintainers
-
-* [Bryan McLellan](https://github.com/btm)
-* [Tim Smith](https://github.com/tas50)
-
-## Dev Tools
-
-ChefDK, Chef Zero, Knife, Chef Apply and Chef Shell.
-To mention the team, use @chef/client-dev-tools
-
-### Maintainers
-
-* [Daniel DeLeo](https://github.com/danielsdeleo)
-* [Joshua Timberman](https://github.com/jtimberman)
-* [Lamont Granquist](https://github.com/lamont-granquist)
-* [Steven Danna](https://github.com/stevendanna)
-
-## Test Tools
-
-ChefSpec
-To mention the team, use @chef/client-test-tools
-
-### Lieutenant
-
-* [Seth Vargo](https://github.com/sethvargo)
-
-### Maintainers
-
-* [Joshua Timberman](https://github.com/jtimberman)
-* [Lamont Granquist](https://github.com/lamont-granquist)
* [Ranjib Dey](https://github.com/ranjib)
+* [Steven Murawski](https://github.com/smurawski)
+* [Steven Danna](https://github.com/stevendanna)
+* [Tim Smith](https://github.com/tas50)
+* [Tom Duffield](https://github.com/tduffield)
+* [Tyler Ball](https://github.com/tyler-ball)
+* [Josh Hudson](https://github.com/itmustbejj)
## Chef Provisioning
@@ -97,9 +62,9 @@ To mention the team, use @chef/provisioning
### Maintainers
* [John Keiser](https://github.com/jkeiser)
-* [Stuart Preston](https://github.com/stuartpreston)
* [JJ Asghar](https://github.com/jjasghar)
-* [João Cravo](https://github.com/joaogbcravo)
+* [Stuart Preston](https://github.com/stuartpreston)
+* [Harley Alaniz](https://github.com/thehar)
## Platform Specific Components
@@ -129,6 +94,7 @@ To mention the team, use @chef/client-ubuntu
### Maintainers
* [Lamont Granquist](https://github.com/lamont-granquist)
+* [Tim Smith](https://github.com/tas50)
* [Thom May](https://github.com/thommay)
## Windows
@@ -178,6 +144,7 @@ To mention the team, use @chef/client-os-x
### Maintainers
* [Tyler Ball](https://github.com/tyler-ball)
+* [mikedodge04](https://github.com/mikedodge04)
## Debian
@@ -190,30 +157,7 @@ To mention the team, use @chef/client-debian
### Maintainers
* [Lamont Granquist](https://github.com/lamont-granquist)
-
-## Cisco NX-OS
-
-To mention the team, use @chef/client-nxos
-
-### Lieutenant
-
-* [Carl Perry](https://github.com/edolnx)
-
-### Maintainers
-
-* [Carl Perry](https://github.com/edolnx)
-
-## Cisco IOS XR
-
-To mention the team, use @chef/client-iosxr
-
-### Lieutenant
-
-* [Carl Perry](https://github.com/edolnx)
-
-### Maintainers
-
-* [Carl Perry](https://github.com/edolnx)
+* [Tim Smith](https://github.com/tas50)
## Fedora
@@ -230,6 +174,7 @@ To mention the team, use @chef/client-opensuse
### Maintainers
* [Lamont Granquist](https://github.com/lamont-granquist)
+* [Tim Smith](https://github.com/tas50)
## SUSE Enterprise Linux Server
@@ -250,7 +195,7 @@ To mention the team, use @chef/client-freebsd
### Maintainers
* [Cory Stephenson](https://github.com/Aevin1387)
-* [David Aronsohn](https://github.com/tbunnyman)
+* [David Aronsohn](https://github.com/OnlyHaveCans)
* [Bryant Lippert](https://github.com/AgentMeerkat)
## OpenBSD
diff --git a/MAINTAINERS.toml b/MAINTAINERS.toml
index 9501c6d049..ce4f514d89 100644
--- a/MAINTAINERS.toml
+++ b/MAINTAINERS.toml
@@ -12,7 +12,7 @@ file tells you who needs to review your patch - you need a simple majority of ma
for the relevant subsystems to provide a :+1: on your pull request. Additionally, you need
to not receive a veto from a Lieutenant or the Project Lead.
-Check out [How Chef is Maintained](https://github.com/opscode/chef-rfc/blob/master/rfc030-maintenance-policy.md#how-the-project-is-maintained) for details on the process, how to become
+Check out [How Chef is Maintained](https://github.com/chef/chef-rfc/blob/master/rfc030-maintenance-policy.md#how-the-project-is-maintained) for details on the process, how to become
a maintainer, lieutenant, or the project lead.
"""
@@ -28,71 +28,32 @@ a maintainer, lieutenant, or the project lead.
title = "Chef Core"
team = "client-core"
text = """
-Handles the core parts of the Chef DSL, base resource and provider
-infrastructure, the Chef applications and [omnibus-chef](https://github.com/chef/omnibus-chef). Includes anything not covered by
-another component.
+Maintainers for the Chef client, Ohai, mixlibs, ChefDK, ChefSpec, Foodcritic, chefstyle, and sundry others.
"""
lieutenant = "thommay"
maintainers = [
+ "adamleff",
"btm",
"coderanger",
"danielsdeleo",
"fujin",
"jaymzh",
"jaym",
+ "jkeiser",
"jonlives",
+ "jtimberman",
"lamont-granquist",
"mcquin",
+ "mwrock",
+ "ranjib",
"smurawski",
+ "stevendanna",
+ "tas50",
+ "tduffield",
"tyler-ball",
- "ranjib",
- "mwrock"
- ]
-
- [Org.Components.Ohai]
- title = "Ohai"
- team = "ohai"
-
- lieutenant = "mcquin"
-
- maintainers = [
- "btm",
- "tas50"
- ]
-
- [Org.Components.DevTools]
- title = "Dev Tools"
- team = "client-dev-tools"
- text = "ChefDK, Chef Zero, Knife, Chef Apply and Chef Shell."
-
- paths = [
- "lib/chef/knife.rb",
- "lib/chef/knife/",
- "spec/functional/knife/",
- "spec/integration/knife/",
- "spec/unit/knife/"
- ]
-
- maintainers = [
- "danielsdeleo",
- "jtimberman",
- "lamont-granquist",
- "stevendanna"
- ]
-
- [Org.Components.TestTools]
- title = "Test Tools"
- team = "client-test-tools"
- text = "ChefSpec"
-
- lieutenant = "sethvargo"
-
- maintainers = [
- "jtimberman",
- "lamont-granquist",
- "ranjib"
+ "itmustbejj"
]
[Org.Components.Provisioning]
@@ -106,9 +67,9 @@ Chef Provisioning and Drivers. Supported Drivers are listed in the [README](htt
maintainers = [
"jkeiser",
- "stuartpreston",
"jjasghar",
- "joaogbcravo",
+ "stuartpreston",
+ "thehar"
]
[Org.Components.Subsystems]
@@ -136,6 +97,7 @@ The specific components of Chef related to a given platform - including (but not
maintainers = [
"lamont-granquist",
+ "tas50",
"thommay"
]
@@ -175,7 +137,8 @@ The specific components of Chef related to a given platform - including (but not
lieutenant = "jtimberman"
maintainers = [
- "tyler-ball"
+ "tyler-ball",
+ "mikedodge04"
]
[Org.Components.Subsystems.Debian]
@@ -185,27 +148,8 @@ The specific components of Chef related to a given platform - including (but not
lieutenant = "thommay"
maintainers = [
- "lamont-granquist"
- ]
-
- [Org.Components.Subsystems.CiscoNXOS]
- title = "Cisco NX-OS"
- team = "client-nxos"
-
- lieutenant = "adamleff"
-
- maintainers = [
- "adamleff"
- ]
-
- [Org.Components.Subsystems.CiscoIOSXR]
- title = "Cisco IOS XR"
- team = "client-iosxr"
-
- lieutenant = "adamleff"
-
- maintainers = [
- "adamleff"
+ "lamont-granquist",
+ "tas50"
]
[Org.Components.Subsystems.Fedora]
@@ -221,7 +165,8 @@ The specific components of Chef related to a given platform - including (but not
team = "client-opensuse"
maintainers = [
- "lamont-granquist"
+ "lamont-granquist",
+ "tas50"
]
[Org.Components.Subsystems."SUSE Enterprise Linux"]
@@ -240,7 +185,7 @@ The specific components of Chef related to a given platform - including (but not
maintainers = [
"Aevin1387",
- "tBunnyMan",
+ "OnlyHaveCans",
"AgentMeerkat"
]
@@ -348,9 +293,9 @@ The specific components of Chef related to a given platform - including (but not
Name = "Steven Danna"
GitHub = "stevendanna"
- [people.tBunnyMan]
+ [people.OnlyHaveCans]
Name = "David Aronsohn"
- GitHub = "tbunnyman"
+ GitHub = "OnlyHaveCans"
IRC = "tBunnyMan"
Twitter = "OnlyHaveCans"
@@ -391,6 +336,8 @@ The specific components of Chef related to a given platform - including (but not
[people.tas50]
Name = "Tim Smith"
GitHub = "tas50"
+ Twitter = "tas50"
+ IRC = "tas50"
[people.jkeiser]
Name = "John Keiser"
@@ -406,7 +353,22 @@ The specific components of Chef related to a given platform - including (but not
Twitter = "jjasghar"
IRC = "j^2"
- [people.joaogbcravo]
- Name = "João Cravo"
- GitHub = "joaogbcravo"
- Twitter = "joaogbcravo"
+ [people.thehar]
+ Name = "Harley Alaniz"
+ GitHub = "thehar"
+
+ [people.tduffield]
+ Name = "Tom Duffield"
+ GitHub = "tduffield"
+
+ [people.mikedodge04]
+ Name = "mikedodge04"
+ GitHub = "mikedodge04"
+ Twitter = "mikedodge04"
+ IRC = "mikedodge04"
+
+ [people.itmustbejj]
+ Name = "Josh Hudson"
+ GitHub = "itmustbejj"
+ Twitter = "itmustbejj"
+ IRC = "itmustbejj"
diff --git a/NOTICE b/NOTICE
index 19223a40bb..932967f59c 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,7 +1,7 @@
Chef NOTICE
===========
-Developed at Opscode (http://www.opscode.com).
+Developed at Chef (https://www.chef.io).
Contributors and Copyright holders:
diff --git a/README.md b/README.md
index 4697e55190..4c3684cbc9 100644
--- a/README.md
+++ b/README.md
@@ -2,16 +2,20 @@
[![Code Climate](https://codeclimate.com/github/chef/chef.svg)](https://codeclimate.com/github/chef/chef)
[![Build Status Master](https://travis-ci.org/chef/chef.svg?branch=master)](https://travis-ci.org/chef/chef)
[![Build Status Master](https://ci.appveyor.com/api/projects/status/github/chef/chef?branch=master&svg=true&passingText=master%20-%20Ok&pendingText=master%20-%20Pending&failingText=master%20-%20Failing)](https://ci.appveyor.com/project/Chef/chef/branch/master)
+[![Gem Version](https://badge.fury.io/rb/chef.svg)](https://badge.fury.io/rb/chef)
+[![](https://img.shields.io/badge/Release%20Policy-Cadence%20Release-brightgreen.svg)](https://github.com/chef/chef-rfc/blob/master/rfc086-chef-oss-project-policies.md#cadence-release)
-Want to try Chef? Get started with [learnchef](https://learn.chef.io)
+## Getting Started
-* Documentation: [http://docs.chef.io](http://docs.chef.io)
-* Source: [http://github.com/chef/chef/tree/master](http://github.com/chef/chef/tree/master)
-* Tickets/Issues: [https://github.com/chef/chef/issues](https://github.com/chef/chef/issues)
-* IRC: `#chef` and `#chef-hacking` on Freenode
- - Join via browser: [#chef](https://webchat.freenode.net/?channels=chef), [#chef-hacking](https://webchat.freenode.net/?channels=chef-hacking)
- - View logs: [#chef](https://botbot.me/freenode/chef/), [#chef-hacking](https://botbot.me/freenode/chef-hacking/)
-* Mailing list: [https://discourse.chef.io](https://discourse.chef.io)
+Want to try Chef?
+For Chef user, please refer to [Quick Start](https://docs.chef.io/quick_start.html)
+For more details, please refer to [learnchef](https://learn.chef.io)
+
+- Documentation: <https://docs.chef.io>
+- Source: <https://github.com/chef/chef/tree/master>
+- Tickets/Issues: <https://github.com/chef/chef/issues>
+- Slack: [Chef Community Slack](https://community-slack.chef.io/)
+- Mailing list: <https://discourse.chef.io>
Chef is a configuration management tool designed to bring automation to your
entire infrastructure.
@@ -19,122 +23,127 @@ entire infrastructure.
This README focuses on developers who want to modify Chef source code.
If you just want to use Chef, check out these resources:
-* [learnchef](https://learn.chef.io): Getting started guide
-* [docs.chef.io](http://docs.chef.io): Comprehensive User Docs
-* [Installer Downloads](https://www.chef.io/download-chef-client/): Install Chef as a complete package
+- [learnchef](https://learn.chef.io): Getting started guide
+- [docs.chef.io](https://docs.chef.io): Comprehensive User Docs
+- [Installer Downloads](https://downloads.chef.io/chef/): Install Chef as a complete package
+- [chef/chef](https://hub.docker.com/r/chef/chef): Docker image for use with [kitchen-dokken](https://github.com/someara/kitchen-dokken)
+
+## Reporting Issues
+
+Issues can be reported by using [GitHub Issues](https://github.com/chef/chef/issues).
+
+Full details on how to report issues can be found in the [CONTRIBUTING](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#-chef-issue-tracking) doc.
+
+Note that this repository is primarily for reporting chef-client issues.
+For reporting issues against other Chef projects, please look up the appropriate repository
+to report issues against in the Chef docs in the
+[community contributions section](https://docs.chef.io/community_contributions.html#issues-and-bug-reports).
+If you can't determine the appropriate place to report an issue, then please open it
+against the repository you think best fits and it will be directed to the appropriate project.
+
+## Installing From Git for Developers
-## Installing From Git
+**NOTE:** As a Chef user, please download the omnibus package of [Chef](https://downloads.chef.io/chef) or [Chef-DK](https://downloads.chef.io/chef)
-**NOTE:** Unless you have a specific reason to install from source (to
-try a new feature, contribute a patch, or run chef on an OS for which no
-package is available), you should head to the [installer page](https://www.chef.io/download-chef-client/)
-to get a prebuilt package.
+We do not recommend installing from gems, or building from source. The following instructions apply only to those doing software development on Chef.
### Prerequisites
-Install these via your platform's preferred method (`apt`, `yum`, `ports`,
-`emerge`, etc.):
+Install:
* git
-* C compiler, header files, etc. On Ubuntu/Debian, use the
- `build-essential` package.
-* ruby 2.0.0 or later
+* C compiler, header files, etc.
+* ruby 2.4 or later
* rubygems
-* bundler
+* bundler gem
+
+We support too many platforms, and there are too many different ways to manage ruby installs, so
+it is assumed the user understands how to accomplish this for their platform and needs (see previous
+note about downloading the pre-built omnibus install if you do not understand how to accomplish this).
### Chef Installation
Then get the source and install it:
```bash
-# Clone this repo
git clone https://github.com/chef/chef.git
-
-# cd into the source tree
cd chef
-
-# Install dependencies with bundler
bundle install
-
-# Build a gem
bundle exec rake gem
-
-# Install the gem you just built
-gem install pkg/chef-VERSION.gem
+bundle exec rake install
```
## Contributing/Development
-Before working on the code, if you plan to contribute your changes, you need to
-read the
-[Chef Contributions document](http://docs.chef.io/community_contributions.html).
+Please read our [Community Contributions Guidelines](https://docs.chef.io/community_contributions.html), and
+ensure you are signing all your commits with DCO sign-off.
The general development process is:
1. Fork this repo and clone it to your workstation.
2. Create a feature branch for your change.
3. Write code and tests.
-4. Push your feature branch to github and open a pull request against
- master.
+4. Push your feature branch to github and open a pull request against master.
-Once your repository is set up, you can start working on the code. We do use
-TDD with RSpec, so you'll need to get a development environment running.
-Follow the above procedure ("Installing from Git") to get your local
-copy of the source running.
-
-## Reporting Issues
-
-Issues can be reported by using [GitHub Issues](https://github.com/chef/chef/issues).
-
-Full details on how to report issues can be found in the [CONTRIBUTING](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#-chef-issue-tracking) doc.
-
-Note that this repository is primarily for reporting chef-client issues.
-For reporting issues against other Chef projects, please look up the appropriate repository
-to report issues against in the Chef docs in the
-[community contributions section](https://docs.chef.io/community_contributions.html#issues-and-bug-reports).
-If you can't detemine the appropriate place to report an issue, then please open it
-against the repository you think best fits and it will be directed to the appropriate project.
+Once your repository is set up, you can start working on the code. We do utilize
+RSpec for test driven development, so you'll need to get a development
+environment running. Follow the above procedure ("Installing from Git") to get
+your local copy of the source running.
## Testing
-We use RSpec for unit/spec tests. It is not necessary to start the development
-environment to run the specs--they are completely standalone.
+This repository only uses rspec for testing.
```bash
-# Run All the Tests
-bundle exec rake spec
+# all tests
+bundle exec rspec
-# Run a Single Test File
+# single test
bundle exec rspec spec/PATH/TO/FILE_spec.rb
-# Run a Subset of Tests
+# all tests under a subdir
bundle exec rspec spec/PATH/TO/DIR
```
-When you submit a pull request, we will automatically run the functional and unit
-tests in spec/functional/ and spec/unit/ respectively. These will be run on Ubuntu
-through Travis CI, and on Windows through AppVeyor. The status of these runs will
-be displayed with your pull request.
+When you submit a PR rspec tests will run automatically on travis and appveyor.
## Building the Full Package
-To build chef as a standalone package (with ruby and all dependent libraries included in a .deb, .rpm, .pkg or .msi), we use the omnibus system. Go to the [omnibus README](omnibus/README.md) to find out how to build!
+To build chef as a standalone package, we use the [omnibus](omnibus/README.md) system.
+
+To build:
+
+```bash
+git clone https://github.com/chef/chef.git
+cd chef/omnibus
+bundle install
+bundle exec omnibus build chef
+```
+
+The prerequisites necessary to run omnibus itself are not documented here. The automation we use is
+the [opscode-ci cookbook](https://github.com/chef-cookbooks/opscode-ci) cookbook, which serves as the most
+current documentation.
## Updating Dependencies
If you want to change our constraints (change which packages and versions we accept in the chef), there are several places to do so:
-* To add or remove a gem from chef, or update a gem version, edit [Gemfile](Gemfile).
-* To change the version of binary packages, edit [version_policy.rb](version_policy.rb).
-* To add new packages to chef, edit [omnibus/config/projects/chef.rb](omnibus/config/projects/chef.rb).
+* [Gemfile](Gemfile) and [Gemfile.lock](Gemfile.lock): All gem version constraints (update with `bundle update`)
+* [omnibus_overrides.rb](omnibus_overrides_rb): Pinned versions of omnibus packages.
+* [omnibus/Gemfile](omnibus/Gemfile) and [omnibus/Gemfile.lock](omnibus/Gemfile.lock): Gems for the omnibus build system itself.
+
+In addition there are several places versions are pinned for CI tasks:
-Once you've made any changes you want, you have to update the lockfiles that actually drive the build:
-* To update chef's gem dependencies to the very latest versions available, run `rake bundle:update`.
-* To update chef's gem dependencies *conservatively* (changing as little as possible), run `rake bundle:install`.
-* To update specific gems only, run `rake bundle:update[gem1 gem2 ...]`
-* **`bundle update` and `bundle install` will *not* work, on purpose:** the rake task handles both the windows and non-windows lockfiles and updates them in sync.
+* [kitchen-tests/Gemfile](kitchen-tests/Gemfile) and [kitchen-tests/Gemfile.lock](kitchen-tests/Gemfile.lock): Gems for test-kitchen tests (travis)
-To perform a full update of all dependencies in chef (including binary packages, tests and build system dependencies), run `rake dependencies`. This will update the `Gemfile.lock`, `omnibus/Gemfile.lock`, `acceptance/Gemfile.lock`, `omnibus/Berksfile.lock`, and `omnibus_overrides.rb`. It will also show you any outdated dependencies due to conflicting constraints. Some outdated dependencies are to be expected; it will inform you if any new ones appear that we don't know about, and tell you how to proceed.
+In order to update everything run `rake dependencies`. Note that the [Gemfile.lock](Gemfile.lock) pins windows platforms and to fully regenerate the lockfile
+you must use the following commands or run `rake dependencies:update_gemfile_lock`:
+
+```bash
+bundle lock --update --add-platform ruby
+bundle lock --update --add-platform x64-mingw32
+bundle lock --update --add-platform x86-mingw32
+```
# How Chef Builds and Versions
@@ -142,57 +151,68 @@ Chef is an amalgam of many components. These components update all the time, nec
## Chef Packages
-Chef is distributed as packages for debian, rhel, ubuntu, windows and os/x. It includes a large number of components from various sources, and these are versioned and maintained separately from chef project, which bundles them all together conveniently for the user.
+Chef is distributed as packages for debian, rhel, ubuntu, windows, solaris, aix, and os x. It includes a large number of components from various sources, and these are versioned and maintained separately from chef project, which bundles them all together conveniently for the user.
These packages go through several milestones:
- `master`: When code is checked in to master, the patch version of chef is bumped (e.g. 0.9.10 -> 0.9.11) and a build is kicked off automatically to create and test the packages in Chef's Jenkins cluster.
- `unstable`: When a package is built, it enters the unstable channel. When all packages for all OS's have successfully built, the test phase is kicked off in Jenkins across all supported OS's. These builds are password-protected and generally only available to the test systems.
-- `current`: If the packages pass all the tests on all supported OS's, it is promoted as a unit to `current`, and is available via Chef's artifactory by running `curl https://omnitruck.chef.io/install.sh | sudo bash -s -- -c current -P chef`
+- `current`: If the packages pass all the tests on all supported OS's, it is promoted as a unit to `current`, and is available via Chef's artifactory by running `curl https://www.chef.io/chef/install.sh | sudo bash -s -- -c current -P chef`
- `stable`: Periodically, Chef will pick a release to "bless" for folks who would like a slower update schedule than "every time a build passes the tests." When this happens, it is manually promoted to stable and an announcement is sent to the list. It can be reached at https://downloads.chef.io or installed using the `curl` command without specifying `-c current`. Packages in `stable` are no longer available in `current`.
Additionally, periodically Chef will update the desired versions of chef components and check that in to `master`, triggering a new build with the updated components in it.
## Automated Version Bumping
-Whenever a change is checked in to `master`, the patch version of `chef` is bumped. To do this, the `lita-versioner` bot listens to github for merged PRs, and when it finds one, takes these actions:
+Whenever a change is checked in to `master`, the patch version of `chef` is bumped. To do this, the `chef-ci` bot listens to GitHub for merged PRs, and when it finds one, takes these actions:
-1. Bumps the patch version in `lib/chef/version.rb` (e.g. 0.9.14 -> 0.9.15).
-2. Runs `rake dependencies:update_conservative` to update the `Gemfile.lock` to include the new version.
+1. Bumps the patch version (e.g. 14.1.14 -> 14.1.15) by running ./ci/version_bump.sh
+2. Updates the changelog with the new pull request and current point release
3. Pushes to `master` and submits a new build to Chef's Jenkins cluster.
-## Component Versions
+## Bumping the minor version of Chef
-Chef has two sorts of component: ruby components like `berkshelf` and `test-kitchen`, and binary components like `openssl` and even `ruby` itself.
+After each "official" stable release we need to bump the minor version. To do this:
-In general, you can find all chef desired versions in the [Gemfile](Gemfile) and [version_policy.rb](version_policy.rb) files. The [Gemfile.lock](Gemfile.lock) is the locked version of the Gemfile, and [omnibus_overrides](omnibus_overrides.rb) is the locked version of omnibus. [build](omnibus/Gemfile) and [test](acceptance/Gemfile) Gemfiles and [Berksfile](omnibus/Berksfile) version the toolset we use to build and test.
+1. Run `bundle exec rake version:bump_minor`
-### Binary Components
+Submit a PR with the changes made by the above.
-The versions of binary components (as well as rubygems and bundler, which can't be versioned in a Gemfile) are stored in [version_policy.rb](version_policy.rb) (the `OMNIBUS_OVERRIDES` constant) and locked in [omnibus_overrides](omnibus_overrides.rb). `rake dependencies` will update the `bundler` version, and the rest are be updated manually by Chef every so often.
+## Addressing a Regression
-These have software definitions either in [omnibus/config/software](omnibus/config/software) or, more often, in the [omnibus-software](https://github.com/chef/omnibus-software/tree/master/config/software) project.
+Sometimes, regressions split through the cracks. Since new functionality is always being added and the minor version is bumped immediately after release, we can't simply roll forward. In this scenario, we'll need to perform a special regression release process. In the example that follows, the stable release with a regression is `1.10.60` while master is currently sitting at `1.11.30`. *Note:* To perform this process, you must be a Chef employee.
-### Rubygems Components
+1. If the regression has not already been addressed, open a Pull Request against master with the fix.
+2. Wait until that Pull Request has been merged and `1.11.31` has passed all the necessary tests and is available in the current channel.
+3. Inspect the Git history and find the `SHA` associated with the Merge Commit for the Pull Request above.
+4. Apply the fix for the regression via a cherry-pick:
+ 1. Check out the stable release tag: `git checkout v1.10.60`
+ 2. Cherry Pick the SHA with the fix: `git cherry-pick SHA`
+ 3. Address any conflicts (if necessary)
+ 4. Tag the sha with the appropriate version: `git tag -a v1.10.61 -m "Release v1.10.61"`
+ 5. Push the new tag to origin: `git push origin --tags`
+5. Log in to Jenkins and trigger a `chef-trigger-release` job specifying the new tag as the `GIT_REF`.
-Most of the actual front-facing software in chef is composed of ruby projects. berkshelf, test-kitchen and even chef itself are made of ruby gems. Chef uses the typical ruby way of controlling rubygems versions, the `Gemfile`. Specifically, the `Gemfile` at the top of chef repository governs the version of every single gem we install into chef package. It's a one-stop shop.
+## Component Versions
-Our rubygems component versions are locked down with `Gemfile.lock`, and can be updated with `rake dependencies`.
+Chef has two sorts of component: ruby components like `berkshelf` and `test-kitchen`, and binary components like `openssl` and even `ruby` itself.
-There are three gems versioned outside the `Gemfile`: `rubygems`, `bundler` and `chef`. `rubygems` and `bundler` are in the `RUBYGEMS_AT_LATEST_VERSION` constant in [version_policy.rb](version-policy.rb) and locked in [omnibus_overrides](omnibus_overrides.rb). They are kept up to date by `rake dependencies`.
+In general, you can find all chef desired versions in the [Gemfile](Gemfile) and [omnibus_overrides.rb](omnibus_overrides.rb) files. The [Gemfile.lock](Gemfile.lock) is the locked version of the Gemfile.
-**Windows**: [Gemfile.lock](Gemfile.lock) is generated platform-agnostic, and then generated again for Windows. The one file has the solution for both Linux and Windows.
+### Binary Components
-The tool we use to generate Windows-specific lockfiles on non-Windows machines is [tasks/bin/bundle-platform](bundle-platform), which takes the first argument and sets `Gem.platforms`, and then calls `bundle` with the remaining arguments.
+The versions of binary components (as well as rubygems and bundler, which can't be versioned in a Gemfile) are stored in [omnibus_overrides.rb](omnibus_overrides.rb).
-### Build Tooling Versions
+These have software definitions either in [omnibus/config/software](omnibus/config/software) or, more often, in the [omnibus-software](https://github.com/chef/omnibus-software/tree/master/config/software) project.
-Of special mention is the software we use to build omnibus itself. There are two distinct bits of code that control the versions of compilers, make, git, and other tools we use to build.
+### Rubygems Components
-First, the Jenkins machines that run the build are configured entirely by the [opscode-ci cookbook](https://github.com/chef-cookbooks/opscode-ci) cookbook. They install most of the tools we use via `build-essentials`, and standardize the build environment so we can tear down and bring up builders at will. These machines are kept alive long-running, are periodically updated by Chef to the latest opscode-ci, omnibus and build-essentials cookbooks.
+Our rubygems component versions are locked down with `Gemfile.lock`, and can be updated with `bundle update` or `rake dependencies:update_gemfile_lock`.
-Second, the version of omnibus we use to build chef is governed by `omnibus/Gemfile`. When software definitions or the omnibus framework is updated, this is the file that drives whether we pick it up.
+### Build Tooling Versions
-The omnibus tooling versions are locked down with `omnibus/Gemfile.lock`, and can be updated by running `rake dependencies`.
+The external environment necessary to build omnibus (compilers, make, git, etc) is configured by the [opscode-ci cookbook](https://github.com/chef-cookbooks/opscode-ci) cookbook. In order to reliably create omnibus builds that cookbook should be used to install the prerequisites. It may be possible to install the latest version
+of utilities on a suitably recent distribution and be able to build an omnibus package, but the necessary prerequisites will not be documented here. In most
+cases a recent MacOS with Xcode and a few homebrew packages or a recent Ubuntu distribution with packages like `build-essentials` should suffice.
### Test Versions
@@ -200,67 +220,6 @@ chef is tested by the [chef-acceptance framework](https://github.com/chef/chef-a
The test tooling versions are locked down with `acceptance/Gemfile.lock`, which can be updated by running `rake dependencies`.
-## The Build Process
-
-The actual Chef build process is done with Omnibus, and has several general steps:
-
-1. `bundle install` from `chef/Gemfile.lock`
-2. Reinstall any gems that came from git or path using `rake install`
-3. appbundle chef, chef, test-kitchen and berkshelf
-4. Put miscellaneous powershell scripts and cleanup
-
-### Kicking Off The Build
-
-The build is kicked off in Jenkins by running this on the machine (which is already the correct OS and already has the correct dependencies, loaded by the `omnibus` cookbook):
-
-```
-load-omnibus-toolchain.bat
-cd chef/omnibus
-bundle install
-bundle exec omnibus build chef
-```
-
-This causes the [chef project definition](omnibus/config/projects/chef.rb) to load, which runs the [chef-complete](omnibus/config/software/chef-complete.rb) software definition, the primary software definition driving the whole build process. The reason we embed it all in a software definiton instead of the project is to take advantage of omnibus caching: omnibus will invalidate the entire project (and recompile ruby, openssl, and everything else) if you change anything at all in the project file. Not so with a software definition.
-
-### Installing the Gems
-
-The primary build definition that installs the many Chef rubygems is [`software/chef.rb`](omnibus/software/chef.rb). This has dependencies on any binary libraries, ruby, rubygems and bundler. It has a lot of steps, so it uses a [library](omnibus/files/chef/build-chef.rb) to help reuse code and make it manageable to look at.
-
-What it does:
-
-1. Depends on software defs for pre-cached gems (see "Gems and Caching" below).
-2. Installs all gems from the bundle:
- - Sets up a `.bundle/config` ([code](omnibus/files/chef/build-chef.rb#L17-L39)) with --retries=4, --jobs=1, --without=development,no_<platform>, and `build.config.nokogiri` to pass.
- - Sets up a common environment, standardizing the compilers and flags we use, in [`env`](omnibus/files/chef-gem/build-chef-gem.rb#L32-L54).
- - [Runs](omnibus/config/software/chef.rb#L68) `bundle install --verbose`
-3. Reinstalls any gems that were installed via path:
- - [Runs](omnibus/files/chef/build-chef.rb#L80) `bundle list --paths` to get the installed directories of all gems.
- - For each gem not installed in the main gem dir, [runs](omnibus/files/chef/build-chef.rb#L89) `rake install` from the installed gem directory.
- - [Deletes](omnibus/files/chef/build-chef.rb#L139-L143) the bundler git cache and path- and git-installed gems from the build.
-4. [Creates](omnibus/files/chef/build-chef.rb#L102-L152) `/opt/chef/Gemfile` and `/opt/chef/Gemfile.lock` with the gems that were installed in the build.
-
-#### Gems and Caching
-
-Some gems take a super long time to install (particularly native-compiled ones such as nokogiri and dep-selector-libgecode) and do not change version very often. In order to avoid doing this work every time, we take advantage of omnibus caching by separating out these gems into their own software definitions. [chef-gem-dep-selector-libgecode](omnibus/config/software/chef-gem-dep-selector-libgecode.rb) for example.
-
-Each of these gems uses the `config/files/chef-gem/build-chef-gem` library to define itself. The name of the software definition itself indicates the .
-
-We only create software definitions for long-running gems. Everything else is just installed in the [chef](omnibus/config/software/chef.rb) software definition in a big `bundle install` catchall.
-
-Most gems we just install in the single `chef` software definition.
-
-The first thing
-
-### Appbundling
-
-After the gems are installed, we *appbundle* them in [chef-appbundle](omnibus/config/software/chef-appbundle.rb). This creates binstubs that use the bundle to pin the software .
-
-During the process of appbundling, we update the gem's `Gemfile` to include the locks in the top level `/opt/chef/Gemfile.lock`, so we can guarantee they will never pick up things outside the build. We then run `bundle lock` to update the gem's `Gemfile.lock`, and `bundle check` to ensure all the gems are actually installed. The appbundler then uses these pins.
-
-### Other Cleanup
-
-Finally, chef does several more steps including installing powershell scripts and shortcuts, and removing extra documentation to keep the build slim.
-
# License
Chef - A configuration management system
@@ -271,6 +230,7 @@ Chef - A configuration management system
| **Copyright:** | Copyright 2008-2016, Chef Software, Inc.
| **License:** | Apache License, Version 2.0
+```
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -282,3 +242,4 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+```
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index b25aa52784..4e26819de6 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,4 +1,1356 @@
-*This file holds "in progress" release notes for the current release under development and is intended for consumption by the Chef Documentation team.
-Please see `https://docs.chef.io/release/<major>-<minor>/release_notes.html` for the official Chef release notes.*
+This file holds "in progress" release notes for the current release under development and is intended for consumption by the Chef Documentation team. Please see <https://docs.chef.io/release_notes.html> for the official Chef release notes.
-# Chef Client Release Notes 12.10:
+# Chef Client Release Notes 14.1.1:
+
+## Platform Additions
+
+Enable Ubuntu-18.04 and Debian-9 tested chef-client packages.
+
+# Chef Client Release Notes 14.1:
+
+## Windows Task
+
+The `windows_task` resource has been entirely rewritten. This resolves a
+large number of bugs, including being able to correctly set the start
+time of tasks, proper creation and deletion of tasks, and improves
+Chef's validation of tasks. The rewrite will also solve the idempotency
+problems that users have reported.
+
+## build_essential
+
+The `build_essential` resource no longer requires a name, similar to the `apt_update` resource.
+
+## Ignore Failure
+
+The `ignore_failure` property takes a new argument, `:quiet`, to
+suppress the error output when the resource does in fact fail.
+
+## This release of Chef Client 14 resolves a number of regressions in 14.0
+
+- On Windows, the installer now correctly re-extracts files during repair mode
+- Fix a number of issues relating to use with Red Hat Satellite
+- Git fetch now prunes remotes before running
+- Fix locking and unlocking packages with apt and zypper
+- Ensure we don't request every remote file when running with lazy loading enabled
+- The sysctl resource correctly handles missing keys when used with `ignore_error`
+- --recipe-url apparently never worked on Windows. Now it does.
+
+# Ohai Release Notes 14.1:
+
+## Configurable DMI Whitelist
+
+The whitelist of DMI IDs is now user configurable using the
+`additional_dmi_ids` configuration setting, which takes an Array.
+
+## Shard plugin
+
+The Shard plugin has been returned to a default plugin rather than an
+optional one. To ensure we work in FIPS environments, the plugin will
+use SHA256 rather than MD5 in those environments.
+
+## SCSI plugin
+
+A new plugin to enumerate SCSI devices has been added. This plugin is optional.
+
+# Chef Client Release Notes 14.0.202:
+
+This release of Chef 14 resolves several regressions in the Chef 14.0 release.
+
+- Resources contained in cookbooks would be used instead of built-in Chef client resources causing older resources to run
+- Resources failed due to a missing `property_is_set?` and `resources` methods
+- `yum_package` changed the order of `disablerepo` and `enablerepo` options
+- Depsolving large numbers of cookbooks with chef zero/local took a very long time
+
+# Chef Client Release Notes 14.0:
+
+## New Resources
+
+Chef 14 includes a large number of resources ported from community cookbooks. These resources have been tested, improved, and had their functionality expanded. With these new resources in the Chef Client itself, the need for external cookbook dependencies and dependency management has been greatly reduced.
+
+### build_essential
+
+Use the build_essential resource to install packages required for compiling C software from source. This resource was ported from the `build-essential` community cookbook.
+
+`Note`: This resource no longer configures msys2 on Windows systems.
+
+### chef_handler
+
+Use the chef_handler resource to install or uninstall Chef reporting/exception handlers. This resource was ported from the `chef_handler` community cookbook.
+
+### dmg_package
+
+Use the dmg_package resource to install a dmg 'package'. The resource will retrieve the dmg file from a remote URL, mount it using hdiutil, copy the application (.app directory) to the specified destination (/Applications), and detach the image using hdiutil. The dmg file will be stored in the Chef::Config[:file_cache_path]. This resource was ported from the `dmg` community cookbook.
+
+### homebrew_cask
+
+Use the homebrew_cask resource to install binaries distributed via the Homebrew package manager. This resource was ported from the `homebrew` community cookbook.
+
+### homebrew_tap
+
+Use the homebrew_tap resource to add additional formula repositories to the Homebrew package manager. This resource was ported from the `homebrew` community cookbook.
+
+### hostname
+
+Use the hostname resource to set the system's hostname, configure hostname and hosts config file, and re-run the Ohai hostname plugin so the hostname will be available in subsequent cookbooks. This resource was ported from the `chef_hostname` community cookbook.
+
+### macos_userdefaults
+
+Use the macos_userdefaults resource to manage the macOS user defaults system. The properties of this resource are passed to the defaults command, and the parameters follow the convention of that command. See the defaults(1) man page for details on how the tool works. This resource was ported from the `mac_os_x` community cookbook.
+
+### ohai_hint
+
+Use the ohai_hint resource to pass hint data to Ohai to aid in configuration detection. This resource was ported from the `ohai` community cookbook.
+
+### openssl_dhparam
+
+Use the openssl_dhparam resource to generate dhparam.pem files. If a valid dhparam.pem file is found at the specified location, no new file will be created. If a file is found at the specified location but it is not a valid dhparam file, it will be overwritten. This resource was ported from the `openssl` community cookbook.
+
+### openssl_rsa_private_key
+
+Use the openssl_rsa_private_key resource to generate RSA private key files. If a valid RSA key file can be opened at the specified location, no new file will be created. If the RSA key file cannot be opened, either because it does not exist or because the password to the RSA key file does not match the password in the recipe, it will be overwritten. This resource was ported from the `openssl` community cookbook.
+
+### openssl_rsa_public_key
+
+Use the openssl_rsa_public_key resource to generate RSA public key files given a RSA private key. This resource was ported from the `openssl` community cookbook.
+
+### rhsm_errata
+
+Use the rhsm_errata resource to install packages associated with a given Red Hat Subscription Manager Errata ID. This is helpful if packages to mitigate a single vulnerability must be installed on your hosts. This resource was ported from the `redhat_subscription_manager` community cookbook.
+
+### rhsm_errata_level
+
+Use the rhsm_errata_level resource to install all packages of a specified errata level from the Red Hat Subscription Manager. For example, you can ensure that all packages associated with errata marked at a 'Critical' security level are installed. This resource was ported from the `redhat_subscription_manager` community cookbook.
+
+### rhsm_register
+
+Use the rhsm_register resource to register a node with the Red Hat Subscription Manager or a local Red Hat Satellite server. This resource was ported from the `redhat_subscription_manager` community cookbook.
+
+### rhsm_repo
+
+Use the rhsm_repo resource to enable or disable Red Hat Subscription Manager repositories that are made available via attached subscriptions. This resource was ported from the `redhat_subscription_manager` community cookbook.
+
+### rhsm_subscription
+
+Use the rhsm_subscription resource to add or remove Red Hat Subscription Manager subscriptions for your host. This can be used when a host's activation_key does not attach all necessary subscriptions to your host. This resource was ported from the `redhat_subscription_manager` community cookbook.
+
+### sudo
+
+Use the sudo resource to add or remove individual sudo entries using `sudoers.d` files. Sudo version 1.7.2 or newer is required to use the sudo resource, as it relies on the `#includedir` directive introduced in version 1.7.2\. This resource does not enforce installation of the required sudo version. Supported releases of Ubuntu, Debian, SuSE, and RHEL (6+) all support this feature. This resource was ported from the `sudo` community cookbook.
+
+### swap_file
+
+Use the swap_file resource to create or delete swap files on Linux systems, and optionally to manage the swappiness configuration for a host. This resource was ported from the `swap` community cookbook.
+
+### sysctl
+
+Use the sysctl resource to set kernel parameters using the sysctl command line tool and configuration files in the system's `sysctl.d` directory. Configuration files managed by this resource are named 99-chef-KEYNAME.conf. If an existing value was already set for the value it will be backed up to the node and restored if the :remove action is used later. This resource was ported from the `sysctl` community cookbook.
+
+`Note`: This resource no longer backs up existing key values to the node when changing values as we have done in the sysctl cookbook previously. The resource has also been renamed from `sysctl_param` to `sysctl` with backwards compatibility for the previous name.
+
+### windows_ad_join
+
+Use the windows_ad_join resource to join a Windows Active Directory domain and reboot the node. This resource is based on the `win_ad_client` resource in the `win_ad` community cookbook, but is not backwards compatible with that resource.
+
+### windows_auto_run
+
+Use the windows_auto_run resource to set applications to run at logon. This resource was ported from the `windows` community cookbook.
+
+### windows_feature
+
+Use the windows_feature resource to add, remove or delete Windows features and roles. This resource calls the `windows_feature_dism` or `windows_feature_powershell` resources depending on the specified installation method and defaults to dism, which is available on both Workstation and Server editions of Windows. This resource was ported from the `windows` community cookbook.
+
+`Note`: These resources received significant refactoring in the 4.0 version of the windows cookbook (March 2018). windows_feature resources now fail if the installation of invalid features is requested and support for installation via server `servermanagercmd.exe` has been removed. If you are using a windows cookbook version less than 4.0 you may need to update cookbooks for Chef 14.
+
+### windows_font
+
+Use the windows_font resource to install or remove font files on Windows. By default, the font is sourced from the cookbook using the resource, but a URI source can be specified as well. This resource was ported from the `windows` community cookbook.
+
+### windows_printer
+
+Use the windows_printer resource to setup Windows printers. Note that this doesn't currently install a printer driver. You must already have the driver installed on the system. This resource was ported from the `windows` community cookbook.
+
+### windows_printer_port
+
+Use the windows_printer_port resource to create and delete TCP/IPv4 printer ports on Windows. This resource was ported from the `windows` community cookbook.
+
+### windows_shortcut
+
+Use the windows_shortcut resource to create shortcut files on Windows. This resource was ported from the `windows` community cookbook.
+
+## Custom Resource Improvements
+
+We've expanded the DSL for custom resources with new functionality to better document your resources and help users with errors and upgrades. Many resources in Chef itself are now using this new functionality, and you'll see more updated to take advantage of this it in the future.
+
+### Deprecations in Cookbook Resources
+
+Chef 14 provides new primitives that allow you to deprecate resources or properties with the same functionality used for deprecations in Chef Client resources. This allows you make breaking changes to enterprise or community cookbooks with friendly notifications to downstream cookbook consumers directly in the Chef run.
+
+Deprecate the foo_bar resource in a cookbook:
+
+```ruby
+deprecated "The foo_bar resource has been deprecated and will be removed in the next major release of this cookbook scheduled for 12/25/2018!"
+
+property :thing, String, name_property: true
+
+action :create do
+ # you'd probably have some actual chef code here
+end
+```
+
+Deprecate the thing2 property in a resource
+
+```ruby
+property :thing2, String, deprecated: 'The thing2 property has been deprecated and will be removed in the next major release of this cookbook scheduled for 12/25/2018!'
+```
+
+Rename a property with a deprecation warning for users of the old property name
+
+```ruby
+deprecated_property_alias 'thing2', 'the_second_thing', 'The thing2 property was renamed the_second_thing in the 2.0 release of this cookbook. Please update your cookbooks to use the new property name.'
+```
+
+### Platform Deprecations
+
+chef-client no longer is built or tested on OS X 10.10 in accordance with Chef's EOL policy.
+
+### validation_message
+
+Validation messages allow you give the user a friendly error message when any validation on a property fails.
+
+Provide a friendly message when a regex fails:
+
+```ruby
+property :repo_name, String, regex: [/^[^\/]+$/], validation_message: "The repo_name property cannot contain a forward slash '/'",
+```
+
+### Resource Documentation
+
+You can now include documentation that describes how a resource is to be used. Expect this data to be consumed by Chef and other tooling in future releases.
+
+A resource which includes description and introduced values in the resource, actions, and properties:
+
+```ruby
+description 'The apparmor_policy resource is used to add or remove policy files from a cookbook file'
+introduced '14.1'
+
+property :source_cookbook, String,
+ description: 'The cookbook to source the policy file from'
+property :source_filename, String,
+ description: 'The name of the source file if it differs from the apparmor.d file being created'
+
+action :add do
+ description 'Adds an apparmor policy'
+
+ # you'd probably have some actual chef code here
+end
+```
+
+## Improved Resources
+
+Many existing resources now include new actions and properties that expand their functionality.
+
+### apt_package
+
+`apt_package` includes a new `overwrite_config_files` property. Setting this new property to true is equivalent to passing `-o Dpkg::Options::="--force-confnew"` to apt, and allows you to install packages that prompt the user to overwrite config files. Thanks @ccope for this new property.
+
+### env
+
+The `env` resource has been renamed to `windows_env` as it only supports the Windows platform. Existing cookbooks using `env` will continue to function, but should be updated to use the new name.
+
+### ifconfig
+
+`ifconfig` includes a new `family` property for setting the network family on Debian systems. Thanks @martinisoft for this new property.
+
+### registry_key
+
+The `sensitive` property can now be used in `registry_key` to suppress the output of the key's data from logs and error messages. Thanks @shoekstra for implementing this.
+
+### powershell_package
+
+`powershell_package` includes a new `source` property to allow specifying the source of the package. Thanks @Happycoil for this new property.
+
+### systemd_unit
+
+`systemd_unit` includes the following new actions:
+
+- `preset` - Restore the preset enable/disable configuration for a unit
+- `revert` - Revert to a vendor's version of a unit file
+- `reenable` - Reenable a unit file
+
+Thanks @nathwill for these new actions.
+
+### windows_service
+
+`windows_service` now includes actions for fully managing services on Windows, in addition to the previous actions for starting/stopping/enabling services.
+
+- `create` - Create a new service
+- `delete` - Delete an existing service
+- `configure` - Reconfigure an existing service
+
+Thanks @jasonwbarnett for these new actions
+
+### route
+
+`route` includes a new `comment` property.
+
+Thanks Thomas Doherty for adding this new property.
+
+## Expanded Configuration Detection
+
+Ohai has been expanded to collect more information than ever. This should make writing cross-platform and cross cloud cookbooks simpler.
+
+### Windows Kernel information
+
+The kernel plugin now reports the following information on Windows:
+
+- `node['kernel']['product_type']` - Workstation vs. Server editions of Windows
+- `node['kernel']['system_type']` - What kind of hardware are we installed on (Desktop, Mobile, Workstation, Enterprise Server, etc.)
+- `node['kernel']['server_core']` - Are we on Windows Server Core edition?
+
+### Cloud Detection
+
+Ohai now detects the Scaleway cloud and provides additional configuration information for systems running on Azure.
+
+### Virtualization / Container Detection
+
+In addition to detecting if a system is a Docker host, we now provide a large amount of Docker configuration information available at `node['docker']`. This includes the release of Docker, installed plugins, network config, and the number of running containers.
+
+Ohai also now properly detects LXD containers and macOS guests running on VirtualBox / VMware. This data is available in `node['virtualization']['systems']`.
+
+### Optional Ohai Plugins
+
+Ohai now includes the ability to mark plugins as optional, which skips those plugins by default. This allows us to ship additional plugins, which some users may find useful, but not all users want that data collected in the node object on a Chef server. The change introduces two new configuration options; `run_all_plugins` which runs everything including optional plugins, and `optional_plugins` which allows you to run plugins marked as optional.
+
+By default we will now be marking the `lspci`, `sessions` `shard` and `passwd` plugins as optional. Passwd has been particularly problematic for nodes attached to LDAP or AD where it attempts to write the entire directory's contents to the node. If you previously disabled this plugin via Ohai config, you no longer need to. Hurray!
+
+## Other Changes
+
+### Ruby 2.5
+
+Ruby has been updated to version 2.5 bringing a 10% performance improvement and improved functionality.
+
+### InSpec 2.0
+
+InSpec has been updated to the 2.0 release. InSpec 2.0 brings compliance automation to the cloud, with new resource types specifically built for AWS and Azure clouds. Along with these changes are major speed improvements and quality of life updates. Please visit <https://www.inspec.io/> for more information.
+
+### Policyfile Hoisting
+
+Many users of Policyfiles rely on "hoisting" to provide group specific attributes. This approach was formalized in the poise-hoist extension, and is now included in Chef 14.
+
+To hoist an attribute, the user provides a default attribute structure in their Policyfile similar to:
+
+```ruby
+default['staging']['myapp']['title'] = "My Staging App" default['production']['myapp']['title'] = "My App"
+```
+
+and then accesses the node attribute in their cookbook as:
+
+```ruby
+node['myapp']['title']
+```
+
+The correct attribute is then provided based on the policy_group of the node, so with a policy_group of staging the attribute would contain "My Staging App".
+
+### yum_package rewrite
+
+yum_package received a ground up rewrite that greatly improves both the performance and functionality while also resolving a dozen existing issues. It introduces a new caching method that runs for the duration of the chef-client process. This caching method speeds up each package install and takes 1/2 the memory of the previous `yum-dump.py` process.
+
+yum_package should now take any argument that `yum install` does and operate the same way, including version constraints "foo < 1.2.3" and globs "foo-1.2*" along with arches "foo.i386" and in combinations
+
+Package with a version constraint:
+
+```ruby
+yum_package "foo < 1.2.3"
+```
+
+Installing a package via what it provides:
+
+```ruby
+yum_package "perl(Git)"
+```
+
+### powershell_exec Mixin
+
+Since our supported Windows platforms can all run .NET Framework 4.0 and PowerShell 4.0 we have taken time to add a new helper that will allow for faster and safer interactions with the system PowerShell. You will be able to use the powershell_exec mixin in most places where you would have previously used powershell_out. For comparison, a basic benchmark test to return the $PSVersionTable 100 times completed 7.3X faster compared to the powershell_out method. The majority of the time difference is because of less time spent in invocation. So we believe it has big future potential where multiple calls to PowerShell are required inside (for example) a custom resource. Many core Chef resources will be updated to use this new mixin in future releases.
+
+### Logging Improvements
+
+Chef now includes a new log level of `:trace` in addition to the existing `:info`, `:warn`, and `:debug` levels. With the introduction of `trace` level logging we've moved a large amount of logging that is more useful for Chef developers from `debug` to `trace`. This makes it easier for Chef Cookbook developers to use `debug` level to get useful information.
+
+## Security Updates
+
+### OpenSSL
+
+OpenSSL has been updated to 1.0.2o to resolve [CVE-2018-0739](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0739)
+
+### Ruby
+
+Ruby has been updated to 2.5.1 to resolve the following vulnerabilities:
+
+- [cve-2017-17742](https://www.ruby-lang.org/en/news/2018/03/28/http-response-splitting-in-webrick-cve-2017-17742/)
+- [cve-2018-6914](https://www.ruby-lang.org/en/news/2018/03/28/unintentional-file-and-directory-creation-with-directory-traversal-cve-2018-6914/)
+- [cve-2018-8777](https://www.ruby-lang.org/en/news/2018/03/28/large-request-dos-in-webrick-cve-2018-8777/)
+- [cve-2018-8778](https://www.ruby-lang.org/en/news/2018/03/28/buffer-under-read-unpack-cve-2018-8778/)
+- [cve-2018-8779](https://www.ruby-lang.org/en/news/2018/03/28/poisoned-nul-byte-unixsocket-cve-2018-8779/)
+- [cve-2018-8780](https://www.ruby-lang.org/en/news/2018/03/28/poisoned-nul-byte-dir-cve-2018-8780/)
+- [Multiple vulnerabilities in rubygems](https://www.ruby-lang.org/en/news/2018/02/17/multiple-vulnerabilities-in-rubygems/)
+
+## Breaking Changes
+
+This release completes the deprecation process for many of the deprecations that were warnings throughout the Chef 12 and Chef 13 releases.
+
+### erl_call Resource
+
+The erl_call resource was deprecated in Chef 13.7 and has been removed.
+
+### deploy Resource
+
+The deploy resource was deprecated in Chef 13.6 and been removed. If you still require this resource, it is available in the new `deploy_resource` cookbook at <https://supermarket.chef.io/cookbooks/deploy_resource>
+
+### Windows 2003 Support
+
+Support for Windows 2003 has been removed from both Chef and Ohai, improving the performance of Chef on Windows hosts.
+
+### knife deprecations
+
+- `knife bootstrap` options `--distro` and `--template_file` flags were deprecated in Chef 12 and have now been removed.
+- `knife help` functionality that read legacy Chef manpages has been removed as the manpages had not been updated and were often quite wrong. Running knife help will now simply show the help menu.
+- `knife index rebuild` has been removed as reindexing Chef Server was only necessary on releases prior to Chef Server 11.
+- The `knife ssh --identity-file` flag was deprecated and has been removed. Users should use the `--ssh_identity_file` flag instead.
+- `knife ssh csshx` was deprecated in Chef 10 and has been removed. Users should use `knife ssh cssh` instead.
+
+### Chef Solo `-r` flag
+
+The Chef Solor `-r` flag has been removed as it was deprecated and replaced with the `--recipe-url` flag in Chef 12.
+
+### node.set and node.set_unless attribute levels removal
+
+`node.set` and `node.set_unless` were deprecated in Chef 12 and have been removed in Chef 14\. To replicate this same functionality users should use `node.normal` and `node.normal_unless`, although we highly recommend reading our [attribute documentation](https://docs.chef.io/attributes.html) to make sure `normal` is in fact the your desired attribute level.
+
+### chocolatey_package :uninstall Action
+
+The chocolatey_package resource in the chocolatey cookbook supported an `:uninstall` action. When this resource was moved into the Chef Client we allowed this action with a deprecation warning. This action is now removed.
+
+### Property names not using new_resource.NAME
+
+Previously if a user wrote a custom resource with a property named `foo` they could reference it throughout the resource using the name `foo`. This caused multiple edge cases where the property name could conflict with resources or methods in Chef. Properties now must be referenced as `new_resource.foo`. This was already the case when writing LWRPs.
+
+### epic_fail
+
+The original name for the `ignore_failure` property in resource was `epic_fail`. The legacy name has been removed.
+
+### Legacy Mixins
+
+Several legacy mixins mostly used in older HWRPs have been removed. Usage of these mixins has resulted in deprecation warnings for several years and they are rarely used in cookbooks available on the Supermarket.
+
+- Chef::Mixin::LanguageIncludeAttribute
+- Chef::Mixin::RecipeDefinitionDSLCore
+- Chef::Mixin::LanguageIncludeRecipe
+- Chef::Mixin::Language
+- Chef::DSL::Recipe::FullDSL
+
+### cloud_v2 and filesystem2 Ohai Plugins
+
+In Chef 13 the `cloud_v2` plugin replaced data at `node['cloud']` and `filesystem2` replaced data at `node['filesystem']`. For compatibility with cookbooks that were previously using the "v2" data we continued to write data to both locations (ie: both node['filesystem'] and node['filesystem2']). We now no longer write data to the "v2" locations which greatly reduces the amount of data we need to store on the Chef server.
+
+### Ipscopes Ohai Plugin Removed
+
+The ipscopes plugin has been removed as it duplicated data already present in the network plugins and required the user to install an additional gem into the Chef installation.
+
+### Ohai libvirt attributes moved
+
+The libvirt Ohai plugin now writes data to `node['libvirt']` instead of writing to various locations in `node['virtualization']`. This plugin required installing an additional gem into the Chef installation and thus was infrequently used.
+
+### Ohai Plugin V6 Support Removed
+
+In 2014 we introduced Ohai v7 with a greatly improved plugin format. With Chef 14 we no longer support loading of the legacy "v6" plugin format.
+
+### Newly-disabled Ohai Plugins
+
+As mentioned above we now support an `optional` flag for Ohai plugins and have marked the `sessions`, `lspci`, and `passwd` plugins as optional, which disables them by default. If you need one of these plugins you can include them using `optional_plugins`.
+
+optional_plugins in the client.rb file:
+
+```ruby
+optional_plugins [ "lspci", "passwd" ]
+```
+
+# Chef Client Release Notes 13.8:
+
+## Revert attributes changes from 13.7
+
+Per <https://discourse.chef.io/t/regression-in-chef-client-13-7-16/12518/1> , there was a regression in how arrays and hashes were handled in 13.7\. In 13.8, we've reverted to the same code as 13.6.
+
+## Continuing work on `windows_task`
+
+13.8 has better validation for the `idle_time` property, when using the `on_idle` frequency.
+
+## Security Updates
+
+- Updated libxml2 to 2.9.7; fixes: CVE-2017-15412
+
+# Chef Client Release Notes 13.7:
+
+## The `windows_task` Resource should be better behaved
+
+We've spent a considerable amount of time testing and fixing the `windows_task` resource to ensure that it is properly idempotent and correct in more situations.
+
+## Credentials handling
+
+Previously, chef on the workstation used `knife.rb` or `config.rb` to handle credentials. This didn't do a great job when interacting with multiple Chef servers, leading to the need for tools like `knife_block`. We've added support for a credentials file that can contain configuration for many Chef servers (or organizations), and we've made it easy to indicate which account you mean to use.
+
+## New deprecations
+
+### `erl_call` Resource
+
+We introduced `erl_call` to help us to manage CouchDB servers back in the olden times of Chef. Since then, we've noticed that no-one uses it, and so `erl_call` will be removed in Chef 14\. Foodcritic rule FC105 has been introduced to detect usage of erl_call.
+
+### epic_fail
+
+The original name for the ignore_failure property in resources was epic_fail. Our documentation hasn't referred to epic_fail for years and out of the 3500 cookbooks on the Supermarket only one uses epic_fail. In Chef 14 we will remove the epic_fail property entirely. Foodcritic rule FC107 has been introduced to detect usage of epic_fail.
+
+### Legacy Mixins
+
+In Chef 14 several legacy legacy mixins will be removed. Usage of these mixins has resulted in deprecation warnings for several years. They were traditionally used in some HWRPs, but are rarely found in code available on the Supermarket. Foodcritic rules FC097, FC098, FC099, FC100, and FC102 have been introduced to detect these mixins.
+
+- Chef::Mixin::LanguageIncludeAttribute
+- Chef::Mixin::RecipeDefinitionDSLCore
+- Chef::Mixin::LanguageIncludeRecipe
+- Chef::Mixin::Language
+- Chef::DSL::Recipe::FullDSL
+
+### :uninstall action in chocolatey_package
+
+The chocolatey cookbook's chocolatey_package resource originally contained an :uninstall action. When chocolatey_package was moved into core Chef we made :uninstall an alias for :remove. In Chef 14 :uninstall will no longer be a valid action. Foodcritic rule FC103 has been introduced to detect the usage of the :uninstall action.
+
+## Bugfixes
+
+- Resolved a bug where knife commands that prompted on Windows would never display the prompt
+- Fixed hiding of sensitive resources when converge_if_changed was used
+- Fixed scenarios where services would fail to start on Solaris
+
+## Security Updates
+
+- OpenSSL has been upgraded to 1.0.2n to resolve CVE-2017-3738, CVE-2017-3737, CVE-2017-3736, and CVE-2017-3735.
+- Ruby has been upgraded to 2.4.3 to resolve CVE-2017-17405
+
+## Ohai 13.7 Release Notes:
+
+### Network Tunnel Information
+
+The Network plugin on Linux hosts now gathers additional information on tunnels
+
+### LsPci Plugin
+
+The new LsPci plugin provides a `node[:pci]` hash with information about the PCI bus based on `lspci`. Only runs on Linux.
+
+### EC2 C5 Detection
+
+The EC2 plugin has been updated to properly detect the new AWS hypervisor used in the C5 instance types
+
+### mdadm
+
+The mdadm plugin has been updated to properly handle arrays with more than 10 disks and to properly handle journal and spare drives in the disk counts
+
+# Chef Client Release Notes 13.6.4:
+
+## Bugfixes
+
+- Resolved a regression in 13.6.0 that prevented upgrading packages on Debian/Ubuntu when the package name contained a tilde.
+
+## Security Updates
+
+- OpenSSL has been upgraded to 1.0.2m to resolve CVE-2017-3735 and CVE-2017-3736
+- RubyGems has been upgraded to 2.6.14 to resolve CVE-2017-0903
+
+# Chef Client Release Notes 13.6:
+
+## `deploy` Resource Is Deprecated
+
+The `deploy` resource (and its alter ego `deploy_revision`) have been deprecated, to be removed in Chef 14\. This is being done because this resource is considered overcomplicated and error-prone in the modern Chef ecosystem. A compatibility cookbook will be available to help users migrate during the Chef 14 release cycle. See [the deprecation documentation](https://docs.chef.io/deprecations_deploy_resource.html) for more information.
+
+## zypper_package supports package downgrades
+
+`zypper_package` now supports downgrading installed packages with the `allow_downgrade` property.
+
+## InSpec updated to 1.42.3
+
+## Reserve certain Data Bag names
+
+It's no longer possible to create data bags named `node`, `role`, `client`, or `environment`. Existing data bags will continue to work as before.
+
+## Properly use yum on RHEL 7
+
+If both dnf and yum were installed, in some circumstances the yum provider might choose to run dnf, which is not what we intended it to do. It now properly runs yum, all the time.
+
+## Ohai 13.6 Release Notes:
+
+### Critical Plugins
+
+Users can now specify a list of plugins which are `critical`. Critical plugins will cause Ohai to fail if they do not run successfully (and thus cause a Chef run using Ohai to fail). The syntax for this is:
+
+```
+ohai.critical_plugins << :Filesystem
+```
+
+### Filesystem now has a `allow_partial_data` configuration option
+
+The Filesystem plugin now has a `allow_partial_data` configuration option. If set, the filesystem will return whatever data it can even if some commands it ran failed.
+
+### Rackspace detection on Windows
+
+Windows nodes running on Rackspace will now properly detect themselves as running on Rackspace without a hint file.
+
+### Package data on Amazon Linux
+
+The Packages plugin now supports gathering packages data on Amazon Linux
+
+### Deprecation updates
+
+In Ohai 13 we replaced the filesystem and cloud plugins with the filesystem2 and cloud_v2 plugins. To maintain compatibility with users of the previous V2 plugins we write data to both locations. We had originally planned to continue writing data to both locations until Chef 15\. Instead due to the large amount of duplicate node data this introduces we are updating OHAI-11 and OHAI-12 deprecations to remove node['cloud_v2'] and node['filesystem2'] with the release of Chef 14 in April 2018.
+
+# Chef Client Release Notes 13.5:
+
+## Mount's password property is now marked as sensitive
+
+This means that passwords passed to mount won't show up in logs.
+
+## The `windows_task` resource now correctly handles `start_day`
+
+Previously, the resource would accept any date that was formatted correctly in the local locale, unlike the Windows cookbook and Windows itself. We now only support the `MM/DD/YYYY` format, in common with the Windows cookbook.
+
+## InSpec updated to 1.39.1
+
+## Ohai 13.5 Release Notes:
+
+### Correctly detect IPv6 routes ending in ::
+
+Previously we would ignore routes that ended `::`, and now we properly detect them.
+
+### Plugin run time is now measured
+
+Debug logs will show the length of time each plugin takes to run, making debugging of long ohai runs easier.
+
+# Chef Client Release Notes 13.4:
+
+## Security release of Ruby
+
+Chef Client 13.4 includes Ruby 2.4.2 to fix the following CVEs:
+
+- CVE-2017-0898
+- CVE-2017-10784
+- CVE-2017-14033
+- CVE-2017-14064
+
+## Security release of RubyGems
+
+Chef Client 13.4 includes RubyGems 2.6.13 to fix the following CVEs:
+
+- CVE-2017-0899
+- CVE-2017-0900
+- CVE-2017-0901
+- CVE-2017-0902
+
+## Ifconfig provider on Red Hat now supports additional properties
+
+It is now possible to set `ETHTOOL_OPTS`, `BONDING_OPTS`, `MASTER` and `SLAVE` properties on interfaces on Red Hat compatible systems. See <https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Deployment_Guide/s1-networkscripts-interfaces.html> for further information
+
+### Properties
+
+- `ethtool_opts`<br>
+ **Ruby types:** String<br>
+ **Platforms:** Fedora, RHEL, Amazon Linux A string containing arguments to ethtool. The string will be wrapped in double quotes, so ensure that any needed quotes in the property are surrounded by single quotes
+
+- `bonding_opts`<br>
+ **Ruby types:** String<br>
+ **Platforms:** Fedora, RHEL, Amazon Linux A string containing configuration parameters for the bonding device.
+
+- `master`<br>
+ **Ruby types:** String<br>
+ **Platforms:** Fedora, RHEL, Amazon Linux The channel bonding interface that this interface is linked to.
+
+- `slave`<br>
+ **Ruby types:** String<br>
+ **Platforms:** Fedora, RHEL, Amazon Linux Whether the interface is controlled by the channel bonding interface defined by `master`, above.
+
+## Chef Vault is now included
+
+Chef Client 13.4 now includes the `chef-vault` gem, making it easier for users of chef-vault to use their encrypted items.
+
+## Windows `remote_file` resource with alternate credentials
+
+The `remote_file` resource now supports the use of credentials on Windows when accessing a remote UNC path on Windows such as `\\myserver\myshare\mydirectory\myfile.txt`. This allows access to the file at that path location even if the Chef client process identity does not have permission to access the file. The new properties `remote_user`, `remote_domain`, and `remote_password` may be used to specify credentials with access to the remote file so that it may be read.
+
+**Note**: This feature is mainly used for accessing files between two nodes in different domains and having different user accounts. In case the two nodes are in same domain, `remote_file` resource does not need `remote_user` and `remote_password` specified because the user has the same access on both systems through the domain.
+
+### Properties
+
+The following properties are new for the `remote_file` resource:
+
+- `remote_user`<br>
+ **Ruby types:** String<br>
+ _Windows only:_ The user name of a user with access to the remote file specified by the `source` property. Default value: `nil`. The user name may optionally be specifed with a domain, i.e. `domain\user` or `user@my.dns.domain.com` via Universal Principal Name (UPN) format. It can also be specified without a domain simply as `user` if the domain is instead specified using the `remote_domain` attribute. Note that this property is ignored if `source` is not a UNC path. If this property is specified, the `remote_password` property **must** be specified.
+
+- `remote_password`<br>
+ **Ruby types** String<br>
+ _Windows only:_ The password of the user specified by the `remote_user` property. Default value: `nil`. This property is mandatory if `remote_user` is specified and may only be specified if `remote_user` is specified. The `sensitive` property for this resource will automatically be set to `true` if `remote_password` is specified.
+
+- `remote_domain`<br>
+ **Ruby types** String<br>
+ _Windows only:_ The domain of the user user specified by the `remote_user` property. Default value: `nil`. If not specified, the user and password properties specified by the `remote_user` and `remote_password` properties will be used to authenticate that user against the domain in which the system hosting the UNC path specified via `source` is joined, or if that system is not joined to a domain it will authenticate the user as a local account on that system. An alternative way to specify the domain is to leave this property unspecified and specify the domain as part of the `remote_user` property.
+
+### Examples
+
+Accessing file from a (different) domain account
+
+```ruby
+remote_file "E://domain_test.txt" do
+ source "\\\\myserver\\myshare\\mydirectory\\myfile.txt"
+ remote_domain "domain"
+ remote_user "username"
+ remote_password "password"
+end
+```
+
+OR
+
+```ruby
+remote_file "E://domain_test.txt" do
+ source "\\\\myserver\\myshare\\mydirectory\\myfile.txt"
+ remote_user "domain\\username"
+ remote_password "password"
+end
+```
+
+Accessing file using a local account on the remote machine
+
+```ruby
+remote_file "E://domain_test.txt" do
+ source "\\\\myserver\\myshare\\mydirectory\\myfile.txt"
+ remote_domain "."
+ remote_user "username"
+ remote_password "password"
+end
+```
+
+OR
+
+```ruby
+remote_file "E://domain_test.txt" do
+ source "\\\\myserver\\myshare\\mydirectory\\myfile.txt"
+ remote_user ".\\username"
+ remote_password "password"
+end
+```
+
+## windows_path resource
+
+`windows_path` resource has been moved to core chef from windows cookbook. Use the `windows_path` resource to manage the path environment variable on Microsoft Windows.
+
+### Actions
+
+- `:add` - Add an item to the system path
+- `:remove` - Remove an item from the system path
+
+### Properties
+
+- `path` - Name attribute. The name of the value to add to the system path
+
+### Examples
+
+Add Sysinternals to the system path
+
+```ruby
+windows_path 'C:\Sysinternals' do
+ action :add
+end
+```
+
+Remove 7-Zip from the system path
+
+```ruby
+windows_path 'C:\7-Zip' do
+ action :remove
+end
+```
+
+## Ohai Release Notes 13.4
+
+### Windows EC2 Detection
+
+Detection of nodes running in EC2 has been greatly improved and should now detect nodes 100% of the time including nodes that have been migrated to EC2 or were built with custom AMIs.
+
+### Azure Metadata Endpoint Detection
+
+Ohai now polls the new Azure metadata endpoint, giving us additional configuration details on nodes running in Azure
+
+Sample data now available under azure:
+
+```javascript
+{
+ "metadata": {
+ "compute": {
+ "location": "westus",
+ "name": "timtest",
+ "offer": "UbuntuServer",
+ "osType": "Linux",
+ "platformFaultDomain": "0",
+ "platformUpdateDomain": "0",
+ "publisher": "Canonical",
+ "sku": "17.04",
+ "version": "17.04.201706191",
+ "vmId": "8d523242-71cf-4dff-94c3-1bf660878743",
+ "vmSize": "Standard_DS1_v2"
+ },
+ "network": {
+ "interfaces": {
+ "000D3A33AF03": {
+ "mac": "000D3A33AF03",
+ "public_ipv6": [
+
+ ],
+ "public_ipv4": [
+ "52.160.95.99",
+ "23.99.10.211"
+ ],
+ "local_ipv6": [
+
+ ],
+ "local_ipv4": [
+ "10.0.1.5",
+ "10.0.1.4",
+ "10.0.1.7"
+ ]
+ }
+ },
+ "public_ipv4": [
+ "52.160.95.99",
+ "23.99.10.211"
+ ],
+ "local_ipv4": [
+ "10.0.1.5",
+ "10.0.1.4",
+ "10.0.1.7"
+ ],
+ "public_ipv6": [
+
+ ],
+ "local_ipv6": [
+
+ ]
+ }
+ }
+}
+```
+
+### Package Plugin Supports Arch Linux
+
+The Package plugin has been updated to include package information on Arch Linux systems.
+
+# Chef Client Release Notes 13.3:
+
+## Unprivileged Symlink Creation on Windows
+
+Chef can now create symlinks without privilege escalation, which allows for the creation of symlinks on Windows 10 Creator Update.
+
+## nokogiri Gem
+
+The nokogiri gem is once again bundled with the omnibus install of Chef
+
+## zypper_package Options
+
+It is now possible to pass additional options to the zypper in the zypper_package resource. This can be used to pass any zypper CLI option
+
+### Example:
+
+```ruby
+zypper_package 'foo' do
+ options '--user-provided'
+end
+```
+
+## windows_task Improvements
+
+The `windows_task` resource now properly allows updating the configuration of a scheduled task when using the `:create` action. Additionally the previous `:change` action from the windows cookbook has been aliased to `:create` to provide backwards compatibility.
+
+## apt_preference Resource
+
+The apt_preference resource has been ported from the apt cookbook. This resource allows for the creation of APT preference files controlling which packages take priority during installation.
+
+Further information regarding apt-pinning is available via <https://wiki.debian.org/AptPreferences> and <https://manpages.debian.org/stretch/apt/apt_preferences.5.en.html>
+
+### Actions
+
+- `:add`: creates a preferences file under /etc/apt/preferences.d
+- `:remove`: Removes the file, therefore unpin the package
+
+### Properties
+
+- `package_name`: name attribute. The name of the package
+- `glob`: Pin by glob() expression or regexp surrounded by /.
+- `pin`: The package version/repository to pin
+- `pin_priority`: The pinning priority aka "the highest package version wins"
+
+### Examples
+
+Pin libmysqlclient16 to version 5.1.49-3:
+
+```ruby
+apt_preference 'libmysqlclient16' do
+ pin 'version 5.1.49-3'
+ pin_priority '700'
+end
+```
+
+Unpin libmysqlclient16:
+
+```ruby
+apt_preference 'libmysqlclient16' do
+ action :remove
+end
+```
+
+Pin all packages from dotdeb.org:
+
+```ruby
+apt_preference 'dotdeb' do
+ glob '*'
+ pin 'origin packages.dotdeb.org'
+ pin_priority '700'
+end
+```
+
+## zypper_repository Resource
+
+The zypper_repository resource allows for the creation of Zypper package repositories on SUSE Enterprise Linux and openSUSE systems. This resource maintains full compatibility with the resource in the existing [zypper](https://supermarket.chef.io/cookbooks/zypper) cookbooks
+
+### Actions
+
+- `:add` - adds a repo
+- `:delete` - removes a repo
+
+### Properties
+
+- `repo_name` - repository name if different from the resource name (name property)
+- `type` - the repository type. default: 'NONE'
+- `description` - the description of the repo that will be shown in `zypper repos`
+- `baseurl` - the base url of the repo
+- `path` - the relative path from the `baseurl`
+- `mirrorlist` - the url to the mirrorlist to use
+- `gpgcheck` - should we gpg check the repo (true/false). default: true
+- `gpgkey` - location of repo key to import
+- `priority` - priority of the repo. default: 99
+- `autorefresh` - should the repository be automatically refreshed (true/false). default: true
+- `keeppackages` - should packages be saved (true/false). default: false
+- `refresh_cache` - should package cache be refreshed (true/false). default: true
+- `enabled` - should this repository be enabled (true/false). default: true
+- `mode` - the file mode of the repository file. default: "0644"
+
+### Examples
+
+Add the Apache repository for openSUSE Leap 42.2
+
+```ruby
+zypper_repository 'apache' do
+ baseurl 'http://download.opensuse.org/repositories/Apache'
+ path '/openSUSE_Leap_42.2'
+ type 'rpm-md'
+ priority '100'
+end
+```
+
+## Ohai Release Notes 13.3:
+
+### Additional Platform Support
+
+Ohai now properly detects the [F5 Big-IP](https://www.f5.com/) platform and platform_version.
+
+- platform: bigip
+- platform_family: rhel
+
+# Chef Client Release Notes 13.2:
+
+## Properly send policyfile data
+
+When sending events back to the Chef Server, we now correctly expand the run_list for nodes that use Policyfiles. This allows Automate to correctly report the node.
+
+## Reconfigure between runs when daemonized
+
+When Chef performs a reconfigure, it re-reads the configuration files. It also re-opens its log files, which facilitates log file rotation.
+
+Chef normally will reconfigure when sent a HUP signal. As of this release if you send a HUP signal while it is converging, the reconfigure happens at the end of the run. This is avoids potential Ruby issues when the configuration file contains additional Ruby code that is executed. While the daemon is sleeping between runs, sending a SIGHUP will still cause an immediate reconfigure.
+
+Additionally, Chef now always performs a reconfigure after every run when daemonized.
+
+## New Deprecations
+
+### Explicit property methods
+
+<https://docs.chef.io/deprecations_namespace_collisions.html>
+
+In Chef 14, custom resources will no longer assume property methods are being called on `new_resource`, and instead require the resource author to be explicit.
+
+# Ohai Release Notes 13.2:
+
+Ohai 13.2 has been a fantastic release in terms of community involvement with new plugins, platform support, and critical bug fixes coming from community members. A huge thank you to msgarbossa, albertomurillo, jaymzh, and davide125 for their work.
+
+## New Features
+
+### Systemd Paths Plugin
+
+A new plugin has been added to expose system and user paths from systemd-path (see <https://www.freedesktop.org/software/systemd/man/systemd-path.html> for details).
+
+### Linux Network, Filesystem, and Mdadm Plugin Resilience
+
+The Network, Filesystem, and Mdadm plugins have been improved to greatly reduce failures to collect data. The Network plugin now better finds the binaries it requires for shelling out, filesystem plugin utilizes data from multiple sources, and mdadm handles arrays in bad states.
+
+### Zpool Plugin Platform Expansion
+
+The Zpool plugin has been updated to support BSD and Linux in addition to Solaris.
+
+### RPM version parsing on AIX
+
+The packages plugin now correctly parses RPM package name / version information on AIX systems.
+
+### Additional Platform Support
+
+Ohai now properly detects the [Clear](https://clearlinux.org/) and [ClearOS](https://www.clearos.com/) Linux distributions.
+
+#### Clear Linux
+
+- platform: clearlinux
+- platform_family: clearlinux
+
+#### ClearOS
+
+- platform: clearos
+- platform_family: rhel
+
+## New Deprecations
+
+### Removal of IpScopes plugin. (OHAI-13)
+
+<https://docs.chef.io/deprecations_ohai_ipscopes.html>
+
+In Chef/Ohai 14 (April 2018) we will remove the IpScopes plugin. The data returned by this plugin is nearly identical to information already returned by individual network plugins and this plugin required the installation of an additional gem into the Chef installation. We believe that few users were installing the gem and users would be better served by the data returned from the network plugins.
+
+# 13.1
+
+## Socketless local mode by default
+
+For security reasons we are switching Local Mode to use socketless connections by default. This prevents potential attacks where an unprivileged user or process connects to the internal Zero server for the converge and changes data.
+
+If you use Chef Provisioning with Local Mode, you may need to pass `--listen` to `chef-client`.
+
+## New Deprecations
+
+### Removal of support for Ohai version 6 plugins (OHAI-10)
+
+<https://docs.chef.io/deprecations_ohai_v6_plugins.html>
+
+In Chef/Ohai 14 (April 2018) we will remove support for loading Ohai v6 plugins, which we deprecated in Ohai 7/Chef 11.12.
+
+# 13.0
+
+## Rubygems provider sources behavior changed.
+
+The behavior of `gem_package` and `chef_gem` is now to always apply the `Chef::Config[:rubygems_url]` sources, which may be a String uri or an Array of Strings. If additional sources are put on the resource with the `source` property those are added to the configured `:rubygems_url` sources.
+
+This should enable easier setup of rubygems mirrors particularly in "airgapped" environments through the use of the global config variable. It also means that an admin may force all rubygems.org traffic to an internal mirror, while still being able to consume external cookbooks which have resources which add other mirrors unchanged (in a non-airgapped environment).
+
+In the case where a resource must force the use of only the specified source(s), then the `include_default_source` property has been added -- setting it to false will remove the `Chef::Config[:rubygems_url]` setting from the list of sources for that resource.
+
+The behavior of the `clear_sources` property is now to only add `--clear-sources` and has no magic side effects on the source options.
+
+## Ruby version upgraded to 2.4.1
+
+We've upgraded to the latest stable release of the Ruby programming language. See the Ruby [2.4.0 Release Notes](https://www.ruby-lang.org/en/news/2016/12/25/ruby-2-4-0-released/) for an overview of what's new in the language.
+
+## Resource can now declare a default name
+
+The core `apt_update` resource can now be declared without any name argument, no need for `apt_update "this string doesn't matter but why do i have to type it?"`.
+
+This can be used by any other resource by just overriding the name property and supplying a default:
+
+```ruby
+ property :name, String, default: ""
+```
+
+Notifications to resources with empty strings as their name is also supported via either the bare resource name (`apt_update` -- matches what the user types in the DSL) or with empty brackets (`apt_update[]` -- matches the resource notification pattern).
+
+## The knife ssh command applies the same fuzzifier as knife search node
+
+A bare name to knife search node will search for the name in `tags`, `roles`, `fqdn`, `addresses`, `policy_name` or `policy_group` fields and will match when given partial strings (available since Chef 11). The `knife ssh` search term has been similarly extended so that the search API matches in both cases. The node search fuzzifier has also been extracted out to a `fuzz` option to Chef::Search::Query for re-use elsewhere.
+
+## Cookbook root aliases
+
+Rather than `attributes/default.rb`, cookbooks can now use `attributes.rb` in the root of the cookbook. Similarly for a single default recipe, cookbooks can use `recipe.rb` in the root of the cookbook.
+
+## knife ssh can now connect to gateways with ssh key authentication
+
+The new `gateway_identity_file` option allows the operator to specify the key to access ssh gateways with.
+
+## Windows Task resource added
+
+The `windows_task` resource has been ported from the windows cookbook, and many bugs have been fixed.
+
+## Solaris SMF services can now been started recursively
+
+It is now possible to load Solaris services recursively, by ensuring the new `options` property of the `service` resource contains `-r`.
+
+## It's now possible to blacklist node attributes
+
+This is the inverse of the pre-existing whitelisting functionality.
+
+## The guard interpreter for `powershell_script` is Powershell, again
+
+When writing `not_if` or `only_if` statements, by default we now run those statements using powershell, rather than forcing the user to set `guard_interpreter` each time.
+
+## Zypper GPG checks by default
+
+Zypper now defaults to performing gpg checks of packages.
+
+## The InSpec gem is now shipped by default
+
+The `inspec` and `train` gems are shipped by default in the chef omnibus package, making it easier for users in airgapped environments to use InSpec.
+
+## Properly support managing Sys-V services on Debian systemd hosts
+
+Chef now properly supports managing sys-v services on hosts running systemd. Previously Chef would incorrectly attempt to fallback to Upstart even if upstart was not installed.
+
+## Backwards Compatibility Breaks
+
+### Resource Cloning has been removed
+
+When Chef compiles resources, it will no longer attempt to merge the properties of previously compiled resources with the same name and type in to the new resource. See [the deprecation page](https://docs.chef.io/deprecations_resource_cloning.html) for further information.
+
+### It is an error to specify both `default` and `name_property` on a property
+
+Chef 12 made this work by picking the first option it found, but it was always an error and has now been disallowed.
+
+### The path property of the execute resource has been removed
+
+It was never implemented in the provider, so it was always a no-op to use it, the remediation is to simply delete it.
+
+### Using the command property on any script resource (including bash, etc) is now a hard error
+
+This was always a usage mistake. The command property was used internally by the script resource and was not intended to be exposed to users. Users should use the code property instead (or use the command property on an execute resource to execute a single command).
+
+### Omitting the code property on any script resource (including bash, etc) is now a hard error
+
+It is possible that this was being used as a no-op resource, but the log resource is a better choice for that until we get a null resource added. Omitting the code property or mixing up the code property with the command property are also common usage mistakes that we need to catch and error on.
+
+### The chef_gem resource defaults to not run at compile time
+
+The `compile_time true` flag may still be used to force compile time.
+
+### The Chef::Config[:chef_gem_compile_time] config option has been removed
+
+In order to for community cookbooks to behave consistently across all users this optional flag has been removed.
+
+### The `supports[:manage_home]` and `supports[:non_unique]` API has been removed from all user providers
+
+The remediation is to set the manage_home and non_unique properties directly.
+
+### Using relative paths in the `creates` property of an execute resource with specifying a `cwd` is now a hard error
+
+Without a declared cwd the relative path was (most likely?) relative to wherever chef-client happened to be invoked which is not deterministic or easy to intuit behavior.
+
+### Chef::PolicyBuilder::ExpandNodeObject#load_node has been removed
+
+This change is most likely to only affect internals of tooling like chefspec if it affects anything at all.
+
+### PolicyFile failback to create non-policyfile nodes on Chef Server < 12.3 has been removed
+
+PolicyFile users on Chef-13 should be using Chef Server 12.3 or higher.
+
+### Cookbooks with self dependencies are no longer allowed
+
+The remediation is removing the self-dependency `depends` line in the metadata.
+
+### Removed `supports` API from Chef::Resource
+
+Retained only for the service resource (where it makes some sense) and for the mount resource.
+
+### Removed retrying of non-StandardError exceptions for Chef::Resource
+
+Exceptions not decending from StandardError (e.g. LoadError, SecurityError, SystemExit) will no longer trigger a retry if they are raised during the executiong of a resources with a non-zero retries setting.
+
+### Removed deprecated `method_missing` access from the Chef::Node object
+
+Previously, the syntax `node.foo.bar` could be used to mean `node["foo"]["bar"]`, but this API had sharp edges where methods collided with the core ruby Object class (e.g. `node.class`) and where it collided with our own ability to extend the `Chef::Node` API. This method access has been deprecated for some time, and has been removed in Chef-13.
+
+### Changed `declare_resource` API
+
+Dropped the `create_if_missing` parameter that was immediately supplanted by the `edit_resource` API (most likely nobody ever used this) and converted the `created_at` parameter from an optional positional parameter to a named parameter. These changes are unlikely to affect any cookbook code.
+
+### Node deep-duping fixes
+
+The `node.to_hash`/`node.to_h` and `node.dup` APIs have been fixed so that they correctly deep-dup the node data structure including every string value. This results in a mutable copy of the immutable merged node structure. This is correct behavior, but is now more expensive and may break some poor code (which would have been buggy and difficult to follow code with odd side effects before).
+
+For example:
+
+```
+node.default["foo"] = "fizz"
+n = node.to_hash # or node.dup
+n["foo"] << "buzz"
+```
+
+before this would have mutated the original string in-place so that `node["foo"]` and `node.default["foo"]` would have changed to "fizzbuzz" while now they remain "fizz" and only the mutable `n["foo"]` copy is changed to "fizzbuzz".
+
+### Freezing immutable merged attributes
+
+Since Chef 11 merged node attributes have been intended to be immutable but the merged strings have not been frozen. In Chef 13, in the process of merging the node attributes strings and other simple objects are dup'd and frozen. In order to get a mutable copy, you can now correctly use the `node.dup` or `node.to_hash` methods, or you should mutate the object correctly through its precedence level like `node.default["some_string"] << "appending_this"`.
+
+### The Chef::REST API has been removed
+
+It has been fully replaced with `Chef::ServerAPI` in chef-client code.
+
+### Properties overriding methods now raise an error
+
+Defining a property that overrides methods defined on the base ruby `Object` or on `Chef::Resource` itself can cause large amounts of confusion. A simple example is `property :hash` which overrides the Object#hash method which will confuse ruby when the Custom Resource is placed into the Chef::ResourceCollection which uses a Hash internally which expects to call Object#hash to get a unique id for the object. Attempting to create `property :action` would also override the Chef::Resource#action method which is unlikely to end well for the user. Overriding inherited properties is still supported.
+
+### `chef-shell` now supports solo and legacy solo modes
+
+Running `chef-shell -s` or `chef-shell --solo` will give you an experience consistent with `chef-solo`. `chef-shell --solo-legacy-mode` will give you an experience consistent with `chef-solo --legacy-mode`.
+
+### Chef::Platform.set and related methods have been removed
+
+The deprecated code has been removed. All providers and resources should now be using Chef >= 12.0 `provides` syntax.
+
+### Remove `sort` option for the Search API
+
+This option has been unimplemented on the server side for years, so any use of it has been pointless.
+
+### Remove Chef::ShellOut
+
+This was deprecated and replaced a long time ago with mixlib-shellout and the shell_out mixin.
+
+### Remove `method_missing` from the Recipe DSL
+
+The core of chef hasn't used this to implement the Recipe DSL since 12.5.1 and its unlikely that any external code depended upon it.
+
+### Simplify Recipe DSL wiring
+
+Support for actions with spaces and hyphens in the action name has been dropped. Resources and property names with spaces and hyphens most likely never worked in Chef-12\. UTF-8 characters have always been supported and still are.
+
+### `easy_install` resource has been removed
+
+The Python `easy_install` package installer has been deprecated for many years, so we have removed support for it. No specific replacement for `pip` is being included with Chef at this time, but a `pip`-based `python_package` resource is available in the [`poise-python`](https://github.com/poise/poise-python) cookbooks.
+
+### Removal of run_command and popen4 APIs
+
+All the APIs in chef/mixlib/command have been removed. They were deprecated by mixlib-shellout and the shell_out mixin API.
+
+### Iconv has been removed from the ruby libraries and chef omnibus build
+
+The ruby Iconv library was replaced by the Encoding library in ruby 1.9.x and since the deprecation of ruby 1.8.7 there has been no need for the Iconv library but we have carried it forwards as a dependency since removing it might break some chef code out there which used this library. It has now been removed from the ruby build. This also removes LGPLv3 code from the omnibus build and reduces build headaches from porting iconv to every platform we ship chef-client on.
+
+This will also affect nokogiri, but that gem natively supports UTF-8, UTF-16LE/BE, ISO-8851-1(Latin-1), ASCII and "HTML" encodings. Users who really need to write something like Shift-JIS inside of XML will need to either maintain their own nokogiri installs or will need to convert to using UTF-8.
+
+### Deprecated cookbook metadata has been removed
+
+The `recommends`, `suggests`, `conflicts`, `replaces` and `grouping` metadata fields are no longer supported, and have been removed, since they were never used. Chef will ignore them in existing `metadata.rb` files, but we recommend that you remove them. This was proposed in RFC 85.
+
+### All unignored cookbook files will now be uploaded.
+
+We now treat every file under a cookbook directory as belonging to a cookbook, unless that file is ignored with a `chefignore` file. This is a change from the previous behaviour where only files in certain directories, such as `recipes` or `templates`, were treated as special. This change allows chef to support new classes of files, such as Ohai plugins or Inspec tests, without having to make changes to the cookbook format to support them.
+
+### DSL-based custom resources and providers no longer get module constants
+
+Up until now, creating a `mycook/resources/thing.rb` would create a `Chef::Resources::MycookThing` name to access the resource class object. This const is no longer created for resources and providers. You can access resource classes through the resolver API like:
+
+```ruby
+Chef::Resource.resource_for_node(:mycook_thing, node)
+```
+
+Accessing a provider class is a bit more complex, as you need a resource against which to run a resolution like so:
+
+```ruby
+Chef::ProviderResolver.new(node, find_resource!("mycook_thing[name]"), :nothing).resolve
+```
+
+### Default values for resource properties are frozen
+
+A resource declaring something like:
+
+```ruby
+property :x, default: {}
+```
+
+will now see the default value set to be immutable. This prevents cases of modifying the default in one resource affecting others. If you want a per-resource mutable default value, define it inside a `lazy{}` helper like:
+
+```ruby
+property :x, default: lazy { {} }
+```
+
+### Resources which later modify their name during creation will have their name changed on the ResourceCollection and notifications
+
+```ruby
+some_resource "name_one" do
+ name "name_two"
+end
+```
+
+The fix for sending notifications to multipackage resources involved changing the API which inserts resources into the resource collection slightly so that it no longer directly takes the string which is typed into the DSL but reads the (possibly coerced) name off of the resource after it is built. The end result is that the above resource will be named `some_resource[name_two]` instead of `some_resource[name_one]`. Note that setting the name (_not_ the `name_property`, but actually renaming the resource) is very uncommon. The fix is to simply name the resource correctly in the first place (`some_resource "name_two" do ...`)
+
+### `use_inline_resources` is always enabled
+
+The `use_inline_resources` provider mode is always enabled when using the `action :name do ... end` syntax. You can remove the `use_inline_resources` line.
+
+### `knife cookbook site vendor` has been removed
+
+Please use `knife cookbook site install` instead.
+
+### `knife cookbook create` has been removed
+
+Please use `chef generate cookbook` from the ChefDK instead.
+
+### Verify commands no longer support "%{file}"
+
+Chef has always recommended `%{path}`, and `%{file}` has now been removed.
+
+### The `partial_search` recipe method has been removed
+
+The `partial_search` method has been fully replaced by the `filter_result` argument to `search`, and has now been removed.
+
+### The logger and formatter settings are more predictable
+
+The default now is the formatter. There is no more automatic switching to the logger when logging or when output is sent to a pipe. The logger needs to be specifically requested with `--force-logger` or it will not show up.
+
+The `--force-formatter` option does still exist, although it will probably be deprecated in the future.
+
+If your logfiles switch to the formatter, you need to include `--force-logger` for your daemonized runs.
+
+Redirecting output to a file with `chef-client > /tmp/chef.out` now captures the same output as invoking it directly on the command line with no redirection.
+
+### Path Sanity disabled by default and modified
+
+The chef client itself no long modifies its `ENV['PATH']` variable directly. When using the `shell_out` API now, in addition to setting up LANG/LANGUAGE/LC_ALL variables that API will also inject certain system paths and the ruby bindir and gemdirs into the PATH (or Path on Windows). The `shell_out_with_systems_locale` API still does not mangle any environment variables. During the Chef-13 lifecycle changes will be made to prep Chef-14 to switch so that `shell_out` by default behaves like `shell_out_with_systems_locale`. A new flag will get introduced to call `shell_out(..., internal: [true|false])` to either get the forced locale and path settings ("internal") or not. When that is introduced in Chef 13.x the default will be `true` (backwards-compat with 13.0) and that default will change in 14.0 to 'false'.
+
+The PATH changes have also been tweaked so that the ruby bindir and gemdir PATHS are prepended instead of appended to the PATH. Some system directories are still appended.
+
+Some examples of changes:
+
+- `which ruby` in 12.x will return any system ruby and fall back to the embedded ruby if using omnibus
+- `which ruby` in 13.x will return any system ruby and will not find the embedded ruby if using omnibus
+- `shell_out_with_systems_locale("which ruby")` behaves the same as `which ruby` above
+- `shell_out("which ruby")` in 12.x will return any system ruby and fall back to the embedded ruby if using omnibus
+- `shell_out("which ruby")` in 13.x will always return the omnibus ruby first (but will find the system ruby if not using omnibus)
+
+The PATH in `shell_out` can also be overridden:
+
+- `shell_out("which ruby", env: { "PATH" => nil })` - behaves like shell_out_with_systems_locale()
+- `shell_out("which ruby", env: { "PATH" => [...include PATH string here...] })` - set it arbitrarily however you need
+
+Since most providers which launch custom user commands use `shell_out_with_systems_locale` (service, execute, script, etc) the behavior will be that those commands that used to be having embedded omnibus paths injected into them no longer will. Generally this will fix more problems than it solves, but may causes issues for some use cases.
+
+### Default guard clauses (`not_if`/`only_if`) do not change the PATH or other env vars
+
+The implementation switched to `shell_out_with_systems_locale` to match `execute` resource, etc.
+
+### Chef Client will now exit using the RFC062 defined exit codes
+
+Chef Client will only exit with exit codes defined in RFC 062\. This allows other tooling to respond to how a Chef run completes. Attempting to exit Chef Client with an unsupported exit code (either via `Chef::Application.fatal!` or `Chef::Application.exit!`) will result in an exit code of 1 (GENERIC_FAILURE) and a warning in the event log.
+
+When Chef Client is running as a forked process on unix systems, the standardized exit codes are used by the child process. To actually have Chef Client return the standard exit code, `client_fork false` will need to be set in Chef Client's configuration file.
diff --git a/ROADMAP.md b/ROADMAP.md
deleted file mode 100644
index 3db4932fa9..0000000000
--- a/ROADMAP.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Roadmap
-
-This file provides direction for the project as set forth by [RFC030#Roadmap](https://github.com/chef/chef-rfc/blob/master/rfc030-maintenance-policy.md#roadmap).
-
-It is drafted by the Project Lead and the Lieutenants, with input from Maintainers and Contributors.
-
-## 2015 Q4
-* Windows - core package provider
- - Moving the remaining package providers from the Windows cookbook into core
-
-## 2016 Q1
-* Windows - core windows_feature
- - Move windows_feature from Windows cookbook, add caching
-* Windows - knife windows bootstrap, knife windows winrm in core
- - Migrate Windows bootstrapping functionality into core
diff --git a/Rakefile b/Rakefile
index 7a9ff83cb9..2ec934c432 100644
--- a/Rakefile
+++ b/Rakefile
@@ -27,14 +27,13 @@ require_relative "tasks/rspec"
require_relative "tasks/maintainers"
require_relative "tasks/cbgb"
require_relative "tasks/dependencies"
-require_relative "tasks/changelog"
+require_relative "tasks/announce"
-ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "Chef") do |package|
+ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "Chef", "chef") do |package|
package.component_paths = ["chef-config"]
package.generate_version_class = true
+ package.use_versionstring = true
end
-# Add a conservative dependency update to version:bump (which was created by PackageTask)
-task "version:bump" => %w{version:bump_patch version:update bundle:install}
task :pedant, :chef_zero_spec
diff --git a/VERSION b/VERSION
index a85572515c..3edb877817 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-12.10.27 \ No newline at end of file
+14.1.8 \ No newline at end of file
diff --git a/acceptance/.gitignore b/acceptance/.gitignore
deleted file mode 100644
index c2ab70737d..0000000000
--- a/acceptance/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-.acceptance_logs
diff --git a/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml b/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml
index 40228829bb..5f6cc7f104 100644
--- a/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml
+++ b/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml
@@ -10,14 +10,16 @@ end
driver:
name: ec2
tags:
- X-Project: chef-ci-acceptance
+ X-Dept: Eng
+ X-Contact: product-team
+ X-Application: chef-ci-acceptance
aws_ssh_key_id: <%= ENV['AWS_SSH_KEY_ID'] || ENV['USER'] || ENV['USERNAME'] %>
# test-specific stuff
region: us-west-2
- availability_zone: a
subnet_id: subnet-19ac017c
security_group_ids: ["sg-e401eb83", "sg-96274af3"]
instance_type: m3.large
+ retryable_tries: 120
# associate_public_ip: true # Don't enable public IP, as subnet specified is behind VPN
# busser installation relies on this
@@ -62,17 +64,6 @@ platforms:
image-type: machine
transport:
username: admin
- - name: debian-7
- driver:
- image_search:
- name: debian-wheezy-*
- owner-id: "379101102735"
- architecture: x86_64
- virtualization-type: hvm
- block-device-mapping.volume-type: standard
- image-type: machine
- transport:
- username: admin
#
# Ubuntu
#
@@ -213,18 +204,7 @@ platforms:
image-type: machine
user_data: |
<powershell>
- $logfile="C:\\Program Files\\Amazon\\Ec2ConfigService\\Logs\\kitchen-ec2.log"
- #PS Remoting and & winrm.cmd basic config
- Enable-PSRemoting -Force -SkipNetworkProfileCheck
- & winrm.cmd set winrm/config '@{MaxTimeoutms="1800000"}' >> $logfile
- & winrm.cmd set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}' >> $logfile
- & winrm.cmd set winrm/config/winrs '@{MaxShellsPerUser="50"}' >> $logfile
- #Server settings - support username/password login
- & winrm.cmd set winrm/config/service/auth '@{Basic="true"}' >> $logfile
- & winrm.cmd set winrm/config/service '@{AllowUnencrypted="true"}' >> $logfile
- & winrm.cmd set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}' >> $logfile
- #Firewall Config
- & netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" profile=public protocol=tcp localport=5985 remoteip=localsubnet new remoteip=any >> $logfile
+ & netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" profile=public protocol=tcp localport=5985 remoteip=localsubnet new remoteip=any
#Set script execution to unrestricted
& Set-ExecutionPolicy Unrestricted -Force
</powershell>
diff --git a/acceptance/.shared/kitchen_acceptance/.kitchen.vagrant.yml b/acceptance/.shared/kitchen_acceptance/.kitchen.vagrant.yml
index 954f5e9eb1..6b382cf417 100644
--- a/acceptance/.shared/kitchen_acceptance/.kitchen.vagrant.yml
+++ b/acceptance/.shared/kitchen_acceptance/.kitchen.vagrant.yml
@@ -25,8 +25,6 @@ verifier:
platforms:
<% %w(
debian-8
-debian-7
-debian-6
ubuntu-15.10
ubuntu-14.04
el-7
diff --git a/acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb b/acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb
index d36909e8b3..cf0b4eda5d 100644
--- a/acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb
+++ b/acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb
@@ -1,3 +1,5 @@
+require 'chef/mixin/shell_out'
+
module KitchenAcceptance
class Kitchen < Chef::Resource
resource_name :kitchen
@@ -30,21 +32,36 @@ module KitchenAcceptance
property :artifactory_username, String, default: lazy { ENV["ARTIFACTORY_USERNAME"] ? ENV["ARTIFACTORY_USERNAME"] : "" }
property :artifactory_password, String, default: lazy { ENV["ARTIFACTORY_PASSWORD"] ? ENV["ARTIFACTORY_PASSWORD"] : "" }
property :env, Hash, default: {}
- property :kitchen_options, String, default: lazy { ENV["PROJECT_NAME"] ? "-c -l debug" : "-c" }
+ property :kitchen_options, String, default: "-c"
action :run do
- execute "bundle exec kitchen #{command}#{instances ? " #{instances}" : ""}#{kitchen_options ? " #{kitchen_options}" : ""}" do
- cwd kitchen_dir
- env({
- "KITCHEN_DRIVER" => driver,
- "KITCHEN_INSTANCES" => instances,
- "KITCHEN_LOCAL_YAML" => ::File.expand_path("../../.kitchen.#{driver}.yml", __FILE__),
- "KITCHEN_CHEF_PRODUCT" => chef_product,
- "KITCHEN_CHEF_CHANNEL" => chef_channel,
- "KITCHEN_CHEF_VERSION" => chef_version,
- "ARTIFACTORY_USERNAME" => artifactory_username,
- "ARTIFACTORY_PASSWORD" => artifactory_password
- }.merge(new_resource.env))
+
+ ruby_block "copy_kitchen_logs_to_data_path" do
+ block do
+ cmd_env = {
+ "KITCHEN_DRIVER" => driver,
+ "KITCHEN_INSTANCES" => instances,
+ "KITCHEN_LOCAL_YAML" => ::File.expand_path("../../.kitchen.#{driver}.yml", __FILE__),
+ "KITCHEN_CHEF_PRODUCT" => chef_product,
+ "KITCHEN_CHEF_CHANNEL" => chef_channel,
+ "KITCHEN_CHEF_VERSION" => chef_version,
+ "ARTIFACTORY_USERNAME" => artifactory_username,
+ "ARTIFACTORY_PASSWORD" => artifactory_password
+ }.merge(new_resource.env)
+ suite = kitchen_dir.split("/").last
+ kitchen_log_path = ENV["WORKSPACE"] ? "#{ENV["WORKSPACE"]}/chef-acceptance-data/logs" : "#{kitchen_dir}/../.acceptance_data/logs/"
+
+ begin
+ shell_out!("bundle exec kitchen #{command}#{instances ? " #{instances}" : ""}#{kitchen_options ? " #{kitchen_options}" : ""}",
+ env: cmd_env,
+ timeout: 60 * 30,
+ live_stream: STDOUT,
+ cwd: kitchen_dir)
+ ensure
+ FileUtils.mkdir_p("#{kitchen_log_path}/#{suite}/#{command}")
+ FileUtils.cp_r("#{kitchen_dir}/.kitchen/logs/.", "#{kitchen_log_path}/#{suite}/#{command}")
+ end
+ end
end
end
end
diff --git a/acceptance/Gemfile b/acceptance/Gemfile
index 185437ed71..6384ae75d2 100644
--- a/acceptance/Gemfile
+++ b/acceptance/Gemfile
@@ -1,13 +1,19 @@
source "https://rubygems.org"
-gem "chef-acceptance", github: "chef/chef-acceptance"
-gem "test-kitchen"
+gem "rake" # required to build some native extensions
+gem "chef-acceptance", git: "https://github.com/chef/chef-acceptance.git"
gem "kitchen-ec2"
-gem "kitchen-inspec"
gem "inspec"
-# Pinning to github for kitchen-vagrant because 0.19.0 incorrectly
-# puts in a box_url for bento when a vagrant box in atlas is specified
gem "kitchen-vagrant"
gem "windows_chef_zero"
-gem "winrm-fs"
+gem "kitchen-inspec"
+gem "test-kitchen"
+gem "winrm-elevated"
gem "berkshelf"
+
+# Pin to 1.2.3 because current mixlib-install has a problem where unstable
+# packages are not always immediately available via the omnitruck API.
+gem "mixlib-install", "1.2.3"
+
+# for chef-14 development - pin to the released rubygems version
+gem "chef-config", "< 14.0"
diff --git a/acceptance/Gemfile.lock b/acceptance/Gemfile.lock
index 009c7cee73..80db23bc4f 100644
--- a/acceptance/Gemfile.lock
+++ b/acceptance/Gemfile.lock
@@ -1,6 +1,6 @@
GIT
- remote: git://github.com/chef/chef-acceptance.git
- revision: 49458ec493dbd12588680eea9f2f9beb76463d09
+ remote: https://github.com/chef/chef-acceptance.git
+ revision: 3877c344098021ac66134e8edb5eb805d14d4643
specs:
chef-acceptance (0.2.0)
mixlib-shellout (~> 2.0)
@@ -9,146 +9,158 @@ GIT
GEM
remote: https://rubygems.org/
specs:
- addressable (2.4.0)
- artifactory (2.3.2)
- aws-sdk (2.2.34)
- aws-sdk-resources (= 2.2.34)
- aws-sdk-core (2.2.34)
+ addressable (2.5.1)
+ public_suffix (~> 2.0, >= 2.0.2)
+ artifactory (2.8.1)
+ aws-sdk (2.9.1)
+ aws-sdk-resources (= 2.9.1)
+ aws-sdk-core (2.9.1)
+ aws-sigv4 (~> 1.0)
jmespath (~> 1.0)
- aws-sdk-resources (2.2.34)
- aws-sdk-core (= 2.2.34)
- berkshelf (4.3.2)
+ aws-sdk-resources (2.9.1)
+ aws-sdk-core (= 2.9.1)
+ aws-sigv4 (1.0.0)
+ berkshelf (5.6.4)
addressable (~> 2.3, >= 2.3.4)
- berkshelf-api-client (~> 2.0, >= 2.0.2)
- buff-config (~> 1.0)
- buff-extensions (~> 1.0)
- buff-shell_out (~> 0.1)
- celluloid (= 0.16.0)
- celluloid-io (~> 0.16.1)
+ berkshelf-api-client (>= 2.0.2, < 4.0)
+ buff-config (~> 2.0)
+ buff-extensions (~> 2.0)
+ buff-shell_out (~> 1.0)
cleanroom (~> 1.0)
faraday (~> 0.9)
httpclient (~> 2.7)
minitar (~> 0.5, >= 0.5.4)
+ mixlib-archive (~> 0.4)
octokit (~> 4.0)
retryable (~> 2.0)
- ridley (~> 4.5)
- solve (~> 2.0)
- thor (~> 0.19)
- berkshelf-api-client (2.0.2)
- faraday (~> 0.9.1)
- httpclient (~> 2.7.0)
- ridley (~> 4.5)
- buff-config (1.0.1)
- buff-extensions (~> 1.0)
- varia_model (~> 0.4)
- buff-extensions (1.0.0)
- buff-ignore (1.1.1)
- buff-ruby_engine (0.1.0)
- buff-shell_out (0.2.0)
- buff-ruby_engine (~> 0.1.0)
- builder (3.2.2)
+ ridley (~> 5.0)
+ solve (> 2.0, < 4.0)
+ thor (~> 0.19, < 0.19.2)
+ berkshelf-api-client (3.0.0)
+ faraday (~> 0.9)
+ httpclient (~> 2.7)
+ ridley (>= 4.5, < 6.0)
+ blankslate (2.1.2.4)
+ buff-config (2.0.0)
+ buff-extensions (~> 2.0)
+ varia_model (~> 0.6)
+ buff-extensions (2.0.0)
+ buff-ignore (1.2.0)
+ buff-ruby_engine (1.0.0)
+ buff-shell_out (1.1.0)
+ buff-ruby_engine (~> 1.0)
+ builder (3.2.3)
celluloid (0.16.0)
timers (~> 4.0.0)
celluloid-io (0.16.2)
celluloid (>= 0.16.0)
nio4r (>= 1.1.0)
- chef-config (12.9.38)
- fuzzyurl (~> 0.8.0)
+ chef-config (12.19.36)
+ addressable
+ fuzzyurl
mixlib-config (~> 2.0)
mixlib-shellout (~> 2.0)
cleanroom (1.0.0)
coderay (1.1.1)
- diff-lcs (1.2.5)
- docker-api (1.26.2)
+ diff-lcs (1.3)
+ docker-api (1.33.2)
excon (>= 0.38.0)
json
erubis (2.7.0)
- excon (0.49.0)
+ excon (0.55.0)
faraday (0.9.2)
multipart-post (>= 1.2, < 3)
- ffi (1.9.10)
- fuzzyurl (0.8.0)
+ ffi (1.9.18)
+ fuzzyurl (0.9.0)
gssapi (1.2.0)
ffi (>= 1.0.1)
gyoku (1.3.1)
builder (>= 2.1.2)
- hashie (3.4.3)
- hitimes (1.2.3)
- httpclient (2.7.1)
- inspec (0.19.1)
- json (~> 1.8)
+ hashie (3.5.5)
+ hitimes (1.2.4)
+ httpclient (2.8.3)
+ inspec (1.18.0)
+ addressable (~> 2.5)
+ faraday (>= 0.9.0)
+ hashie (~> 3.4)
+ json (>= 1.8, < 3.0)
method_source (~> 0.8)
+ mixlib-log
+ nokogiri (~> 1.6)
+ parallel (~> 1.9)
pry (~> 0)
- r-train (~> 0.10.5)
rainbow (~> 2)
rspec (~> 3)
rspec-its (~> 1.2)
rubyzip (~> 1.1)
+ sslshake (~> 1)
thor (~> 0.19)
- jmespath (1.2.4)
- json_pure (>= 1.8.1)
- json (1.8.3)
- json_pure (1.8.3)
- kitchen-ec2 (1.0.0)
+ toml (~> 0.1)
+ train (>= 0.22.0, < 1.0)
+ jmespath (1.3.1)
+ json (2.0.3)
+ kitchen-ec2 (1.3.2)
aws-sdk (~> 2)
excon
multi_json
retryable (~> 2.0)
test-kitchen (~> 1.4, >= 1.4.1)
- kitchen-inspec (0.12.5)
- inspec (>= 0.14.1, < 1.0.0)
+ kitchen-inspec (0.17.0)
+ hashie (~> 3.4)
+ inspec (>= 0.34.0, < 2.0.0)
test-kitchen (~> 1.6)
- kitchen-vagrant (0.20.0)
+ kitchen-vagrant (1.0.2)
test-kitchen (~> 1.4)
little-plugger (1.1.4)
- logging (2.1.0)
+ logging (2.2.0)
little-plugger (~> 1.1)
multi_json (~> 1.10)
method_source (0.8.2)
- minitar (0.5.4)
- mixlib-authentication (1.4.0)
+ mini_portile2 (2.1.0)
+ minitar (0.6.1)
+ mixlib-archive (0.4.1)
mixlib-log
- rspec-core (~> 3.2)
- rspec-expectations (~> 3.2)
- rspec-mocks (~> 3.2)
- mixlib-config (2.2.1)
- mixlib-install (1.0.7)
+ mixlib-authentication (1.4.1)
+ mixlib-log
+ mixlib-config (2.2.4)
+ mixlib-install (1.2.3)
artifactory
mixlib-shellout
mixlib-versioning
- mixlib-log (1.6.0)
- mixlib-shellout (2.2.6)
+ mixlib-log (1.7.1)
+ mixlib-shellout (2.3.2)
mixlib-versioning (1.1.0)
- molinillo (0.4.4)
- multi_json (1.11.2)
+ molinillo (0.5.7)
+ multi_json (1.12.1)
multipart-post (2.0.0)
net-scp (1.2.1)
net-ssh (>= 2.6.5)
- net-ssh (3.1.1)
- nio4r (1.2.1)
+ net-ssh (4.1.0)
+ net-ssh-gateway (1.3.0)
+ net-ssh (>= 2.6.5)
+ nio4r (2.0.0)
+ nokogiri (1.7.1)
+ mini_portile2 (~> 2.1.0)
nori (2.6.0)
- octokit (4.3.0)
- sawyer (~> 0.7.0, >= 0.5.3)
- pry (0.10.3)
+ octokit (4.6.2)
+ sawyer (~> 0.8.0, >= 0.5.3)
+ parallel (1.11.1)
+ parslet (1.5.0)
+ blankslate (~> 2.0)
+ pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
- r-train (0.10.5)
- docker-api (~> 1.26.2)
- json (~> 1.8)
- mixlib-shellout (~> 2.1)
- net-scp (~> 1.2)
- net-ssh (>= 2.9, < 4.0)
- winrm (~> 1.6)
- winrm-fs (~> 0.3)
- rainbow (2.1.0)
- retryable (2.0.3)
- ridley (4.5.0)
+ public_suffix (2.0.5)
+ rainbow (2.2.1)
+ rake (12.0.0)
+ retryable (2.0.4)
+ ridley (5.1.0)
addressable
- buff-config (~> 1.0)
- buff-extensions (~> 1.0)
- buff-ignore (~> 1.1)
- buff-shell_out (~> 0.1)
+ buff-config (~> 2.0)
+ buff-extensions (~> 2.0)
+ buff-ignore (~> 1.2)
+ buff-shell_out (~> 1.0)
celluloid (~> 0.16.0)
celluloid-io (~> 0.16.1)
chef-config (>= 12.5.0)
@@ -159,63 +171,79 @@ GEM
json (>= 1.7.7)
mixlib-authentication (>= 1.3.0)
retryable (~> 2.0)
- semverse (~> 1.1)
- varia_model (~> 0.4.0)
- rspec (3.4.0)
- rspec-core (~> 3.4.0)
- rspec-expectations (~> 3.4.0)
- rspec-mocks (~> 3.4.0)
- rspec-core (3.4.4)
- rspec-support (~> 3.4.0)
- rspec-expectations (3.4.0)
+ semverse (~> 2.0)
+ varia_model (~> 0.6)
+ rspec (3.5.0)
+ rspec-core (~> 3.5.0)
+ rspec-expectations (~> 3.5.0)
+ rspec-mocks (~> 3.5.0)
+ rspec-core (3.5.4)
+ rspec-support (~> 3.5.0)
+ rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.4.0)
+ rspec-support (~> 3.5.0)
rspec-its (1.2.0)
rspec-core (>= 3.0.0)
rspec-expectations (>= 3.0.0)
- rspec-mocks (3.4.1)
+ rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.4.0)
- rspec-support (3.4.1)
- rubyntlm (0.6.0)
- rubyzip (1.2.0)
+ rspec-support (~> 3.5.0)
+ rspec-support (3.5.0)
+ rubyntlm (0.6.1)
+ rubyzip (1.2.1)
safe_yaml (1.0.4)
- sawyer (0.7.0)
- addressable (>= 2.3.5, < 2.5)
- faraday (~> 0.8, < 0.10)
- semverse (1.2.1)
+ sawyer (0.8.1)
+ addressable (>= 2.3.5, < 2.6)
+ faraday (~> 0.8, < 1.0)
+ semverse (2.0.0)
slop (3.6.0)
- solve (2.0.3)
- molinillo (~> 0.4.2)
- semverse (~> 1.1)
- test-kitchen (1.7.3)
- mixlib-install (~> 1.0, >= 1.0.4)
+ solve (3.1.0)
+ molinillo (>= 0.5)
+ semverse (>= 1.1, < 3.0)
+ sslshake (1.1.0)
+ test-kitchen (1.16.0)
+ mixlib-install (>= 1.2, < 3.0)
mixlib-shellout (>= 1.2, < 3.0)
net-scp (~> 1.1)
- net-ssh (>= 2.9, < 4.0)
+ net-ssh (>= 2.9, < 5.0)
+ net-ssh-gateway (~> 1.2)
safe_yaml (~> 1.0)
- thor (~> 0.18)
+ thor (~> 0.19, < 0.19.2)
thor (0.19.1)
timers (4.0.4)
hitimes
- varia_model (0.4.1)
- buff-extensions (~> 1.0)
+ toml (0.1.2)
+ parslet (~> 1.5.0)
+ train (0.23.0)
+ docker-api (~> 1.26)
+ json (>= 1.8, < 3.0)
+ mixlib-shellout (~> 2.0)
+ net-scp (~> 1.2)
+ net-ssh (>= 2.9, < 5.0)
+ winrm (~> 2.0)
+ winrm-fs (~> 1.0)
+ varia_model (0.6.0)
+ buff-extensions (~> 2.0)
hashie (>= 2.0.2, < 4.0.0)
windows_chef_zero (2.0.0)
test-kitchen (>= 1.2.1)
- winrm (1.7.3)
+ winrm (2.1.3)
builder (>= 2.1.2)
+ erubis (~> 2.7)
gssapi (~> 1.2)
gyoku (~> 1.0)
httpclient (~> 2.2, >= 2.2.0.2)
logging (>= 1.6.1, < 3.0)
nori (~> 2.0)
- rubyntlm (~> 0.6.0)
- winrm-fs (0.4.2)
+ rubyntlm (~> 0.6.0, >= 0.6.1)
+ winrm-elevated (1.1.0)
+ winrm (~> 2.0)
+ winrm-fs (~> 1.0)
+ winrm-fs (1.0.1)
erubis (~> 2.7)
logging (>= 1.6.1, < 3.0)
rubyzip (~> 1.1)
- winrm (~> 1.5)
+ winrm (~> 2.0)
PLATFORMS
ruby
@@ -223,13 +251,16 @@ PLATFORMS
DEPENDENCIES
berkshelf
chef-acceptance!
+ chef-config (< 13.0)
inspec
kitchen-ec2
kitchen-inspec
kitchen-vagrant
+ mixlib-install (= 1.2.3)
+ rake
test-kitchen
windows_chef_zero
- winrm-fs
+ winrm-elevated
BUNDLED WITH
- 1.11.2
+ 1.14.6
diff --git a/acceptance/README.md b/acceptance/README.md
index fa4bab2806..992129bf2b 100644
--- a/acceptance/README.md
+++ b/acceptance/README.md
@@ -1,15 +1,16 @@
# Acceptance Testing for Chef Client
-This folder contains acceptance tests that are required for Chef client
-release readiness.
+
+This folder contains acceptance tests that are required for Chef client release readiness.
## Getting started
-The tests use the _chef-acceptance_ gem as the high level framework.
-All the gems needed to run these tests can be installed with Bundler.
+
+The tests use the _chef-acceptance_ gem as the high level framework. All the gems needed to run these tests can be installed with Bundler from this directory.
### Important Note!
-Before running chef-acceptance, you *MUST* do the following on your current session:
-```
+Before running chef-acceptance, you _MUST_ do the following on your current session:
+
+```shell
export APPBUNDLER_ALLOW_RVM=true
```
@@ -17,14 +18,14 @@ export APPBUNDLER_ALLOW_RVM=true
### Set up for local VM (Vagrant)
-If you intend to run the acceptance tests on a local VM, the supported solution is to use Vagrant.
-Ensure that Vagrant is installed on the machine that tests will run from, along with a
-virtualization driver (E.g.: VirtualBox).
+If you intend to run the acceptance tests on a local VM, the supported solution is to use Vagrant. Ensure that Vagrant is installed on the machine that tests will run from, along with a virtualization driver (E.g.: VirtualBox).
-Set up the KITCHEN_DRIVER environment variable appropriately (value should be "vagrant"). E.g.:
-```
+Set up the KITCHEN_DRIVER environment variable appropriately (value should be "vagrant"). E.g.:
+
+```shell
export KITCHEN_DRIVER=vagrant
```
+
Add this to your shell profile or startup script as needed.
### Set up for cloud VM (EC2)
@@ -34,44 +35,59 @@ If you intend to run the acceptance tests on a cloud VM, the supported solution
The steps you will need to do are:
1. Add your AWS credentials to the machine - e.g., to the ~/.aws/credentials directory.
- - The easiest way to do this is to download the aws command line (`brew install awscli` on OS/X) and run `aws configure`.
+
+ - The easiest way to do this is to download the aws command line (`brew install awscli` on OS/X) and run `aws configure`.
+
2. Create or import a SSH key to AWS. (If you already have one, you can skip the import/create)
- - In the AWS console, click Key Pairs, then Create Key Pair or Import Key Pair.
+
+ - In the AWS console, click Key Pairs, then Create Key Pair or Import Key Pair.
+
3. Copy or move the private key file (USERNAME.pem) to the SSH folder (e.g. `~/.ssh/USERNAME.pem`).
- - If you Created a key pair in step 2, download the private key and move it to `~/.ssh`.
- - If you Importd a key pair in step 2, just ensure your private key is in `~/.ssh` and has the same name as the key pair (`~/.ssh/USERNAME` or `~/.ssh/USERNAME.pem`).
+
+ - If you Created a key pair in step 2, download the private key and move it to `~/.ssh`.
+ - If you Importd a key pair in step 2, just ensure your private key is in `~/.ssh` and has the same name as the key pair (`~/.ssh/USERNAME` or `~/.ssh/USERNAME.pem`).
+
4. Set AWS_SSH_KEY_ID to the SSH key name.
- - This is **optional** if your AWS SSH key name is your local username.
- - You may want to set this in your shell `.profile`.
- ```shell
- export AWS_SSH_KEY_ID=name-of-private-key
- ```
+ - This is **optional** if your AWS SSH key name is your local username.
+ - You may want to set this in your shell `.profile`.
+
+ ```shell
+ export AWS_SSH_KEY_ID=name-of-private-key
+ ```
+
5. Set the private key to only be readable by root
- ```shell
- chmod 0400 ~/.ssh/USERNAME.pem
- ```
+ ```shell
+ chmod 0400 ~/.ssh/USERNAME.pem
+ ```
+
6. Set up the KITCHEN_DRIVER environment variable appropriately (value should be "ec2"). (This is optional, as ec2 is the default.) E.g.:
- ```shell
- export KITCHEN_DRIVER=ec2
- ```
- Add this to your shell profile or startup script as needed.
+ ```shell
+ export KITCHEN_DRIVER=ec2
+ ```
+
+ Add this to your shell profile or startup script as needed.
+
7. **Connect to Chef VPN**. The instances you create will not have public IPs!
## Setting up and running a test suite
+
To get started, do a bundle install from the acceptance directory:
+
```shell
chef/acceptance$ bundle install --binstubs
```
To get some basic info and ensure chef-acceptance can be run, do:
+
```shell
chef/acceptance$ bin/chef-acceptance info
```
To run a particular test suite, do the following:
+
```shell
chef/acceptance$ bin/chef-acceptance test TEST_SUITE
```
@@ -83,26 +99,20 @@ chef/acceptance$ export KITCHEN_INSTANCES=*-ubuntu-1404
chef/acceptance$ bin/chef-acceptance test cookbook-git
```
-If KITCHEN_INSTANCES is not specified, the default instances are default-ubuntu-1404 and default-windows-windows-2012r2. All selected instances will be run in *parallel* if the driver supports it (ec2 does, vagrant doesn't).
+If KITCHEN_INSTANCES is not specified, the default instances are default-ubuntu-1404 and default-windows-windows-2012r2\. All selected instances will be run in _parallel_ if the driver supports it (ec2 does, vagrant doesn't).
## Optional Settings
-In addition to the environment settings above, there are a number of
-key values that are available to set for changing the way the acceptance
-tests are run.
+In addition to the environment settings above, there are a number of key values that are available to set for changing the way the acceptance tests are run.
### KITCHEN_CHEF_CHANNEL
-Use this setting to specify which channel we will pull the chef build from.
-The default is to use the "current" channel, unless the ARTIFACTORY_USERNAME is set
-(which normally happens when running under Jenkins), in which case the default is
-changed to "unstable".
+Use this setting to specify which channel we will pull the chef build from. The default is to use the "current" channel, unless the ARTIFACTORY_USERNAME is set (which normally happens when running under Jenkins), in which case the default is changed to "unstable".
```shell
export KITCHEN_CHEF_CHANNEL=name-of-channel
```
-
### KITCHEN_CHEF_VERSION
Use this setting to override the version of the Chef client that is installed. The default is to get the latest version in the desired channel.
@@ -122,11 +132,6 @@ export ARTIFACTORY_PASSWORD=password
## Future Work
-Currently, there is no simple mechanism for chef-acceptance
-to build an Omnibus package of the developers local chef
-instance and run acceptance tests on it - the only packages
-that can be exercised are ones that come from one of the
-pipeline channels (unstable, current or stable).
+Currently, there is no simple mechanism for chef-acceptance to build an Omnibus package of the developers local chef instance and run acceptance tests on it - the only packages that can be exercised are ones that come from one of the pipeline channels (unstable, current or stable).
-This is not an issue when adding acceptance tests for pre-existing functionality (as that functionality is presumed
-to already be in a build in one of the pipeline channels).
+This is not an issue when adding acceptance tests for pre-existing functionality (as that functionality is presumed to already be in a build in one of the pipeline channels).
diff --git a/acceptance/basics/test/integration/chef-current-install/serverspec/chef_client_spec.rb b/acceptance/basics/test/integration/chef-current-install/serverspec/chef_client_spec.rb
index dd0e0e4e34..6c5a1d7f58 100644
--- a/acceptance/basics/test/integration/chef-current-install/serverspec/chef_client_spec.rb
+++ b/acceptance/basics/test/integration/chef-current-install/serverspec/chef_client_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
gem_path = "/opt/chef/embedded/bin/gem"
-white_list = %w{chef-config json rake}
+white_list = %w{addressable chef-config json minitest rake}
describe "gem list" do
it "should not have non-whitelisted duplicate gems" do
diff --git a/acceptance/basics/test/integration/helpers/serverspec/Gemfile b/acceptance/basics/test/integration/helpers/serverspec/Gemfile
new file mode 100644
index 0000000000..b56d1e1298
--- /dev/null
+++ b/acceptance/basics/test/integration/helpers/serverspec/Gemfile
@@ -0,0 +1,8 @@
+source "https://rubygems.org"
+
+# Until https://github.com/test-kitchen/busser-serverspec/pull/42 is merged and
+# released, we need to include rake and rspec-core in the Gemfile
+gem "rake"
+gem "rspec-core"
+gem "busser-serverspec"
+gem "serverspec"
diff --git a/acceptance/fips/.acceptance/acceptance-cookbook/.gitignore b/acceptance/data-collector/.acceptance/acceptance-cookbook/.gitignore
index 041413b040..041413b040 100644
--- a/acceptance/fips/.acceptance/acceptance-cookbook/.gitignore
+++ b/acceptance/data-collector/.acceptance/acceptance-cookbook/.gitignore
diff --git a/acceptance/data-collector/.acceptance/acceptance-cookbook/metadata.rb b/acceptance/data-collector/.acceptance/acceptance-cookbook/metadata.rb
new file mode 100644
index 0000000000..68fc3af2dd
--- /dev/null
+++ b/acceptance/data-collector/.acceptance/acceptance-cookbook/metadata.rb
@@ -0,0 +1,3 @@
+name 'acceptance-cookbook'
+depends "kitchen_acceptance"
+depends "data-collector-test"
diff --git a/acceptance/data-collector/.acceptance/acceptance-cookbook/recipes/destroy.rb b/acceptance/data-collector/.acceptance/acceptance-cookbook/recipes/destroy.rb
new file mode 100644
index 0000000000..7f4be2c7f7
--- /dev/null
+++ b/acceptance/data-collector/.acceptance/acceptance-cookbook/recipes/destroy.rb
@@ -0,0 +1,2 @@
+log "Running 'destroy' recipe from the acceptance-cookbook in directory '#{node['chef-acceptance']['suite-dir']}'"
+kitchen "destroy"
diff --git a/acceptance/data-collector/.acceptance/acceptance-cookbook/recipes/provision.rb b/acceptance/data-collector/.acceptance/acceptance-cookbook/recipes/provision.rb
new file mode 100644
index 0000000000..c707e874f0
--- /dev/null
+++ b/acceptance/data-collector/.acceptance/acceptance-cookbook/recipes/provision.rb
@@ -0,0 +1,2 @@
+log "Running 'provision' recipe from the acceptance-cookbook in directory '#{node['chef-acceptance']['suite-dir']}'"
+kitchen "converge"
diff --git a/acceptance/data-collector/.acceptance/acceptance-cookbook/recipes/verify.rb b/acceptance/data-collector/.acceptance/acceptance-cookbook/recipes/verify.rb
new file mode 100644
index 0000000000..e4a547272b
--- /dev/null
+++ b/acceptance/data-collector/.acceptance/acceptance-cookbook/recipes/verify.rb
@@ -0,0 +1,2 @@
+log "Running 'verify' recipe from the acceptance-cookbook in directory '#{node['chef-acceptance']['suite-dir']}'"
+kitchen "verify"
diff --git a/kitchen-tests/cookbooks/audit_test/.gitignore b/acceptance/data-collector/.acceptance/data-collector-test/.gitignore
index 1e074046f0..ec2a890bd3 100644
--- a/kitchen-tests/cookbooks/audit_test/.gitignore
+++ b/acceptance/data-collector/.acceptance/data-collector-test/.gitignore
@@ -1,4 +1,5 @@
.vagrant
+Berksfile.lock
*~
*#
.#*
diff --git a/acceptance/data-collector/.acceptance/data-collector-test/Berksfile b/acceptance/data-collector/.acceptance/data-collector-test/Berksfile
new file mode 100644
index 0000000000..34fea2166b
--- /dev/null
+++ b/acceptance/data-collector/.acceptance/data-collector-test/Berksfile
@@ -0,0 +1,3 @@
+source 'https://supermarket.chef.io'
+
+metadata
diff --git a/acceptance/data-collector/.acceptance/data-collector-test/files/default/api.rb b/acceptance/data-collector/.acceptance/data-collector-test/files/default/api.rb
new file mode 100644
index 0000000000..fcfc704d29
--- /dev/null
+++ b/acceptance/data-collector/.acceptance/data-collector-test/files/default/api.rb
@@ -0,0 +1,101 @@
+require "json"
+require "sinatra"
+
+class Chef
+ class Node
+ # dummy class for JSON parsing
+ end
+end
+
+module ApiHelpers
+ def self.payload_type(payload)
+ message_type = payload["message_type"]
+ status = payload["status"]
+
+ message_type == "run_converge" ? "#{message_type}.#{status}" : message_type
+ end
+end
+
+class Counter
+ class << self
+ attr_accessor :counters
+
+ def counters
+ @counters ||= Hash.new { |h, k| h[k] = 0 }
+ end
+
+ def reset
+ @counters = nil
+ end
+
+ def increment(payload)
+ counter_name = ApiHelpers.payload_type(payload)
+ counters[counter_name] += 1
+ end
+
+ def to_json
+ counters.to_json
+ end
+ end
+end
+
+class MessageCache
+ include ApiHelpers
+
+ class << self
+ attr_accessor :message_cache
+
+ def message_cache
+ @message_cache ||= {}
+ end
+
+ def reset
+ @message_cache = nil
+ end
+
+ def store(payload)
+ cache_key = ApiHelpers.payload_type(payload)
+
+ message_cache[cache_key] = payload
+ end
+
+ def fetch(cache_key)
+ message_cache[cache_key].to_json
+ end
+ end
+end
+
+Counter.reset
+
+get "/" do
+ "Data Collector API server"
+end
+
+get "/reset-counters" do
+ Counter.reset
+ "counters reset"
+end
+
+get "/counters" do
+ Counter.to_json
+end
+
+get "/cache/:key" do |cache_key|
+ MessageCache.fetch(cache_key)
+end
+
+get "/reset-cache" do
+ MessageCache.reset
+ "cache reset"
+end
+
+post "/data-collector/v0" do
+ body = request.body.read
+ payload = JSON.load(body)
+
+ Counter.increment(payload)
+ MessageCache.store(payload)
+
+ status 201
+ "message received"
+end
diff --git a/acceptance/fips/test/integration/fips-integration/serverspec/Gemfile b/acceptance/data-collector/.acceptance/data-collector-test/files/default/apigemfile
index 3921e6a92a..94fc334d88 100644
--- a/acceptance/fips/test/integration/fips-integration/serverspec/Gemfile
+++ b/acceptance/data-collector/.acceptance/data-collector-test/files/default/apigemfile
@@ -1,3 +1,3 @@
source "https://rubygems.org"
-gem "mixlib-shellout"
+gem "sinatra"
diff --git a/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-both-mode.rb b/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-both-mode.rb
new file mode 100644
index 0000000000..89f3555be1
--- /dev/null
+++ b/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-both-mode.rb
@@ -0,0 +1,4 @@
+chef_server_url "http://localhost:8889"
+node_name "data-collector-test"
+data_collector.server_url "http://localhost:9292/data-collector/v0"
+data_collector.mode :both
diff --git a/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-client-mode.rb b/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-client-mode.rb
new file mode 100644
index 0000000000..8e3f0c4845
--- /dev/null
+++ b/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-client-mode.rb
@@ -0,0 +1,4 @@
+chef_server_url "http://localhost:8889"
+node_name "data-collector-test"
+data_collector.server_url "http://localhost:9292/data-collector/v0"
+data_collector.mode :client
diff --git a/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-no-endpoint.rb b/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-no-endpoint.rb
new file mode 100644
index 0000000000..f8374107ea
--- /dev/null
+++ b/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-no-endpoint.rb
@@ -0,0 +1,2 @@
+chef_server_url "http://localhost:8889"
+node_name "data-collector-test"
diff --git a/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-solo-mode.rb b/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-solo-mode.rb
new file mode 100644
index 0000000000..904e952e85
--- /dev/null
+++ b/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-solo-mode.rb
@@ -0,0 +1,4 @@
+chef_server_url "http://localhost:8889"
+node_name "data-collector-test"
+data_collector.server_url "http://localhost:9292/data-collector/v0"
+data_collector.mode :solo
diff --git a/acceptance/data-collector/.acceptance/data-collector-test/files/default/config.ru b/acceptance/data-collector/.acceptance/data-collector-test/files/default/config.ru
new file mode 100644
index 0000000000..81cf29d9fb
--- /dev/null
+++ b/acceptance/data-collector/.acceptance/data-collector-test/files/default/config.ru
@@ -0,0 +1,2 @@
+require_relative "./api"
+run Sinatra::Application
diff --git a/acceptance/data-collector/.acceptance/data-collector-test/metadata.rb b/acceptance/data-collector/.acceptance/data-collector-test/metadata.rb
new file mode 100644
index 0000000000..34402c8af1
--- /dev/null
+++ b/acceptance/data-collector/.acceptance/data-collector-test/metadata.rb
@@ -0,0 +1,7 @@
+name 'data-collector-test'
+maintainer 'Adam Leff'
+maintainer_email 'adamleff@chef.io'
+license 'Apache-2.0'
+description 'Installs/Configures data-collector-test'
+long_description 'Installs/Configures data-collector-test'
+version '0.1.0'
diff --git a/acceptance/data-collector/.acceptance/data-collector-test/recipes/default.rb b/acceptance/data-collector/.acceptance/data-collector-test/recipes/default.rb
new file mode 100644
index 0000000000..61915c54b7
--- /dev/null
+++ b/acceptance/data-collector/.acceptance/data-collector-test/recipes/default.rb
@@ -0,0 +1,44 @@
+
+embedded_path = "/opt/chef/embedded/bin:#{ENV["PATH"]}"
+
+api_root_dir = "/var/opt/data_collector_api"
+
+directory api_root_dir do
+ recursive true
+end
+
+cookbook_file ::File.join(api_root_dir, "Gemfile") do
+ source "apigemfile"
+end
+
+cookbook_file ::File.join(api_root_dir, "config.ru")
+
+cookbook_file ::File.join(api_root_dir, "api.rb")
+
+execute "bundle install --binstubs" do
+ cwd api_root_dir
+ environment({ "PATH" => embedded_path })
+end
+
+pid_file = "/var/run/api.pid"
+running_pid = ::File.exist?(pid_file) ? ::File.read(pid_file).strip : nil
+
+execute "kill existing API process" do
+ command "kill #{running_pid}"
+ environment({ "PATH" => embedded_path })
+ not_if { running_pid.nil? }
+end
+
+execute "start API" do
+ command "bin/rackup -D -P #{pid_file}"
+ environment({ "PATH" => embedded_path })
+ cwd api_root_dir
+end
+
+directory "/etc/chef"
+
+["both-mode", "client-mode", "no-endpoint", "solo-mode"].each do |config_file|
+ cookbook_file "/etc/chef/#{config_file}.rb" do
+ source "client-rb-#{config_file}.rb"
+ end
+end
diff --git a/acceptance/data-collector/.kitchen.yml b/acceptance/data-collector/.kitchen.yml
new file mode 100644
index 0000000000..f719e3ea69
--- /dev/null
+++ b/acceptance/data-collector/.kitchen.yml
@@ -0,0 +1,9 @@
+verifier:
+ name: busser
+
+suites:
+ - name: default
+ includes:
+ - ubuntu-14.04
+ run_list:
+ - recipe[data-collector-test::default]
diff --git a/acceptance/data-collector/Berksfile b/acceptance/data-collector/Berksfile
new file mode 100644
index 0000000000..b8f003071b
--- /dev/null
+++ b/acceptance/data-collector/Berksfile
@@ -0,0 +1,3 @@
+source "https://supermarket.chef.io"
+
+cookbook "data-collector-test", path: File.join(File.expand_path(File.dirname(__FILE__)), ".acceptance", "data-collector-test")
diff --git a/acceptance/data-collector/test/integration/default/serverspec/default_spec.rb b/acceptance/data-collector/test/integration/default/serverspec/default_spec.rb
new file mode 100644
index 0000000000..040fbcbcca
--- /dev/null
+++ b/acceptance/data-collector/test/integration/default/serverspec/default_spec.rb
@@ -0,0 +1,208 @@
+#
+# Author:: Adam Leff (<adamleff@chef.io)
+#
+# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "json"
+require "serverspec"
+
+set :backend, :exec
+
+class Chef
+ class Node
+ # dummy class for parsing JSON action messages
+ end
+end
+
+shared_examples_for "reset counters" do
+ it "resets the counters" do
+ expect(command("curl http://localhost:9292/reset-counters").exit_status).to eq(0)
+ end
+end
+
+shared_examples_for "reset cache" do
+ it "resets the message cache" do
+ expect(command("curl http://localhost:9292/reset-cache").exit_status).to eq(0)
+ end
+end
+
+shared_examples_for "successful chef run" do |cmd|
+ include_examples "reset counters"
+ include_examples "reset cache"
+
+ it "runs chef and expects a zero exit status" do
+ expect(command(cmd).exit_status).to eq(0)
+ end
+end
+
+shared_examples_for "unsuccessful chef run" do |cmd|
+ include_examples "reset counters"
+ include_examples "reset cache"
+
+ it "runs chef and expects a non-zero exit status" do
+ expect(command(cmd).exit_status).not_to eq(0)
+ end
+end
+
+shared_examples_for "counter checks" do |counters_to_check|
+ counters_to_check.each do |counter, value|
+ it "counter #{counter} should equal #{value.inspect}" do
+ counter_values = JSON.load(command("curl http://localhost:9292/counters").stdout)
+ expect(counter_values[counter]).to eq(value)
+ end
+ end
+end
+
+shared_examples_for "run_start payload check" do
+ describe "run_start message" do
+ let(:required_fields) do
+ %w{
+ chef_server_fqdn
+ entity_uuid
+ id
+ message_version
+ message_type
+ node_name
+ organization_name
+ run_id
+ source
+ start_time
+ }
+ end
+ let(:optional_fields) { [] }
+
+ it "is not missing any required fields" do
+ payload = JSON.load(command("curl http://localhost:9292/cache/run_start").stdout)
+ missing_fields = required_fields.select { |key| !payload.key?(key) }
+ expect(missing_fields).to eq([])
+ end
+
+ end
+end
+
+shared_examples_for "run_converge.success payload check" do
+ describe "run_converge success message" do
+ let(:required_fields) do
+ %w{
+ chef_server_fqdn
+ entity_uuid
+ id
+ end_time
+ expanded_run_list
+ message_type
+ message_version
+ node
+ node_name
+ organization_name
+ policy_name
+ policy_group
+ resources
+ run_id
+ run_list
+ source
+ start_time
+ status
+ total_resource_count
+ updated_resource_count
+ deprecations
+ }
+ end
+ let(:optional_fields) { [] }
+
+ it "is not missing any required fields" do
+ payload = JSON.load(command("curl http://localhost:9292/cache/run_converge.success").stdout)
+ missing_fields = required_fields.select { |key| !payload.key?(key) }
+ expect(missing_fields).to eq([])
+ end
+
+ end
+end
+
+shared_examples_for "run_converge.failure payload check" do
+ describe "run_converge failure message" do
+ let(:required_fields) do
+ %w{
+ chef_server_fqdn
+ entity_uuid
+ error
+ id
+ end_time
+ expanded_run_list
+ message_type
+ message_version
+ node
+ node_name
+ organization_name
+ policy_name
+ policy_group
+ resources
+ run_id
+ run_list
+ source
+ start_time
+ status
+ total_resource_count
+ updated_resource_count
+ deprecations
+ }
+ end
+ let(:optional_fields) { [] }
+
+ it "is not missing any required fields" do
+ payload = JSON.load(command("curl http://localhost:9292/cache/run_converge.failure").stdout)
+ missing_fields = required_fields.select { |key| !payload.key?(key) }
+ expect(missing_fields).to eq([])
+ end
+
+ it "does not have any extra fields" do
+ payload = JSON.load(command("curl http://localhost:9292/cache/run_converge.failure").stdout)
+ extra_fields = payload.keys.select { |key| !required_fields.include?(key) && !optional_fields.include?(key) }
+ expect(extra_fields).to eq([])
+ end
+ end
+end
+
+describe "CCR with no data collector URL configured" do
+ include_examples "successful chef run", "chef-client -z -c /etc/chef/no-endpoint.rb"
+ include_examples "counter checks", { "run_start" => nil, "run_converge.success" => nil, "run_converge.failure" => nil }
+end
+
+describe "CCR, local mode, config in solo mode" do
+ include_examples "successful chef run", "chef-client -z -c /etc/chef/solo-mode.rb"
+ include_examples "counter checks", { "run_start" => 1, "run_converge.success" => 1, "run_converge.failure" => nil }
+ include_examples "run_start payload check"
+ include_examples "run_converge.success payload check"
+end
+
+describe "CCR, local mode, config in client mode" do
+ include_examples "successful chef run", "chef-client -z -c /etc/chef/client-mode.rb"
+ include_examples "counter checks", { "run_start" => nil, "run_converge.success" => nil, "run_converge.failure" => nil }
+end
+
+describe "CCR, local mode, config in both mode" do
+ include_examples "successful chef run", "chef-client -z -c /etc/chef/both-mode.rb"
+ include_examples "counter checks", { "run_start" => 1, "run_converge.success" => 1, "run_converge.failure" => nil }
+ include_examples "run_start payload check"
+ include_examples "run_converge.success payload check"
+end
+
+describe "CCR, local mode, config in solo mode, failed run" do
+ include_examples "unsuccessful chef run", "chef-client -z -c /etc/chef/solo-mode.rb -r 'recipe[cookbook-that-does-not-exist::default]'"
+ include_examples "counter checks", { "run_start" => 1, "run_converge.success" => nil, "run_converge.failure" => 1 }
+ include_examples "run_start payload check"
+ include_examples "run_converge.failure payload check"
+end
diff --git a/acceptance/data-collector/test/integration/helpers/serverspec/Gemfile b/acceptance/data-collector/test/integration/helpers/serverspec/Gemfile
new file mode 100644
index 0000000000..b56d1e1298
--- /dev/null
+++ b/acceptance/data-collector/test/integration/helpers/serverspec/Gemfile
@@ -0,0 +1,8 @@
+source "https://rubygems.org"
+
+# Until https://github.com/test-kitchen/busser-serverspec/pull/42 is merged and
+# released, we need to include rake and rspec-core in the Gemfile
+gem "rake"
+gem "rspec-core"
+gem "busser-serverspec"
+gem "serverspec"
diff --git a/acceptance/fips/.acceptance/acceptance-cookbook/metadata.rb b/acceptance/fips/.acceptance/acceptance-cookbook/metadata.rb
deleted file mode 100644
index 6c754560f0..0000000000
--- a/acceptance/fips/.acceptance/acceptance-cookbook/metadata.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-name "acceptance-cookbook"
-depends "kitchen_acceptance"
diff --git a/acceptance/fips/.acceptance/acceptance-cookbook/recipes/destroy.rb b/acceptance/fips/.acceptance/acceptance-cookbook/recipes/destroy.rb
deleted file mode 100644
index e2d663ac2f..0000000000
--- a/acceptance/fips/.acceptance/acceptance-cookbook/recipes/destroy.rb
+++ /dev/null
@@ -1 +0,0 @@
-kitchen "destroy"
diff --git a/acceptance/fips/.acceptance/acceptance-cookbook/recipes/provision.rb b/acceptance/fips/.acceptance/acceptance-cookbook/recipes/provision.rb
deleted file mode 100644
index 5726c0e7b5..0000000000
--- a/acceptance/fips/.acceptance/acceptance-cookbook/recipes/provision.rb
+++ /dev/null
@@ -1 +0,0 @@
-kitchen "converge"
diff --git a/acceptance/fips/.acceptance/acceptance-cookbook/recipes/verify.rb b/acceptance/fips/.acceptance/acceptance-cookbook/recipes/verify.rb
deleted file mode 100644
index 05ac94ce66..0000000000
--- a/acceptance/fips/.acceptance/acceptance-cookbook/recipes/verify.rb
+++ /dev/null
@@ -1 +0,0 @@
-kitchen "verify"
diff --git a/acceptance/fips/.kitchen.yml b/acceptance/fips/.kitchen.yml
deleted file mode 100644
index 23280f9142..0000000000
--- a/acceptance/fips/.kitchen.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-suites:
- - name: fips-unit-functional
- includes: [centos-6, windows-2012r2]
- run_list:
-
- - name: fips-integration
- includes: [centos-6, windows-2012r2]
- run_list:
diff --git a/acceptance/fips/test/integration/fips-integration/serverspec/fips-integration_spec.rb b/acceptance/fips/test/integration/fips-integration/serverspec/fips-integration_spec.rb
deleted file mode 100644
index 59a888bef1..0000000000
--- a/acceptance/fips/test/integration/fips-integration/serverspec/fips-integration_spec.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require "mixlib/shellout"
-require "bundler"
-
-describe "Chef Fips Integration Specs" do
- def windows?
- if RUBY_PLATFORM =~ /mswin|mingw|windows/
- true
- else
- false
- end
- end
-
- let(:omnibus_root) do
- if windows?
- "c:/opscode/chef"
- else
- "/opt/chef"
- end
- end
-
- let(:env) do
- {
- "PATH" => [ "#{omnibus_root}/embedded/bin", ENV["PATH"] ].join(File::PATH_SEPARATOR),
- "BUNDLE_GEMFILE" => "#{omnibus_root}/Gemfile",
- "GEM_PATH" => nil, "GEM_CACHE" => nil, "GEM_HOME" => nil,
- "BUNDLE_IGNORE_CONFIG" => "true",
- "BUNDLE_FROZEN" => "1",
- "CHEF_FIPS" => "1"
- }
- end
-
- let(:chef_dir) do
- cmd = Mixlib::ShellOut.new("bundle show chef", env: env).run_command
- cmd.error!
- cmd.stdout.chomp
- end
-
- def run_rspec_test(test)
- Bundler.with_clean_env do
- cmd = Mixlib::ShellOut.new(
- "bundle exec rspec -f documentation -t ~requires_git #{test}",
- env: env, cwd: chef_dir, timeout: 3600
- )
- cmd.run_command.error!
- end
- end
-
- it "passes the integration specs" do
- run_rspec_test("spec/integration")
- end
-end
diff --git a/acceptance/fips/test/integration/fips-unit-functional/serverspec/Gemfile b/acceptance/fips/test/integration/fips-unit-functional/serverspec/Gemfile
deleted file mode 100644
index 3921e6a92a..0000000000
--- a/acceptance/fips/test/integration/fips-unit-functional/serverspec/Gemfile
+++ /dev/null
@@ -1,3 +0,0 @@
-source "https://rubygems.org"
-
-gem "mixlib-shellout"
diff --git a/acceptance/fips/test/integration/fips-unit-functional/serverspec/fips-unit-functional_spec.rb b/acceptance/fips/test/integration/fips-unit-functional/serverspec/fips-unit-functional_spec.rb
deleted file mode 100644
index 446261f83f..0000000000
--- a/acceptance/fips/test/integration/fips-unit-functional/serverspec/fips-unit-functional_spec.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-require "mixlib/shellout"
-require "bundler"
-
-describe "Chef Fips Unit/Functional Specs" do
- def windows?
- if RUBY_PLATFORM =~ /mswin|mingw|windows/
- true
- else
- false
- end
- end
-
- let(:omnibus_root) do
- if windows?
- "c:/opscode/chef"
- else
- "/opt/chef"
- end
- end
-
- let(:env) do
- {
- "PATH" => [ "#{omnibus_root}/embedded/bin", ENV["PATH"] ].join(File::PATH_SEPARATOR),
- "BUNDLE_GEMFILE" => "#{omnibus_root}/Gemfile",
- "GEM_PATH" => nil, "GEM_CACHE" => nil, "GEM_HOME" => nil,
- "BUNDLE_IGNORE_CONFIG" => "true",
- "BUNDLE_FROZEN" => "1",
- "CHEF_FIPS" => "1"
- }
- end
-
- let(:chef_dir) do
- cmd = Mixlib::ShellOut.new("bundle show chef", env: env).run_command
- cmd.error!
- cmd.stdout.chomp
- end
-
- def run_rspec_test(test)
- Bundler.with_clean_env do
- cmd = Mixlib::ShellOut.new(
- "bundle exec rspec -f documentation -t ~requires_git #{test}",
- env: env, cwd: chef_dir, timeout: 3600
- )
- cmd.run_command.error!
- end
- end
-
- it "passes the unit specs" do
- run_rspec_test("spec/unit")
- end
-
- it "passes the functional specs" do
- run_rspec_test("spec/functional")
- end
-
-end
diff --git a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb b/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb
index 203ea9809a..dec292567c 100644
--- a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb
+++ b/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb
@@ -3,22 +3,20 @@ class TopCookbooks < Chef::Resource
property :command, String, name_property: true
+ # Disabling all windows tests until winrm issue is properly settled.
+ #
action :run do
- cookbook_kitchen "#{command} docker" do
- end
-
- cookbook_kitchen "#{command} git" do
- end
-
- cookbook_kitchen "#{command} learn-the-basics-ubuntu" do
- repository "learn-chef/learn-chef-acceptance"
- cookbook_relative_dir "cookbooks/learn-the-basics-ubuntu"
- end
+
+ # FIXME: waiting for https://github.com/learn-chef/learn-chef-acceptance/pull/23
+ # cookbook_kitchen "#{command} learn-the-basics-ubuntu" do
+ # repository "learn-chef/learn-chef-acceptance"
+ # cookbook_relative_dir "cookbooks/learn-the-basics-ubuntu"
+ # end
- cookbook_kitchen "#{command} learn-the-basics-windows" do
- repository "learn-chef/learn-chef-acceptance"
- cookbook_relative_dir "cookbooks/learn-the-basics-windows"
- end
+ # cookbook_kitchen "#{command} learn-the-basics-windows" do
+ # repository "learn-chef/learn-chef-acceptance"
+ # cookbook_relative_dir "cookbooks/learn-the-basics-windows"
+ # end
cookbook_kitchen "#{command} powershell" do
end
@@ -33,8 +31,6 @@ class TopCookbooks < Chef::Resource
repository "adamedx/winbox"
end
- # Temporarily disabling windows and chocolatey to eliminate
- # transient errors on the builders
# cookbook_kitchen "#{command} windows" do
# end
diff --git a/acceptance/top-cookbooks/.kitchen.docker.yml b/acceptance/top-cookbooks/.kitchen.docker.yml
deleted file mode 100644
index 74a238495a..0000000000
--- a/acceptance/top-cookbooks/.kitchen.docker.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-suites:
- - name: docker-default
- attributes:
- docker:
- version: 1.10.0
- run_list:
- - recipe[apt]
- - recipe[apt-docker]
- - recipe[docker_test::installation_package]
- - recipe[docker_test::service_upstart]
- - recipe[docker_test::auto]
- includes: [ubuntu-14.04]
diff --git a/acceptance/top-cookbooks/.kitchen.git.yml b/acceptance/top-cookbooks/.kitchen.git.yml
deleted file mode 100644
index 302657dffc..0000000000
--- a/acceptance/top-cookbooks/.kitchen.git.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-suites:
- - name: git-default
- # Ubuntu images need to run apt update
- run_list: ["recipe[apt]","recipe[git]"]
- includes: [ubuntu-14.04]
- - name: git-source
- run_list: ["recipe[git::source]"]
- includes: [nonexistent]
- - name: git-default-windows
- run_list: ["recipe[git]"]
- includes: [windows-2012r2]
diff --git a/acceptance/trivial/.kitchen.yml b/acceptance/trivial/.kitchen.yml
index 1e0af03503..0db67c468f 100644
--- a/acceptance/trivial/.kitchen.yml
+++ b/acceptance/trivial/.kitchen.yml
@@ -3,5 +3,5 @@ verifier:
suites:
- name: chef-current-install
- includes: [windows-2012r2]
+ includes: [ubuntu-14.04, windows-server-2012r2]
run_list:
diff --git a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb
index e2d663ac2f..e12f938cf3 100644
--- a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb
+++ b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb
@@ -1 +1 @@
-kitchen "destroy"
+#kitchen "destroy"
diff --git a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb
index 5726c0e7b5..cec9de4e5d 100644
--- a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb
+++ b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb
@@ -1 +1 @@
-kitchen "converge"
+#kitchen "converge"
diff --git a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb
index 05ac94ce66..52e3560cf6 100644
--- a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb
+++ b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb
@@ -1 +1 @@
-kitchen "verify"
+#kitchen "verify"
diff --git a/acceptance/windows-service/test/integration/chef-windows-service/inspec/chef_windows_service_spec.rb b/acceptance/windows-service/test/integration/chef-windows-service/inspec/chef_windows_service_spec.rb
index a791177362..75383b69bf 100644
--- a/acceptance/windows-service/test/integration/chef-windows-service/inspec/chef_windows_service_spec.rb
+++ b/acceptance/windows-service/test/integration/chef-windows-service/inspec/chef_windows_service_spec.rb
@@ -13,7 +13,7 @@ describe service("chef-client") do
it { should_not be_running }
end
-describe command("chef-service-manager -a install") do
+describe command("/opscode/chef/bin/chef-service-manager.bat -a install") do
its("exit_status") { should eq 0 }
its(:stdout) { should match /Service 'chef-client' has successfully been installed./ }
end
@@ -24,7 +24,7 @@ describe service("chef-client") do
it { should_not be_running }
end
-describe command("chef-service-manager -a start") do
+describe command("/opscode/chef/bin/chef-service-manager.bat -a start") do
its("exit_status") { should eq 0 }
its(:stdout) { should match /Service 'chef-client' is now 'running'/ }
end
@@ -35,7 +35,7 @@ describe service("chef-client") do
it { should be_running }
end
-describe command("chef-service-manager -a stop") do
+describe command("/opscode/chef/bin/chef-service-manager.bat -a stop") do
its("exit_status") { should eq 0 }
its(:stdout) { should match /Service 'chef-client' is now 'stopped'/ }
end
@@ -46,7 +46,7 @@ describe service("chef-client") do
it { should_not be_running }
end
-describe command("chef-service-manager -a uninstall") do
+describe command("/opscode/chef/bin/chef-service-manager.bat -a uninstall") do
its("exit_status") { should eq 0 }
its(:stdout) { should match /Service chef-client deleted/ }
end
diff --git a/appveyor.yml b/appveyor.yml
index 7d89df1eb3..f825cd8768 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,14 +1,15 @@
version: "master-{build}"
-os: Windows Server 2012 R2
+os: Visual Studio 2017
platform:
- x64
+cache:
+- vendor/bundle
+
environment:
matrix:
- # 21-x64 is failing right now
- #- ruby_version: "21-x64"
- - ruby_version: "21"
+ - ruby_version: "25-x64"
clone_folder: c:\projects\chef
clone_depth: 1
@@ -16,24 +17,28 @@ skip_tags: true
branches:
only:
- master
+ - chef-13
+ - chef-12
install:
- systeminfo
- winrm quickconfig -q
+ - regedit /s c:\projects\chef\appveyor_registry.reg
- SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
- echo %PATH%
+ - appveyor DownloadFile http://curl.haxx.se/ca/cacert.pem -FileName C:\cacert.pem
+ - set SSL_CERT_FILE=C:\cacert.pem
+ - SET BUNDLE_WITHOUT=server:docgen:maintenance:pry:travis:integration:ci
+ - bundle config --local path vendor/bundle # use the cache we define above
+ - bundle install || bundle install || bundle install
+
+build: off
+
+before_test:
- ruby --version
- - gem update --system || gem update --system || gem update --system
- - gem install bundler --quiet --no-ri --no-rdoc || gem install bundler --quiet --no-ri --no-rdoc || gem install bundler --quiet --no-ri --no-rdoc
- - update_rubygems
- gem --version
- bundler --version
- - SET BUNDLE_IGNORE_CONFIG=true
- - SET BUNDLE_FROZEN=1
- - SET BUNDLE_WITHOUT=development:guard:maintenance:tools:integration:changelog:docgen:travis:style:omnibus_package:aix:bsd:linux:mac_os_x:solaris
-
-build_script:
- - bundle install || bundle install || bundle install
+ - bundle env
test_script:
- SET SPEC_OPTS=--format progress
diff --git a/appveyor_registry.reg b/appveyor_registry.reg
new file mode 100644
index 0000000000..293e98dd07
--- /dev/null
+++ b/appveyor_registry.reg
@@ -0,0 +1,29 @@
+REGEDIT4
+
+[HKEY_CLASSES_ROOT\Chef.PowerShell]
+@="Chef.PowerShell"
+
+[HKEY_CLASSES_ROOT\Chef.PowerShell\CLSID]
+@="{9008CA83-83E4-41FF-9C07-696E2CC47B52}"
+
+[HKEY_CLASSES_ROOT\CLSID\{9008CA83-83E4-41FF-9C07-696E2CC47B52}]
+@="Chef.PowerShell"
+
+[HKEY_CLASSES_ROOT\CLSID\{9008CA83-83E4-41FF-9C07-696E2CC47B52}\InprocServer32]
+@="c:\\windows\\system32\\mscoree.dll"
+"ThreadingModel"="Both"
+"Class"="Chef.PowerShell"
+"Assembly"="Chef.PowerShell, Version=1.0.14.0, Culture=neutral, PublicKeyToken=7def9f799d039a95"
+"RuntimeVersion"="v4.0.30319"
+"CodeBase"="file:///C:/projects/chef/distro/powershell/chef/Chef.PowerShell.dll"
+
+[HKEY_CLASSES_ROOT\CLSID\{9008CA83-83E4-41FF-9C07-696E2CC47B52}\InprocServer32\1.0.0.0]
+"Class"="Chef.PowerShell"
+"Assembly"="Chef.PowerShell, Version=1.0.14.0, Culture=neutral, PublicKeyToken=7def9f799d039a95"
+"RuntimeVersion"="v4.0.30319"
+"CodeBase"="file:///C:/projects/chef/distro/powershell/chef/Chef.PowerShell.dll"
+
+[HKEY_CLASSES_ROOT\CLSID\{9008CA83-83E4-41FF-9C07-696E2CC47B52}\ProgId]
+@="Chef.PowerShell"
+
+[HKEY_CLASSES_ROOT\CLSID\{9008CA83-83E4-41FF-9C07-696E2CC47B52}\Implemented Categories\{62C8FE65-4EBB-45E7-B440-6E39B2CDBF29}]
diff --git a/lib-backcompat/chef/chef_fs/file_system/already_exists_error.rb b/bin/chef-resource-inspector
index 6d9973e8a4..6a7eac0c32 100644..100755
--- a/lib-backcompat/chef/chef_fs/file_system/already_exists_error.rb
+++ b/bin/chef-resource-inspector
@@ -1,6 +1,8 @@
+#!/usr/bin/env ruby
#
-# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# ./chef-resource-inspector - Find information about a resource
+#
+# Copyright:: Copyright (c) 2018, Chef Software, Inc
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,7 +16,11 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
-require "chef/chef_fs/file_system/exceptions"
-Chef.log_deprecation "Individual ChefFS error files are deprecated. Please require 'chef/chef_fs/file_system/exceptions' rather than 'chef/chef_fs/file_system/#{File.basename(__FILE__, ".rb")}'."
+Encoding.default_external = Encoding::UTF_8
+
+$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
+
+require "chef/resource_inspector"
+
+ResourceInspector.start
diff --git a/bin/chef-shell b/bin/chef-shell
index 4d9300ebb7..9b10aa134b 100755
--- a/bin/chef-shell
+++ b/bin/chef-shell
@@ -23,6 +23,8 @@ begin
rescue LoadError
end
+Encoding.default_external = Encoding::UTF_8
+
require "irb"
require "irb/completion"
require "irb/ext/save-history"
diff --git a/chef-config/.gitignore b/chef-config/.gitignore
deleted file mode 100644
index 0cb6eeb067..0000000000
--- a/chef-config/.gitignore
+++ /dev/null
@@ -1,9 +0,0 @@
-/.bundle/
-/.yardoc
-/Gemfile.lock
-/_yardoc/
-/coverage/
-/doc/
-/pkg/
-/spec/reports/
-/tmp/
diff --git a/chef-config/.travis.yml b/chef-config/.travis.yml
deleted file mode 100644
index 927580f35f..0000000000
--- a/chef-config/.travis.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-language: ruby
-
-sudo: false
-# Early warning system to catch if Rubygems breaks something
-before_install:
- gem update --system
-
-branches:
- only:
- - master
-
-matrix:
- include:
- - rvm: 2.0
- - rvm: 2.1
-
-notifications:
- on_change: true
- on_failure: true
- on_success: change
- on_pull_requests: false
- irc:
- channels:
- - chat.freenode.net#chef-hacking
- hipchat:
- rooms:
- # Build Statuses
- - secure: G8MNo94L8bmWWwkH2/ViB2QaZnZHZscYM/mEjDbOGd15sqrruwckeARyBoUcRI7P1C6AFmS4IKCNVXa6KzX4Pbh51gQWM92zRpRTZpplwtXz53/1l8ajLFLLMLvEMTlBFAANUKEUFAQPY4dMa14V3Qc5oijfIncN61k4nZNTKpY=
- - rvm: 2.2
- # Open Source
- - secure: hmcex4PpG5dn8WvjndONO4xCUKOC5kPU/bUEGRrfVbe2YKJE7t0XXbNDC96W/xBgzgnJzvf1Er0zJKDrNf4qEDEWFoozdN00WLcqREgaLLS3Seto2FjR/BpBk5q+sCV0rwwEMms2P4Qk+VSnDCnm9EaeM55hOabqNuOrRzoZLBQ=
diff --git a/chef-config/README.md b/chef-config/README.md
deleted file mode 100644
index c36527282e..0000000000
--- a/chef-config/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# ChefConfig
-
-This repo is experimental. Use at your own risk.
-
diff --git a/chef-config/Rakefile b/chef-config/Rakefile
index 151c2754c3..fd6497a287 100644
--- a/chef-config/Rakefile
+++ b/chef-config/Rakefile
@@ -1,13 +1,17 @@
-require "rspec/core/rake_task"
require "chef-config/package_task"
-ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "ChefConfig") do |package|
+ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "ChefConfig", "chef-config") do |package|
package.module_path = "chef-config"
end
task :default => :spec
-desc "Run standard specs"
-RSpec::Core::RakeTask.new(:spec) do |t|
- t.pattern = FileList["spec/**/*_spec.rb"]
+begin
+ require "rspec/core/rake_task"
+ desc "Run standard specs"
+ RSpec::Core::RakeTask.new(:spec) do |t|
+ t.pattern = FileList["spec/**/*_spec.rb"]
+ end
+rescue LoadError
+ STDERR.puts "\n*** RSpec not available. (sudo) gem install rspec to run unit tests. ***\n\n"
end
diff --git a/chef-config/chef-config.gemspec b/chef-config/chef-config.gemspec
index 307112126e..532b1699d7 100644
--- a/chef-config/chef-config.gemspec
+++ b/chef-config/chef-config.gemspec
@@ -17,7 +17,9 @@ Gem::Specification.new do |spec|
spec.add_dependency "mixlib-shellout", "~> 2.0"
spec.add_dependency "mixlib-config", "~> 2.0"
- spec.add_dependency "fuzzyurl", "~> 0.8.0"
+ spec.add_dependency "fuzzyurl"
+ spec.add_dependency "addressable"
+ spec.add_dependency "tomlrb", "~> 1.2"
spec.add_development_dependency "rake", "~> 10.0"
@@ -25,7 +27,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency(rspec, "~> 3.2")
end
- spec.files = %w{Rakefile LICENSE README.md} + Dir.glob("*.gemspec") +
+ spec.files = %w{Rakefile LICENSE} + Dir.glob("*.gemspec") +
Dir.glob("{lib,spec}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }
spec.bindir = "bin"
diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb
index e6192c22cb..ef792b2db7 100644
--- a/chef-config/lib/chef-config/config.rb
+++ b/chef-config/lib/chef-config/config.rb
@@ -4,7 +4,7 @@
# Author:: AJ Christensen (<aj@chef.io>)
# Author:: Mark Mzyk (<mmzyk@chef.io>)
# Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,6 +22,7 @@
require "mixlib/config"
require "pathname"
+require "chef-config/fips"
require "chef-config/logger"
require "chef-config/windows"
require "chef-config/path_helper"
@@ -29,7 +30,9 @@ require "chef-config/mixin/fuzzy_hostname_matcher"
require "mixlib/shellout"
require "uri"
+require "addressable/uri"
require "openssl"
+require "yaml"
module ChefConfig
@@ -42,24 +45,46 @@ module ChefConfig
#
# +filename+ is used for context in stacktraces, but doesn't need to be the name of an actual file.
def self.from_string(string, filename)
- self.instance_eval(string, filename, 1)
+ instance_eval(string, filename, 1)
end
def self.inspect
configuration.inspect
end
+ # given a *nix style config path return the platform specific path
+ # to that same config file
+ # @example client.pem path on Windows
+ # platform_specific_path("/etc/chef/client.pem") #=> "C:\\chef\\client.pem"
+ # @param path [String] The unix path to convert to a platform specific path
+ # @return [String] a platform specific path
def self.platform_specific_path(path)
path = PathHelper.cleanpath(path)
if ChefConfig.windows?
# turns \etc\chef\client.rb and \var\chef\client.rb into C:/chef/client.rb
- if env["SYSTEMDRIVE"] && path[0] == '\\' && path.split('\\')[2] == "chef"
- path = PathHelper.join(env["SYSTEMDRIVE"], path.split('\\', 3)[2])
+ # Some installations will be on different drives so use the drive that
+ # the expanded path to __FILE__ is found.
+ drive = windows_installation_drive
+ if drive && path[0] == '\\' && path.split('\\')[2] == "chef"
+ path = PathHelper.join(drive, path.split('\\', 3)[2])
end
end
path
end
+ # the drive where Chef is installed on a windows host. This is determined
+ # either by the drive containing the current file or by the SYSTEMDRIVE ENV
+ # variable
+ #
+ # @return [String] the drive letter
+ def self.windows_installation_drive
+ if ChefConfig.windows?
+ drive = File.expand_path(__FILE__).split("/", 2)[0]
+ drive = ENV["SYSTEMDRIVE"] if drive.to_s == ""
+ drive
+ end
+ end
+
def self.add_formatter(name, file_path = nil)
formatters << [name, file_path]
end
@@ -68,6 +93,25 @@ module ChefConfig
event_handlers << logger
end
+ def self.apply_extra_config_options(extra_config_options)
+ if extra_config_options
+ extra_parsed_options = extra_config_options.inject({}) do |memo, option|
+ # Sanity check value.
+ if option.empty? || !option.include?("=")
+ raise UnparsableConfigOption, "Unparsable config option #{option.inspect}"
+ end
+ # Split including whitespace if someone does truly odd like
+ # --config-option "foo = bar"
+ key, value = option.split(/\s*=\s*/, 2)
+ # Call to_sym because Chef::Config expects only symbol keys. Also
+ # runs a simple parse on the string for some common types.
+ memo[key.to_sym] = YAML.safe_load(value)
+ memo
+ end
+ merge!(extra_parsed_options)
+ end
+ end
+
# Config file to load (client.rb, knife.rb, etc. defaults set differently in knife, chef-client, etc.)
configurable(:config_file)
@@ -109,16 +153,16 @@ module ChefConfig
# that upload or download files (such as knife upload, knife role from file,
# etc.) work.
default :chef_repo_path do
- if self.configuration[:cookbook_path]
- if self.configuration[:cookbook_path].kind_of?(String)
- File.expand_path("..", self.configuration[:cookbook_path])
+ if configuration[:cookbook_path]
+ if configuration[:cookbook_path].kind_of?(String)
+ File.expand_path("..", configuration[:cookbook_path])
else
- self.configuration[:cookbook_path].map do |path|
+ configuration[:cookbook_path].map do |path|
File.expand_path("..", path)
end
end
elsif configuration[:cookbook_artifact_path]
- File.expand_path("..", self.configuration[:cookbook_artifact_path])
+ File.expand_path("..", configuration[:cookbook_artifact_path])
else
cache_path
end
@@ -131,8 +175,8 @@ module ChefConfig
until File.directory?(PathHelper.join(path, "cookbooks")) || File.directory?(PathHelper.join(path, "cookbook_artifacts"))
new_path = File.expand_path("..", path)
if new_path == path
- ChefConfig.logger.warn("No cookbooks directory found at or above current directory. Assuming #{Dir.pwd}.")
- return Dir.pwd
+ ChefConfig.logger.warn("No cookbooks directory found at or above current directory. Assuming #{cwd}.")
+ return cwd
end
path = new_path
end
@@ -172,7 +216,7 @@ module ChefConfig
# Defaults to <chef_repo_path>/cookbooks. If chef_repo_path
# is not specified, this is set to [/var/chef/cookbooks, /var/chef/site-cookbooks]).
default(:cookbook_path) do
- if self.configuration[:chef_repo_path]
+ if configuration[:chef_repo_path]
derive_path_from_chef_repo_path("cookbooks")
else
Array(derive_path_from_chef_repo_path("cookbooks")).flatten +
@@ -216,8 +260,8 @@ module ChefConfig
# Defaults to <chef_repo_path>/policies.
default(:policy_path) { derive_path_from_chef_repo_path("policies") }
- # Turn on "path sanity" by default. See also: http://wiki.opscode.com/display/chef/User+Environment+PATH+Sanity
- default :enforce_path_sanity, true
+ # Turn on "path sanity" by default.
+ default :enforce_path_sanity, false
# Formatted Chef Client output is a beta feature, disabled by default:
default :formatter, "null"
@@ -278,6 +322,7 @@ module ChefConfig
default :umask, 0022
# Valid log_levels are:
+ # * :trace
# * :debug
# * :info
# * :warn
@@ -310,6 +355,11 @@ module ChefConfig
default :http_retry_count, 5
default :http_retry_delay, 5
+ # Whether or not to send the Authorization header again on http redirects.
+ # As per the plan in https://github.com/chef/chef/pull/7006, this will be
+ # False in Chef 14, True in Chef 15, and will be removed entirely in Chef 16.
+ default :http_disable_auth_on_redirect, false
+
default :interval, nil
default :once, nil
default :json_attribs, nil
@@ -348,10 +398,7 @@ module ChefConfig
# Whether Chef Zero local mode should bind to a port. All internal requests
# will go through the socketless code path regardless, so the socket is
# only needed if other processes will connect to the local mode server.
- #
- # For compatibility this is set to true but it will be changed to false in
- # the future.
- default :listen, true
+ default :listen, false
config_context :chef_zero do
config_strict_mode true
@@ -379,10 +426,10 @@ module ChefConfig
default(:chef_server_root) do
# if the chef_server_url is a path to an organization, aka
# 'some_url.../organizations/*' then remove the '/organization/*' by default
- if self.configuration[:chef_server_url] =~ /\/organizations\/\S*$/
- self.configuration[:chef_server_url].split("/")[0..-3].join("/")
- elsif self.configuration[:chef_server_url] # default to whatever chef_server_url is
- self.configuration[:chef_server_url]
+ if configuration[:chef_server_url] =~ /\/organizations\/\S*$/
+ configuration[:chef_server_url].split("/")[0..-3].join("/")
+ elsif configuration[:chef_server_url] # default to whatever chef_server_url is
+ configuration[:chef_server_url]
else
"https://localhost:443"
end
@@ -391,11 +438,15 @@ module ChefConfig
default :rest_timeout, 300
default :yum_timeout, 900
default :yum_lock_timeout, 30
- default :solo, false
+ default :solo, false
+
+ # Are we running in old Chef Solo legacy mode?
+ default :solo_legacy_mode, false
+
default :splay, nil
default :why_run, false
default :color, false
- default :client_fork, true
+ default :client_fork, nil
default :ez, false
default :enable_reporting, true
default :enable_reporting_url_fatals, false
@@ -413,6 +464,10 @@ module ChefConfig
# most of our testing scenarios)
default :minimal_ohai, false
+ # When consuming Ohai plugins from cookbook segments, we place those plugins in this directory.
+ # Subsequent chef client runs will wipe and re-populate the directory to ensure cleanliness
+ default(:ohai_segment_plugin_path) { PathHelper.join(config_dir, "ohai", "cookbook_plugins") }
+
###
# Policyfile Settings
#
@@ -513,12 +568,23 @@ module ChefConfig
default :recipe_url, nil
# Set to true if Chef is to set OpenSSL to run in FIPS mode
- default(:fips) { ENV["CHEF_FIPS"] == "1" }
+ default(:fips) do
+ # CHEF_FIPS is used in testing to override checking for system level
+ # enablement. There are 3 possible values that this variable may have:
+ # nil - no override and the system will be checked
+ # empty - FIPS is NOT enabled
+ # a non empty value - FIPS is enabled
+ if ENV["CHEF_FIPS"] == ""
+ false
+ else
+ !ENV["CHEF_FIPS"].nil? || ChefConfig.fips?
+ end
+ end
# Initialize openssl
def self.init_openssl
if fips
- self.enable_fips_mode
+ enable_fips_mode
end
end
@@ -543,6 +609,12 @@ module ChefConfig
# If chef-zero is enabled, this defaults to nil (no authentication).
default(:client_key) { chef_zero.enabled ? nil : platform_specific_path("/etc/chef/client.pem") }
+ # A credentials file may contain a complete client key, rather than the path
+ # to one.
+ #
+ # We'll use this preferentially.
+ default :client_key_contents, nil
+
# When registering the client, should we allow the client key location to
# be a symlink? eg: /etc/chef/client.pem -> /etc/chef/prod-client.pem
# If the path of the key goes through a directory like /tmp this should
@@ -558,13 +630,10 @@ module ChefConfig
end
end
- # As of Chef 11.0, version "1" is the default encrypted data bag item
- # format. Version "2" is available which adds encrypt-then-mac protection.
- # To maintain compatibility, versions other than 1 must be opt-in.
+ # As of Chef 13.0, version "3" is the default encrypted data bag item
+ # format.
#
- # Set this to `2` if you have chef-client 11.6.0+ in your infrastructure.
- # Set this to `3` if you have chef-client 11.?.0+, ruby 2 and OpenSSL >= 1.0.1 in your infrastructure. (TODO)
- default :data_bag_encrypt_version, 1
+ default :data_bag_encrypt_version, 3
# When reading data bag items, any supported version is accepted. However,
# if all encrypted data bags have been generated with the version 2 format,
@@ -585,6 +654,7 @@ module ChefConfig
default(:validation_key) { chef_zero.enabled ? nil : platform_specific_path("/etc/chef/validation.pem") }
default :validation_client_name, "chef-validator"
+ default :validation_key_contents, nil
# When creating a new client via the validation_client account, Chef 11
# servers allow the client to generate a key pair locally and send the
# public key to the server. This is more secure and helps offload work from
@@ -596,11 +666,10 @@ module ChefConfig
# generation (server generates client keys).
default(:local_key_generation) { true }
- # Zypper package provider gpg checks. Set to true to enable package
- # gpg signature checking. This will be default in the
- # future. Setting to false disables the warnings.
- # Leaving this set to nil or false is a security hazard!
- default :zypper_check_gpg, nil
+ # Zypper package provider gpg checks. Set to false to disable package
+ # gpg signature checking globally. This will warn you that it is a
+ # bad thing to do.
+ default :zypper_check_gpg, true
# Report Handlers
default :report_handlers, []
@@ -642,12 +711,40 @@ module ChefConfig
ENV.key?("CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS")
end
+ # Whether the resource count should be updated for log resource
+ # on running chef-client
+ default :count_log_resource_updates, true
+
+ # The selected profile when using credentials.
+ default :profile, nil
+
+ default :chef_guid_path do
+ PathHelper.join(config_dir, "chef_guid")
+ end
+
+ default :chef_guid, nil
+
# knife configuration data
config_context :knife do
+ # XXX: none of these default values are applied to knife (and would create a backcompat
+ # break in knife if this bug was fixed since many of the defaults below are wrong). this appears
+ # to be the start of an attempt to be able to use config_strict_mode true? if so, this approach
+ # is fraught with peril because this namespace is used by every knife plugin in the wild and
+ # we would need to validate every cli option in every knife attribute out there and list them all here.
+ #
+ # based on the way that people may define `knife[:foobar] = "something"` for the knife-foobar
+ # gem plugin i'm pretty certain we can never turn on anything like config_string_mode since
+ # any config value may be a typo or it may be in some gem in some knife plugin we don't know about.
+ #
+ # we do still need to maintain at least one of these so that the knife config hash gets
+ # created.
+ #
+ # this whole situation is deeply unsatisfying.
default :ssh_port, nil
default :ssh_user, nil
default :ssh_attribute, nil
default :ssh_gateway, nil
+ default :ssh_gateway_identity, nil
default :bootstrap_version, nil
default :bootstrap_proxy, nil
default :bootstrap_template, nil
@@ -759,13 +856,6 @@ module ChefConfig
#
default :no_lazy_load, true
- # Default for the chef_gem compile_time attribute. Nil is the same as true but will emit
- # warnings on every use of chef_gem prompting the user to be explicit. If the user sets this to
- # true then the user will get backcompat behavior but with a single nag warning that cookbooks
- # may break with this setting in the future. The false setting is the recommended setting and
- # will become the default.
- default :chef_gem_compile_time, nil
-
# A whitelisted array of attributes you want sent over the wire when node
# data is saved.
# The default setting is nil, which collects all data. Setting to [] will not
@@ -775,6 +865,20 @@ module ChefConfig
default :normal_attribute_whitelist, nil
default :override_attribute_whitelist, nil
+ # A blacklisted array of attributes you do not want to send over the
+ # wire when node data is saved
+ # The default setting is nil, which collects all data. Setting to [] will
+ # still collect all data for save
+ default :automatic_attribute_blacklist, nil
+ default :default_attribute_blacklist, nil
+ default :normal_attribute_blacklist, nil
+ default :override_attribute_blacklist, nil
+
+ # Pull down all the rubygems versions from rubygems and cache them the first time we do a gem_package or
+ # chef_gem install. This is memory-expensive and will grow without bounds, but will reduce network
+ # round trips.
+ default :rubygems_cache_enabled, false
+
config_context :windows_service do
# Set `watchdog_timeout` to the number of seconds to wait for a chef-client run
# to finish
@@ -789,6 +893,49 @@ module ChefConfig
config_context :chefdk do
end
+ # Configuration options for Data Collector reporting. These settings allow
+ # the user to configure where to send their Data Collector data, what token
+ # to send, and whether Data Collector should report its findings in client
+ # mode vs. solo mode.
+ config_context :data_collector do
+ # Full URL to the endpoint that will receive our data. If nil, the
+ # data collector will not run.
+ # Ex: http://my-data-collector.mycompany.com/ingest
+ default(:server_url) do
+ if config_parent.solo || config_parent.local_mode
+ nil
+ else
+ File.join(config_parent.chef_server_url, "/data-collector")
+ end
+ end
+
+ # An optional pre-shared token to pass as an HTTP header (x-data-collector-token)
+ # that can be used to determine whether or not the poster of this
+ # run data should be trusted.
+ # Ex: some-uuid-here
+ default :token, nil
+
+ # The Chef mode during which Data Collector is allowed to function. This
+ # can be used to run Data Collector only when running as Chef Solo but
+ # not when using Chef Client.
+ # Options: :solo (for both Solo Legacy Mode and Client Local Mode), :client, :both
+ default :mode, :both
+
+ # When the Data Collector cannot send the "starting a run" message to
+ # the Data Collector server, the Data Collector will be disabled for that
+ # run. In some situations, such as highly-regulated environments, it
+ # may be more reasonable to prevent Chef from performing the actual run.
+ # In these situations, setting this value to true will cause the Chef
+ # run to raise an exception before starting any converge activities.
+ default :raise_on_failure, false
+
+ # A user-supplied Organization string that can be sent in payloads
+ # generated by the DataCollector when Chef is run in Solo mode. This
+ # allows users to associate their Solo nodes with faux organizations
+ # without the nodes being connected to an actual Chef Server.
+ default :organization, nil
+ end
+
configurable(:http_proxy)
configurable(:http_proxy_user)
configurable(:http_proxy_pass)
@@ -812,6 +959,13 @@ module ChefConfig
export_no_proxy(no_proxy) if no_proxy
end
+ # Character classes for Addressable
+ # See https://www.ietf.org/rfc/rfc3986.txt 3.2.1
+ # The user part may not have a : in it
+ USER = Addressable::URI::CharacterClasses::UNRESERVED + Addressable::URI::CharacterClasses::SUB_DELIMS
+ # The password part may have any valid USERINFO characters
+ PASSWORD = USER + "\\:"
+
# Builds a proxy uri and exports it to the appropriate environment variables. Examples:
# http://username:password@hostname:port
# https://username@hostname:port
@@ -826,19 +980,17 @@ module ChefConfig
path = "#{scheme}://#{path}" unless path.include?("://")
# URI.split returns the following parts:
# [scheme, userinfo, host, port, registry, path, opaque, query, fragment]
- parts = URI.split(URI.encode(path))
- # URI::Generic.build requires an integer for the port, but URI::split gives
- # returns a string for the port.
- parts[3] = parts[3].to_i if parts[3]
+ uri = Addressable::URI.encode(path, Addressable::URI)
+
if user && !user.empty?
- userinfo = URI.encode(URI.encode(user), "@:")
+ userinfo = Addressable::URI.encode_component(user, USER)
if pass
- userinfo << ":#{URI.encode(URI.encode(pass), '@:')}"
+ userinfo << ":#{Addressable::URI.encode_component(pass, PASSWORD)}"
end
- parts[1] = userinfo
+ uri.userinfo = userinfo
end
- path = URI::Generic.build(parts).to_s
+ path = uri.to_s
ENV["#{scheme}_proxy".downcase] = path unless ENV["#{scheme}_proxy".downcase]
ENV["#{scheme}_proxy".upcase] = path unless ENV["#{scheme}_proxy".upcase]
end
@@ -884,7 +1036,7 @@ module ChefConfig
# If there is no 'locale -a' then we return 'en_US.UTF-8' since that is the most commonly
# available English UTF-8 locale. However, all modern POSIXen should support 'locale -a'.
def self.guess_internal_locale
- # https://github.com/opscode/chef/issues/2181
+ # https://github.com/chef/chef/issues/2181
# Some systems have the `locale -a` command, but the result has
# invalid characters for the default encoding.
#
@@ -914,9 +1066,9 @@ module ChefConfig
end
rescue
if ChefConfig.windows?
- ChefConfig.logger.debug "Defaulting to locale en_US.UTF-8 on Windows, until it matters that we do something else."
+ ChefConfig.logger.trace "Defaulting to locale en_US.UTF-8 on Windows, until it matters that we do something else."
else
- ChefConfig.logger.debug "No usable locale -a command found, assuming you have en_US.UTF-8 installed."
+ ChefConfig.logger.trace "No usable locale -a command found, assuming you have en_US.UTF-8 installed."
end
"en_US.UTF-8"
end
@@ -932,7 +1084,8 @@ module ChefConfig
# break Chef community cookbooks and is very highly discouraged.
default :ruby_encoding, Encoding::UTF_8
- default :rubygems_url, "https://rubygems.org"
+ # can be set to a string or array of strings for URIs to set as rubygems sources
+ default :rubygems_url, "https://www.rubygems.org"
# If installed via an omnibus installer, this gives the path to the
# "embedded" directory which contains all of the software packaged with
@@ -966,6 +1119,7 @@ module ChefConfig
Digest.const_set("SHA1", OpenSSL::Digest::SHA1)
OpenSSL::Digest.send(:remove_const, "MD5") if OpenSSL::Digest.const_defined?("MD5")
OpenSSL::Digest.const_set("MD5", Digest::MD5)
+ ChefConfig.logger.debug "FIPS mode is enabled."
end
end
end
diff --git a/chef-config/lib/chef-config/exceptions.rb b/chef-config/lib/chef-config/exceptions.rb
index db10a5f364..23fd28f9c8 100644
--- a/chef-config/lib/chef-config/exceptions.rb
+++ b/chef-config/lib/chef-config/exceptions.rb
@@ -22,5 +22,6 @@ module ChefConfig
class ConfigurationError < ArgumentError; end
class InvalidPath < StandardError; end
+ class UnparsableConfigOption < StandardError; end
end
diff --git a/chef-config/lib/chef-config/fips.rb b/chef-config/lib/chef-config/fips.rb
new file mode 100644
index 0000000000..623ce87686
--- /dev/null
+++ b/chef-config/lib/chef-config/fips.rb
@@ -0,0 +1,51 @@
+#
+# Author:: Matt Wrock (<matt@mattwrock.com>)
+# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+module ChefConfig
+
+ def self.fips?
+ if ChefConfig.windows?
+ begin
+ require "win32/registry"
+ rescue LoadError
+ return false
+ end
+
+ # from http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129(v=vs.85).aspx
+ reg_type =
+ case ::RbConfig::CONFIG["target_cpu"]
+ when "i386"
+ Win32::Registry::KEY_READ | 0x100
+ when "x86_64"
+ Win32::Registry::KEY_READ | 0x200
+ else
+ Win32::Registry::KEY_READ
+ end
+ begin
+ Win32::Registry::HKEY_LOCAL_MACHINE.open('System\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy', reg_type) do |policy|
+ policy["Enabled"] != 0
+ end
+ rescue Win32::Registry::Error
+ false
+ end
+ else
+ fips_path = "/proc/sys/crypto/fips_enabled"
+ File.exist?(fips_path) && File.read(fips_path).chomp != "0"
+ end
+ end
+end
diff --git a/chef-config/lib/chef-config/logger.rb b/chef-config/lib/chef-config/logger.rb
index d239e85f43..69d7889f88 100644
--- a/chef-config/lib/chef-config/logger.rb
+++ b/chef-config/lib/chef-config/logger.rb
@@ -27,6 +27,9 @@ module ChefConfig
def add(_severity, _message = nil, _progname = nil)
end
+ def trace(_progname = nil, &block)
+ end
+
def debug(_progname = nil, &block)
end
diff --git a/chef-config/lib/chef-config/mixin/credentials.rb b/chef-config/lib/chef-config/mixin/credentials.rb
new file mode 100644
index 0000000000..4c0192fff8
--- /dev/null
+++ b/chef-config/lib/chef-config/mixin/credentials.rb
@@ -0,0 +1,57 @@
+#
+# Copyright:: Copyright 2017, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "tomlrb"
+require "chef-config/path_helper"
+
+module ChefConfig
+ module Mixin
+ module Credentials
+
+ def load_credentials(profile = nil)
+ credentials_file = PathHelper.home(".chef", "credentials").freeze
+ context_file = PathHelper.home(".chef", "context").freeze
+
+ return unless File.file?(credentials_file)
+
+ context = File.read(context_file) if File.file?(context_file)
+
+ environment = ENV.fetch("CHEF_PROFILE", nil)
+
+ profile = if !profile.nil?
+ profile
+ elsif !environment.nil?
+ environment
+ elsif !context.nil?
+ context
+ else
+ "default"
+ end
+
+ config = Tomlrb.load_file(credentials_file)
+ apply_credentials(config[profile], profile)
+ rescue ChefConfig::ConfigurationError
+ raise
+ rescue => e
+ # TOML's error messages are mostly rubbish, so we'll just give a generic one
+ message = "Unable to parse Credentials file: #{credentials_file}\n"
+ message << e.message
+ raise ChefConfig::ConfigurationError, message
+ end
+ end
+ end
+end
diff --git a/chef-config/lib/chef-config/mixin/fuzzy_hostname_matcher.rb b/chef-config/lib/chef-config/mixin/fuzzy_hostname_matcher.rb
index c4d9185d81..ca1bdb4c0b 100644
--- a/chef-config/lib/chef-config/mixin/fuzzy_hostname_matcher.rb
+++ b/chef-config/lib/chef-config/mixin/fuzzy_hostname_matcher.rb
@@ -21,9 +21,11 @@ module ChefConfig
module FuzzyHostnameMatcher
def fuzzy_hostname_match_any?(hostname, matches)
- return matches.to_s.split(/\s*,\s*/).compact.any? {
- |m| fuzzy_hostname_match?(hostname, m)
- } if (hostname != nil) && (matches != nil)
+ if (!hostname.nil?) && (!matches.nil?)
+ return matches.to_s.split(/\s*,\s*/).compact.any? do |m|
+ fuzzy_hostname_match?(hostname, m)
+ end
+ end
false
end
diff --git a/chef-config/lib/chef-config/package_task.rb b/chef-config/lib/chef-config/package_task.rb
index b984f60f9f..3ab273532d 100644
--- a/chef-config/lib/chef-config/package_task.rb
+++ b/chef-config/lib/chef-config/package_task.rb
@@ -31,6 +31,10 @@ module ChefConfig
# the top level module which contains VERSION and MODULE_ROOT.
attr_accessor :module_name
+ # Name of the gem being built. This is used to find the lines to fix in
+ # Gemfile.lock.
+ attr_accessor :gem_name
+
# Should the generated version.rb be in a class or module? Default is false (module).
attr_accessor :generate_version_class
@@ -55,15 +59,19 @@ module ChefConfig
# Name of git remote used to push tags during a release. Default is origin.
attr_accessor :git_remote
- def initialize(root_path = nil, module_name = nil)
- init(root_path, module_name)
+ # True if should use Chef::VersionString.
+ attr_accessor :use_versionstring
+
+ def initialize(root_path = nil, module_name = nil, gem_name = nil)
+ init(root_path, module_name, gem_name)
yield self if block_given?
define unless root_path.nil? || module_name.nil?
end
- def init(root_path, module_name)
+ def init(root_path, module_name, gem_name)
@root_path = root_path
@module_name = module_name
+ @gem_name = gem_name
@component_paths = []
@module_path = nil
@package_dir = "pkg"
@@ -87,6 +95,10 @@ module ChefConfig
File.join(chef_root_path, "VERSION")
end
+ def gemfile_lock_path
+ File.join(root_path, "Gemfile.lock")
+ end
+
def version
IO.read(version_file_path).strip
end
@@ -153,8 +165,42 @@ module ChefConfig
end
namespace :version do
- desc 'Regenerate lib/#{@module_path}/version.rb from VERSION file'
+ desc "Regenerate lib/#{module_path}/version.rb from VERSION file"
task :update => :update_components_versions do
+ update_version_rb
+ update_gemfile_lock
+ end
+
+ task :bump => %w{version:bump_patch version:update}
+
+ task :show do
+ puts version
+ end
+
+ # Add 1 to the current patch version in the VERSION file, and write it back out.
+ task :bump_patch do
+ current_version = version
+ new_version = current_version.sub(/^(\d+\.\d+\.)(\d+)/) { "#{$1}#{$2.to_i + 1}" }
+ puts "Updating version in #{version_rb_path} from #{current_version.chomp} to #{new_version.chomp}"
+ IO.write(version_file_path, new_version)
+ end
+
+ task :bump_minor do
+ current_version = version
+ new_version = current_version.sub(/^(\d+)\.(\d+)\.(\d+)/) { "#{$1}.#{$2.to_i + 1}.0" }
+ puts "Updating version in #{version_rb_path} from #{current_version.chomp} to #{new_version.chomp}"
+ IO.write(version_file_path, new_version)
+ end
+
+ task :bump_major do
+ current_version = version
+ new_version = current_version.sub(/^(\d+)\.(\d+\.\d+)/) { "#{$1.to_i + 1}.0.0" }
+ puts "Updating version in #{version_rb_path} from #{current_version.chomp} to #{new_version.chomp}"
+ IO.write(version_file_path, new_version)
+ end
+
+ def update_version_rb # rubocop:disable Lint/NestedMethodDefinition
+ puts "Updating #{version_rb_path} to include version #{version} ..."
contents = <<-VERSION_RB
# Copyright:: Copyright 2010-2016, Chef Software, Inc.
# License:: Apache License, Version 2.0
@@ -176,10 +222,10 @@ module ChefConfig
# this repo. Do not edit this manually. Edit the VERSION file and run the rake
# task instead.
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
+#{"\nrequire \"chef/version_string\"\n" if use_versionstring}
#{class_or_module} #{module_name}
#{module_name.upcase}_ROOT = File.expand_path("../..", __FILE__)
- VERSION = "#{version}"
+ VERSION = #{use_versionstring ? "Chef::VersionString.new(\"#{version}\")" : "\"#{version}\""}
end
#
@@ -194,18 +240,15 @@ end
IO.write(version_rb_path, contents)
end
- task :bump => %w{version:bump_patch version:update}
-
- task :show do
- puts version
- end
-
- # Add 1 to the current patch version in the VERSION file, and write it back out.
- task :bump_patch do
- current_version = version
- new_version = current_version.sub(/^(\d+\.\d+\.)(\d+)/) { "#{$1}#{$2.to_i + 1}" }
- puts "Updating version in #{version_rb_path} from #{current_version.chomp} to #{new_version.chomp}"
- IO.write(version_file_path, new_version)
+ def update_gemfile_lock # rubocop:disable Lint/NestedMethodDefinition
+ if File.exist?(gemfile_lock_path)
+ puts "Updating #{gemfile_lock_path} to include version #{version} ..."
+ contents = IO.read(gemfile_lock_path)
+ contents.gsub!(/^\s*(chef|chef-config)\s*\((= )?\S+\)\s*$/) do |line|
+ line.gsub(/\((= )?\d+(\.\d+)+/) { "(#{$1}#{version}" }
+ end
+ IO.write(gemfile_lock_path, contents)
+ end
end
end
diff --git a/chef-config/lib/chef-config/path_helper.rb b/chef-config/lib/chef-config/path_helper.rb
index 0304694516..6341ffe4e6 100644
--- a/chef-config/lib/chef-config/path_helper.rb
+++ b/chef-config/lib/chef-config/path_helper.rb
@@ -79,7 +79,7 @@ module ChefConfig
end
if windows_max_length_exceeded?(path)
- ChefConfig.logger.debug("Path '#{path}' is longer than #{WIN_MAX_PATH}, prefixing with'\\\\?\\'")
+ ChefConfig.logger.trace("Path '#{path}' is longer than #{WIN_MAX_PATH}, prefixing with'\\\\?\\'")
path.insert(0, "\\\\?\\")
end
end
@@ -187,7 +187,7 @@ module ChefConfig
#
# See self.all_homes.
def self.home(*args)
- @@home_dir ||= self.all_homes { |p| break p }
+ @@home_dir ||= all_homes { |p| break p }
if @@home_dir
path = File.join(@@home_dir, *args)
block_given? ? (yield path) : path
@@ -235,7 +235,7 @@ module ChefConfig
paths = paths.map { |home_path| home_path.gsub(path_separator, ::File::SEPARATOR) if home_path }
# Filter out duplicate paths and paths that don't exist.
- valid_paths = paths.select { |home_path| home_path && Dir.exists?(home_path.force_encoding("utf-8")) }
+ valid_paths = paths.select { |home_path| home_path && Dir.exist?(home_path.force_encoding("utf-8")) }
valid_paths = valid_paths.uniq
# Join all optional path elements at the end.
diff --git a/chef-config/lib/chef-config/version.rb b/chef-config/lib/chef-config/version.rb
index beba185130..be3937e054 100644
--- a/chef-config/lib/chef-config/version.rb
+++ b/chef-config/lib/chef-config/version.rb
@@ -21,7 +21,7 @@
module ChefConfig
CHEFCONFIG_ROOT = File.expand_path("../..", __FILE__)
- VERSION = "12.10.27"
+ VERSION = "14.1.8"
end
#
diff --git a/chef-config/lib/chef-config/workstation_config_loader.rb b/chef-config/lib/chef-config/workstation_config_loader.rb
index babb78aeb8..99d2ab198f 100644
--- a/chef-config/lib/chef-config/workstation_config_loader.rb
+++ b/chef-config/lib/chef-config/workstation_config_loader.rb
@@ -22,24 +22,31 @@ require "chef-config/logger"
require "chef-config/path_helper"
require "chef-config/windows"
require "chef-config/mixin/dot_d"
+require "chef-config/mixin/credentials"
module ChefConfig
class WorkstationConfigLoader
include ChefConfig::Mixin::DotD
+ include ChefConfig::Mixin::Credentials
# Path to a config file requested by user, (e.g., via command line option). Can be nil
attr_accessor :explicit_config_file
+ # The name of a credentials profile. Can be nil
+ attr_accessor :profile
+ attr_reader :credentials_found
# TODO: initialize this with a logger for Chef and Knife
- def initialize(explicit_config_file, logger = nil)
+ def initialize(explicit_config_file, logger = nil, profile: nil)
@explicit_config_file = explicit_config_file
@chef_config_dir = nil
@config_location = nil
+ @profile = profile
@logger = logger || NullLogger.new
+ @credentials_found = false
end
def no_config_found?
- config_location.nil?
+ config_location.nil? && !credentials_found
end
def config_location
@@ -62,9 +69,10 @@ module ChefConfig
end
def load
+ load_credentials(profile)
# Ignore it if there's no explicit_config_file and can't find one at a
# default path.
- if !config_location.nil?
+ unless config_location.nil?
if explicit_config_file && !path_exists?(config_location)
raise ChefConfig::ConfigurationError, "Specified config file #{config_location} does not exist"
end
@@ -138,6 +146,40 @@ module ChefConfig
a
end
+ def apply_credentials(creds, profile)
+ Config.profile ||= profile
+ if creds.key?("node_name") && creds.key?("client_name")
+ raise ChefConfig::ConfigurationError, "Do not specify both node_name and client_name. You should prefer client_name."
+ end
+ Config.node_name = creds.fetch("node_name") if creds.key?("node_name")
+ Config.node_name = creds.fetch("client_name") if creds.key?("client_name")
+ Config.chef_server_url = creds.fetch("chef_server_url") if creds.key?("chef_server_url")
+ Config.validation_client_name = creds.fetch("validation_client_name") if creds.key?("validation_client_name")
+
+ Config.knife.merge!(Hash[creds.fetch("knife", {}).map { |k, v| [k.to_sym, v] }])
+
+ extract_key(creds, "validation_key", :validation_key, :validation_key_contents)
+ extract_key(creds, "validator_key", :validation_key, :validation_key_contents)
+ extract_key(creds, "client_key", :client_key, :client_key_contents)
+ @credentials_found = true
+ end
+
+ def extract_key(creds, name, config_path, config_contents)
+ return unless creds.has_key?(name)
+
+ val = creds.fetch(name)
+ if val.start_with?("-----BEGIN RSA PRIVATE KEY-----")
+ Config.send(config_contents, val)
+ else
+ abs_path = Pathname.new(val).expand_path(home_chef_dir)
+ Config.send(config_path, abs_path)
+ end
+ end
+
+ def home_chef_dir
+ @home_chef_dir ||= PathHelper.home(".chef")
+ end
+
def apply_config(config_content, config_file_path)
Config.from_string(config_content, config_file_path)
rescue SignalException
@@ -156,7 +198,7 @@ module ChefConfig
message << "#{e.class.name}: #{e.message}\n"
filtered_trace = e.backtrace.grep(/#{Regexp.escape(config_file_path)}/)
filtered_trace.each { |bt_line| message << " " << bt_line << "\n" }
- if !filtered_trace.empty?
+ unless filtered_trace.empty?
line_nr = filtered_trace.first[/#{Regexp.escape(config_file_path)}:([\d]+)/, 1]
message << highlight_config_error(config_file_path, line_nr.to_i)
end
diff --git a/chef-config/spec/unit/config_spec.rb b/chef-config/spec/unit/config_spec.rb
index 72c0981eca..71ea158840 100644
--- a/chef-config/spec/unit/config_spec.rb
+++ b/chef-config/spec/unit/config_spec.rb
@@ -68,6 +68,91 @@ RSpec.describe ChefConfig::Config do
end
end
+ describe "parsing arbitrary config from the CLI" do
+
+ def apply_config
+ described_class.apply_extra_config_options(extra_config_options)
+ end
+
+ context "when no arbitrary config is given" do
+
+ let(:extra_config_options) { nil }
+
+ it "succeeds" do
+ expect { apply_config }.to_not raise_error
+ end
+
+ end
+
+ context "when given a simple string option" do
+
+ let(:extra_config_options) { [ "node_name=bobotclown" ] }
+
+ it "applies the string option" do
+ apply_config
+ expect(described_class[:node_name]).to eq("bobotclown")
+ end
+
+ end
+
+ context "when given a blank value" do
+
+ let(:extra_config_options) { [ "http_retries=" ] }
+
+ it "sets the value to nil" do
+ # ensure the value is actually changed in the test
+ described_class[:http_retries] = 55
+ apply_config
+ expect(described_class[:http_retries]).to eq(nil)
+ end
+ end
+
+ context "when given spaces between `key = value`" do
+
+ let(:extra_config_options) { [ "node_name = bobo" ] }
+
+ it "handles the extra spaces and applies the config option" do
+ apply_config
+ expect(described_class[:node_name]).to eq("bobo")
+ end
+
+ end
+
+ context "when given an integer value" do
+
+ let(:extra_config_options) { [ "http_retries=9000" ] }
+
+ it "converts to a numeric type and applies the config option" do
+ apply_config
+ expect(described_class[:http_retries]).to eq(9000)
+ end
+
+ end
+
+ context "when given a boolean" do
+
+ let(:extra_config_options) { [ "boolean_thing=true" ] }
+
+ it "converts to a boolean type and applies the config option" do
+ apply_config
+ expect(described_class[:boolean_thing]).to eq(true)
+ end
+
+ end
+
+ context "when given input that is not in key=value form" do
+
+ let(:extra_config_options) { [ "http_retries:9000" ] }
+
+ it "raises UnparsableConfigOption" do
+ message = 'Unparsable config option "http_retries:9000"'
+ expect { apply_config }.to raise_error(ChefConfig::UnparsableConfigOption, message)
+ end
+
+ end
+
+ end
+
describe "when configuring formatters" do
# if TTY and not(force-logger)
# formatter = configured formatter or default formatter
@@ -118,16 +203,41 @@ RSpec.describe ChefConfig::Config do
before :each do
allow(ChefConfig).to receive(:windows?).and_return(is_windows)
end
-
+ describe "class method: windows_installation_drive" do
+ before do
+ allow(File).to receive(:expand_path).and_return("D:/Path/To/Executable")
+ end
+ if is_windows
+ it "should return D: on a windows system" do
+ expect(ChefConfig::Config.windows_installation_drive).to eq("D:")
+ end
+ else
+ it "should return nil on a non-windows system" do
+ expect(ChefConfig::Config.windows_installation_drive).to eq(nil)
+ end
+ end
+ end
describe "class method: platform_specific_path" do
+ before do
+ allow(ChefConfig::Config).to receive(:env).and_return({ "SYSTEMDRIVE" => "C:" })
+ end
if is_windows
- it "should return a windows path on windows systems" do
- path = "/etc/chef/cookbooks"
- allow(ChefConfig::Config).to receive(:env).and_return({ "SYSTEMDRIVE" => "C:" })
- # match on a regex that looks for the base path with an optional
- # system drive at the beginning (c:)
- # system drive is not hardcoded b/c it can change and b/c it is not present on linux systems
- expect(ChefConfig::Config.platform_specific_path(path)).to eq("C:\\chef\\cookbooks")
+ path = "/etc/chef/cookbooks"
+ context "a windows system with chef installed on C: drive" do
+ before do
+ allow(ChefConfig::Config).to receive(:windows_installation_drive).and_return("C:")
+ end
+ it "should return a windows path rooted in C:" do
+ expect(ChefConfig::Config.platform_specific_path(path)).to eq("C:\\chef\\cookbooks")
+ end
+ end
+ context "a windows system with chef installed on D: drive" do
+ before do
+ allow(ChefConfig::Config).to receive(:windows_installation_drive).and_return("D:")
+ end
+ it "should return a windows path rooted in D:" do
+ expect(ChefConfig::Config.platform_specific_path(path)).to eq("D:\\chef\\cookbooks")
+ end
end
else
it "should return given path on non-windows systems" do
@@ -165,6 +275,56 @@ RSpec.describe ChefConfig::Config do
allow(ChefConfig::Config).to receive(:path_accessible?).and_return(false)
end
+ describe "ChefConfig::Config[:fips]" do
+ let(:fips_enabled) { false }
+
+ before(:all) do
+ @original_env = ENV.to_hash
+ end
+
+ after(:all) do
+ ENV.clear
+ ENV.update(@original_env)
+ end
+
+ before(:each) do
+ ENV["CHEF_FIPS"] = nil
+ allow(ChefConfig).to receive(:fips?).and_return(fips_enabled)
+ end
+
+ it "returns false when no environment is set and not enabled on system" do
+ expect(ChefConfig::Config[:fips]).to eq(false)
+ end
+
+ context "when ENV['CHEF_FIPS'] is empty" do
+ before do
+ ENV["CHEF_FIPS"] = ""
+ end
+
+ it "returns false" do
+ expect(ChefConfig::Config[:fips]).to eq(false)
+ end
+ end
+
+ context "when ENV['CHEF_FIPS'] is set" do
+ before do
+ ENV["CHEF_FIPS"] = "1"
+ end
+
+ it "returns true" do
+ expect(ChefConfig::Config[:fips]).to eq(true)
+ end
+ end
+
+ context "when fips is enabled on system" do
+ let(:fips_enabled) { true }
+
+ it "returns true" do
+ expect(ChefConfig::Config[:fips]).to eq(true)
+ end
+ end
+ end
+
describe "ChefConfig::Config[:chef_server_root]" do
context "when chef_server_url isn't set manually" do
it "returns the default of 'https://localhost:443'" do
@@ -210,6 +370,11 @@ RSpec.describe ChefConfig::Config do
end
describe "ChefConfig::Config[:cache_path]" do
+ before do
+ if is_windows
+ allow(File).to receive(:expand_path).and_return("#{ChefConfig::Config.env["SYSTEMDRIVE"]}/Path/To/Executable")
+ end
+ end
context "when /var/chef exists and is accessible" do
it "defaults to /var/chef" do
allow(ChefConfig::Config).to receive(:path_accessible?).with(to_platform("/var/chef")).and_return(true)
@@ -602,8 +767,8 @@ RSpec.describe ChefConfig::Config do
shared_examples_for "a suitable locale" do
it "returns an English UTF-8 locale" do
expect(ChefConfig.logger).to_not receive(:warn).with(/Please install an English UTF-8 locale for Chef to use/)
- expect(ChefConfig.logger).to_not receive(:debug).with(/Defaulting to locale en_US.UTF-8 on Windows/)
- expect(ChefConfig.logger).to_not receive(:debug).with(/No usable locale -a command found/)
+ expect(ChefConfig.logger).to_not receive(:trace).with(/Defaulting to locale en_US.UTF-8 on Windows/)
+ expect(ChefConfig.logger).to_not receive(:trace).with(/No usable locale -a command found/)
expect(ChefConfig::Config.guess_internal_locale).to eq expected_locale
end
end
@@ -672,9 +837,9 @@ RSpec.describe ChefConfig::Config do
it "should default to 'en_US.UTF-8'" do
if is_windows
- expect(ChefConfig.logger).to receive(:debug).with("Defaulting to locale en_US.UTF-8 on Windows, until it matters that we do something else.")
+ expect(ChefConfig.logger).to receive(:trace).with("Defaulting to locale en_US.UTF-8 on Windows, until it matters that we do something else.")
else
- expect(ChefConfig.logger).to receive(:debug).with("No usable locale -a command found, assuming you have en_US.UTF-8 installed.")
+ expect(ChefConfig.logger).to receive(:trace).with("No usable locale -a command found, assuming you have en_US.UTF-8 installed.")
end
expect(ChefConfig::Config.guess_internal_locale).to eq "en_US.UTF-8"
end
@@ -687,9 +852,13 @@ RSpec.describe ChefConfig::Config do
before(:all) do
@original_env = ENV.to_hash
ENV["http_proxy"] = nil
+ ENV["HTTP_PROXY"] = nil
ENV["https_proxy"] = nil
+ ENV["HTTPS_PROXY"] = nil
ENV["ftp_proxy"] = nil
+ ENV["FTP_PROXY"] = nil
ENV["no_proxy"] = nil
+ ENV["NO_PROXY"] = nil
end
after(:all) do
@@ -992,4 +1161,34 @@ RSpec.describe ChefConfig::Config do
end
+ describe "data collector URL" do
+
+ context "when using default settings" do
+
+ context "for Chef Client" do
+
+ it "configures the data collector URL as a relative path to the Chef Server URL" do
+ ChefConfig::Config[:chef_server_url] = "https://chef.example/organizations/myorg"
+ expect(ChefConfig::Config[:data_collector][:server_url]).to eq("https://chef.example/organizations/myorg/data-collector")
+ end
+
+ end
+
+ context "for Chef Solo" do
+
+ before do
+ ChefConfig::Config[:solo] = true
+ end
+
+ it "sets the data collector server URL to nil" do
+ ChefConfig::Config[:chef_server_url] = "https://chef.example/organizations/myorg"
+ expect(ChefConfig::Config[:data_collector][:server_url]).to be_nil
+ end
+
+ end
+
+ end
+
+ end
+
end
diff --git a/chef-config/spec/unit/fips_spec.rb b/chef-config/spec/unit/fips_spec.rb
new file mode 100644
index 0000000000..d53096cead
--- /dev/null
+++ b/chef-config/spec/unit/fips_spec.rb
@@ -0,0 +1,128 @@
+#
+# Author:: Matt Wrock (<matt@mattwrock.com>)
+# Copyright:: Copyright (c) 2016-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef-config/fips"
+require "spec_helper"
+
+begin
+ require "win32/registry"
+rescue LoadError
+ # not on unix
+end
+
+RSpec.describe "ChefConfig.fips?" do
+ let(:enabled) { "0" }
+
+ context "on *nix" do
+ let(:fips_path) { "/proc/sys/crypto/fips_enabled" }
+
+ before(:each) do
+ allow(ChefConfig).to receive(:windows?).and_return(false)
+ allow(::File).to receive(:exist?).with(fips_path).and_return(true)
+ allow(::File).to receive(:read).with(fips_path).and_return(enabled)
+ end
+
+ context "fips file is present and contains 1" do
+ let(:enabled) { "1" }
+
+ it "returns true" do
+ expect(ChefConfig.fips?).to be(true)
+ end
+ end
+
+ context "fips file does not contain 1" do
+ let(:enabled) { "0" }
+
+ it "returns false" do
+ expect(ChefConfig.fips?).to be(false)
+ end
+ end
+
+ context "fips file is not present" do
+ before do
+ allow(::File).to receive(:exist?).with(fips_path).and_return(false)
+ end
+
+ it "returns false" do
+ expect(ChefConfig.fips?).to be(false)
+ end
+ end
+ end
+
+ context "on windows", :windows_only do
+ let(:fips_key) { 'System\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy' }
+ let(:win_reg_entry) { { "Enabled" => enabled } }
+
+ before(:each) do
+ allow(ChefConfig).to receive(:windows?).and_return(true)
+ allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).with(fips_key, arch).and_yield(win_reg_entry)
+ end
+
+ shared_examples "fips_detection" do
+ context "fips enabled key is set to 1" do
+ let(:enabled) { 1 }
+
+ it "returns true" do
+ expect(ChefConfig.fips?).to be(true)
+ end
+ end
+
+ context "fips enabled key is set to 0" do
+ let(:enabled) { 0 }
+
+ it "returns false" do
+ expect(ChefConfig.fips?).to be(false)
+ end
+ end
+
+ context "fips key does not exist" do
+ before do
+ allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).and_raise(Win32::Registry::Error, 50)
+ end
+
+ it "returns false" do
+ expect(ChefConfig.fips?).to be(false)
+ end
+ end
+ end
+
+ context "on 32 bit ruby" do
+ let(:arch) { Win32::Registry::KEY_READ | 0x100 }
+
+ before { stub_const("::RbConfig::CONFIG", { "target_cpu" => "i386" } ) }
+
+ it_behaves_like "fips_detection"
+ end
+
+ context "on 64 bit ruby" do
+ let(:arch) { Win32::Registry::KEY_READ | 0x200 }
+
+ before { stub_const("::RbConfig::CONFIG", { "target_cpu" => "x86_64" } ) }
+
+ it_behaves_like "fips_detection"
+ end
+
+ context "on unknown ruby" do
+ let(:arch) { Win32::Registry::KEY_READ }
+
+ before { stub_const("::RbConfig::CONFIG", { "target_cpu" => nil } ) }
+
+ it_behaves_like "fips_detection"
+ end
+ end
+end
diff --git a/chef-config/spec/unit/workstation_config_loader_spec.rb b/chef-config/spec/unit/workstation_config_loader_spec.rb
index 087f249724..8f1cde17e6 100644
--- a/chef-config/spec/unit/workstation_config_loader_spec.rb
+++ b/chef-config/spec/unit/workstation_config_loader_spec.rb
@@ -38,6 +38,7 @@ RSpec.describe ChefConfig::WorkstationConfigLoader do
before do
# We set this to nil so that a dev workstation will
# not interfere with the tests.
+ ChefConfig::Config.reset
ChefConfig::Config[:config_d_dir] = nil
end
@@ -363,4 +364,173 @@ RSpec.describe ChefConfig::WorkstationConfigLoader do
end
end
end
+
+ describe "when loading a credentials file" do
+ if ChefConfig.windows?
+ let(:home) { "C:/Users/example.user" }
+ else
+ let(:home) { "/Users/example.user" }
+ end
+ let(:credentials_file) { "#{home}/.chef/credentials" }
+ let(:context_file) { "#{home}/.chef/context" }
+
+ before do
+ allow(ChefConfig::PathHelper).to receive(:home).with(".chef").and_return(File.join(home, ".chef"))
+ allow(ChefConfig::PathHelper).to receive(:home).with(".chef", "credentials").and_return(credentials_file)
+ allow(ChefConfig::PathHelper).to receive(:home).with(".chef", "context").and_return(context_file)
+ allow(File).to receive(:file?).with(context_file).and_return false
+ end
+
+ context "when the file exists" do
+ before do
+ expect(File).to receive(:read).with(credentials_file, { encoding: "utf-8" }).and_return(content)
+ allow(File).to receive(:file?).with(credentials_file).and_return true
+ end
+
+ context "and has a default profile" do
+ let(:content) do
+ content = <<EOH
+[default]
+node_name = 'barney'
+client_key = "barney_rubble.pem"
+chef_server_url = "https://api.chef.io/organizations/bedrock"
+EOH
+ content
+ end
+
+ it "applies the expected config" do
+ expect { config_loader.load_credentials }.not_to raise_error
+ expect(ChefConfig::Config.chef_server_url).to eq("https://api.chef.io/organizations/bedrock")
+ expect(ChefConfig::Config.client_key.to_s).to eq("#{home}/.chef/barney_rubble.pem")
+ expect(ChefConfig::Config.profile.to_s).to eq("default")
+ end
+ end
+
+ context "and has a default profile with knife settings" do
+ let(:content) do
+ content = <<EOH
+[default]
+node_name = 'barney'
+client_key = "barney_rubble.pem"
+chef_server_url = "https://api.chef.io/organizations/bedrock"
+knife = {
+ secret_file = "/home/barney/.chef/encrypted_data_bag_secret.pem"
+}
+[default.knife]
+ssh_user = "knife_ssh_user"
+EOH
+ content
+ end
+
+ it "applies the expected knife config" do
+ expect { config_loader.load_credentials }.not_to raise_error
+ expect(ChefConfig::Config.chef_server_url).to eq("https://api.chef.io/organizations/bedrock")
+ expect(ChefConfig::Config.client_key.to_s).to eq("#{home}/.chef/barney_rubble.pem")
+ expect(ChefConfig::Config.knife[:ssh_user].to_s).to eq("knife_ssh_user")
+ expect(ChefConfig::Config.knife[:secret_file].to_s).to eq("/home/barney/.chef/encrypted_data_bag_secret.pem")
+ expect(ChefConfig::Config.profile.to_s).to eq("default")
+ end
+ end
+
+ context "and has a profile containing a full key" do
+ let(:content) do
+ content = <<EOH
+[default]
+client_key = """
+-----BEGIN RSA PRIVATE KEY-----
+foo
+"""
+EOH
+ content
+ end
+
+ it "applies the expected config" do
+ expect { config_loader.load_credentials }.not_to raise_error
+ expect(ChefConfig::Config.client_key_contents).to eq(<<EOH
+-----BEGIN RSA PRIVATE KEY-----
+foo
+EOH
+)
+ end
+ end
+
+ context "and has several profiles" do
+ let(:content) do
+ content = <<EOH
+[default]
+client_name = "default"
+[environment]
+client_name = "environment"
+[explicit]
+client_name = "explicit"
+[context]
+client_name = "context"
+EOH
+ content
+ end
+
+ let(:env) { {} }
+ before do
+ stub_const("ENV", env)
+ end
+
+ it "selects the correct profile explicitly" do
+ expect { config_loader.load_credentials("explicit") }.not_to raise_error
+ expect(ChefConfig::Config.node_name).to eq("explicit")
+ end
+
+ context "with an environment variable" do
+ let(:env) { { "CHEF_PROFILE" => "environment" } }
+
+ it "selects the correct profile" do
+ expect { config_loader.load_credentials }.not_to raise_error
+ expect(ChefConfig::Config.node_name).to eq("environment")
+ end
+ end
+
+ it "selects the correct profile with a context file" do
+ allow(File).to receive(:file?).with(context_file).and_return true
+ expect(File).to receive(:read).with(context_file).and_return "context"
+ expect { config_loader.load_credentials }.not_to raise_error
+ expect(ChefConfig::Config.node_name).to eq("context")
+ end
+
+ it "falls back to the default" do
+ expect { config_loader.load_credentials }.not_to raise_error
+ expect(ChefConfig::Config.node_name).to eq("default")
+ end
+ end
+
+ context "and contains both node_name and client_name" do
+ let(:content) do
+ content = <<EOH
+[default]
+node_name = 'barney'
+client_name = 'barney'
+EOH
+ content
+ end
+
+ it "raises a ConfigurationError" do
+ expect { config_loader.load_credentials }.to raise_error(ChefConfig::ConfigurationError)
+ end
+ end
+
+ context "and has a syntax error" do
+ let(:content) { "<<<<<" }
+
+ it "raises a ConfigurationError" do
+ expect { config_loader.load_credentials }.to raise_error(ChefConfig::ConfigurationError)
+ end
+ end
+ end
+
+ context "when the file does not exist" do
+ it "does not load anything" do
+ allow(File).to receive(:file?).with(credentials_file).and_return false
+ expect(Tomlrb).not_to receive(:load_file)
+ config_loader.load_credentials
+ end
+ end
+ end
end
diff --git a/chef-universal-mingw32.gemspec b/chef-universal-mingw32.gemspec
index a4f086cfaf..a4949db712 100644
--- a/chef-universal-mingw32.gemspec
+++ b/chef-universal-mingw32.gemspec
@@ -2,13 +2,11 @@ gemspec = eval(IO.read(File.expand_path("../chef.gemspec", __FILE__)))
gemspec.platform = Gem::Platform.new(%w{universal mingw32})
-gemspec.add_dependency "ffi", "~> 1.9"
gemspec.add_dependency "win32-api", "~> 1.5.3"
gemspec.add_dependency "win32-dir", "~> 0.5.0"
gemspec.add_dependency "win32-event", "~> 0.6.1"
-# Hard pin on win32-eventlog until djberg96/win32-eventlog#20 is resolved.
-# win32-eventlog was stomping over the CreateEvent FFI bindings that
-# win32-service needed, causing the chef service to not run.
+# TODO: Relax this pin and make the necessary updaets. The issue originally
+# leading to this pin has been fixed in 0.6.5.
gemspec.add_dependency "win32-eventlog", "0.6.3"
gemspec.add_dependency "win32-mmap", "~> 0.4.1"
gemspec.add_dependency "win32-mutex", "~> 0.4.2"
@@ -16,6 +14,7 @@ gemspec.add_dependency "win32-process", "~> 0.8.2"
gemspec.add_dependency "win32-service", "~> 0.8.7"
gemspec.add_dependency "windows-api", "~> 0.4.4"
gemspec.add_dependency "wmi-lite", "~> 1.0"
+gemspec.add_dependency "win32-taskscheduler", "~> 0.4.0"
gemspec.extensions << "ext/win32-eventlog/Rakefile"
gemspec.files += %w{ext/win32-eventlog/Rakefile ext/win32-eventlog/chef-log.man}
diff --git a/chef.gemspec b/chef.gemspec
index b88c899d5c..63e4192652 100644
--- a/chef.gemspec
+++ b/chef.gemspec
@@ -11,33 +11,38 @@ Gem::Specification.new do |s|
s.license = "Apache-2.0"
s.author = "Adam Jacob"
s.email = "adam@chef.io"
- s.homepage = "http://www.chef.io"
+ s.homepage = "https://www.chef.io"
- s.required_ruby_version = ">= 2.1.0"
+ s.required_ruby_version = ">= 2.4.0"
s.add_dependency "chef-config", "= #{Chef::VERSION}"
- s.add_dependency "mixlib-cli", "~> 1.4"
- s.add_dependency "mixlib-log", "~> 1.3"
- s.add_dependency "mixlib-authentication", "~> 1.4"
+ s.add_dependency "mixlib-cli", "~> 1.7"
+ s.add_dependency "mixlib-log", "~> 2.0", ">= 2.0.3"
+ s.add_dependency "mixlib-authentication", "~> 2.0"
s.add_dependency "mixlib-shellout", "~> 2.0"
- s.add_dependency "ohai", ">= 8.6.0.alpha.1", "< 9"
+ s.add_dependency "mixlib-archive", "~> 0.4"
+ s.add_dependency "ohai", "~> 14.0"
+ # ffi 1.9.22+1.9.23 are buggy in our spec/unit/application/client_spec.rb tests on el6/el7
+ s.add_dependency "ffi", "< 1.9.22"
s.add_dependency "ffi-yajl", "~> 2.2"
- s.add_dependency "net-ssh", ">= 2.9", "< 4.0"
- s.add_dependency "net-ssh-multi", "~> 1.1"
+ s.add_dependency "net-ssh", "~> 4.2"
+ s.add_dependency "net-ssh-multi", "~> 1.2", ">= 1.2.1"
s.add_dependency "net-sftp", "~> 2.1", ">= 2.1.2"
s.add_dependency "highline", "~> 1.6", ">= 1.6.9"
s.add_dependency "erubis", "~> 2.7"
s.add_dependency "diff-lcs", "~> 1.2", ">= 1.2.4"
- s.add_dependency "chef-zero", "~> 4.5"
+ s.add_dependency "chef-zero", ">= 13.0"
s.add_dependency "plist", "~> 3.2"
s.add_dependency "iniparse", "~> 1.4"
+ s.add_dependency "addressable"
+ s.add_dependency "iso8601", "~> 0.9.1"
# Audit mode requires these, so they are non-developmental dependencies now
- %w{rspec-core rspec-expectations rspec-mocks}.each { |gem| s.add_dependency gem, "~> 3.4" }
+ %w{rspec-core rspec-expectations rspec-mocks}.each { |gem| s.add_dependency gem, "~> 3.5" }
s.add_dependency "rspec_junit_formatter", "~> 0.2.0"
s.add_dependency "serverspec", "~> 2.7"
s.add_dependency "specinfra", "~> 2.10"
@@ -52,8 +57,8 @@ Gem::Specification.new do |s|
s.add_dependency "bundler", ">= 1.10"
s.bindir = "bin"
- s.executables = %w{ chef-client chef-solo knife chef-shell chef-apply }
+ s.executables = %w{ chef-client chef-solo knife chef-shell chef-apply chef-resource-inspector }
- s.require_paths = %w{ lib lib-backcompat }
+ s.require_paths = %w{ lib }
s.files = %w{Gemfile Rakefile LICENSE README.md CONTRIBUTING.md VERSION} + Dir.glob("{distro,lib,lib-backcompat,tasks,acceptance,spec}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) } + Dir.glob("*.gemspec")
end
diff --git a/ci/dependency_update.sh b/ci/dependency_update.sh
new file mode 100755
index 0000000000..100e2513ec
--- /dev/null
+++ b/ci/dependency_update.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+# FIXME: this seems uselessly trivial, replace with a rake task and have ci call the rake task?
+
+set -evx
+
+bundle install --without omnibus_package test pry integration docgen maintenance travis aix bsd linux mac_os_x solaris windows development
+
+bundle exec rake dependencies_ci
diff --git a/ci/verify-chef.bat b/ci/verify-chef.bat
index 7ba0817938..7160b7b2f0 100755
--- a/ci/verify-chef.bat
+++ b/ci/verify-chef.bat
@@ -18,6 +18,20 @@ SET TMP=%TMP%\cheftest
RMDIR /S /Q %TEMP%
MKDIR %TEMP%
+REM ; FIXME: we should really use Bundler.with_clean_env in the caller instead of re-inventing it here
+set _ORIGINAL_GEM_PATH=
+set BUNDLE_BIN_PATH=
+set BUNDLE_GEMFILE=
+set GEM_HOME=
+set GEM_PATH=
+set GEM_ROOT=
+set RUBYLIB=
+set RUBYOPT=
+set RUBY_ENGINE=
+set RUBY_ROOT=
+set RUBY_VERSION=
+set BUNDLER_VERSION=
+
FOR %%b IN (
chef-client
knife
@@ -46,8 +60,15 @@ call %EMBEDDED_BIN_DIR%\rspec --version
SET PATH=C:\opscode\%PROJECT_NAME%\bin;C:\opscode\%PROJECT_NAME%\embedded\bin;%PATH%
-REM ; Test against the vendored chef gem (cd into the output of "bundle show chef")
-for /f "delims=" %%a in ('bundle show chef') do cd %%a
+REM ; Test against the vendored chef gem (cd into the output of "gem which chef")
+for /f "delims=" %%a in ('gem which chef') do set CHEFDIR=%%a
+call :dirname "%CHEFDIR%" CHEFDIR
+call :dirname "%CHEFDIR%" CHEFDIR
+cd %CHEFDIR%
+
+cd
+
+type Gemfile.lock
IF NOT EXIST "Gemfile.lock" (
ECHO "Chef gem does not contain a Gemfile.lock! This is needed to run any tests."
@@ -61,7 +82,19 @@ IF "%PIPELINE_NAME%" == "chef-fips" (
REM ; ffi-yajl must run in c-extension mode for perf, so force it so we don't accidentally fall back to ffi
set FORCE_FFI_YAJL=ext
-set BUNDLE_GEMFILE=C:\opscode\%PROJECT_NAME%\Gemfile
-set BUNDLE_IGNORE_CONFIG=true
-set BUNDLE_FROZEN=1
-call bundle exec rspec -r rspec_junit_formatter -f RspecJunitFormatter -o %WORKSPACE%\test.xml -f documentation spec/functional
+set
+REM ; we should use bundler, but aren't because of a bug
+REM ; this should be `bundle exec rspec yadda yadda...`
+REM ; the bug is here: https://github.com/bundler/bundler/issues/5644
+REM ; while this opens us up to some testing edge conditions if we're doing our job right to build the omnibus package and its gems correct that shouldn't matter
+REM ; since that assumption has gone wrong before we should really go back to using bundler at some point
+call %EMBEDDED_BIN_DIR%\rspec -r rspec_junit_formatter -f RspecJunitFormatter -o %WORKSPACE%\test.xml -f documentation spec/functional
+
+GOTO :EOF
+
+:dirname file varName
+ setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
+ SET _dir=%~dp1
+ SET _dir=%_dir:~0,-1%
+ endlocal & set %2=%_dir%
+GOTO thout:EOF
diff --git a/ci/verify-chef.sh b/ci/verify-chef.sh
index 6c6c4606de..3a9bcf2111 100755
--- a/ci/verify-chef.sh
+++ b/ci/verify-chef.sh
@@ -5,9 +5,12 @@ set -evx
# Set up a custom tmpdir, and clean it up before and after the tests
TMPDIR="${TMPDIR:-/tmp}/cheftest"
export TMPDIR
-rm -rf $TMPDIR
+sudo rm -rf $TMPDIR
mkdir -p $TMPDIR
+# Verify that we kill any orphaned test processes. Kill any orphaned rspec processes.
+ps ax | egrep 'rspec' | grep -v grep | awk '{ print $1 }' | xargs sudo kill -s KILL || true
+
# $PROJECT_NAME is set by Jenkins, this allows us to use the same script to verify
# Chef and Angry Chef
PATH=/opt/$PROJECT_NAME/bin:$PATH
@@ -64,7 +67,8 @@ for ruby_env_var in _ORIGINAL_GEM_PATH \
RUBYOPT \
RUBY_ENGINE \
RUBY_ROOT \
- RUBY_VERSION
+ RUBY_VERSION \
+ BUNDLER_VERSION
do
unset $ruby_env_var
@@ -83,39 +87,36 @@ $EMBEDDED_BIN_DIR/rspec --version
FORCE_FFI_YAJL=ext
export FORCE_FFI_YAJL
+OLD_PATH=$PATH
+PATH=/opt/$PROJECT_NAME/bin:/opt/$PROJECT_NAME/embedded/bin:$PATH
+
+gem_list=`gem which chef`
+lib_dir=`dirname $gem_list`
+CHEF_GEM=`dirname $lib_dir`
+
# ACCEPTANCE environment variable will be set on acceptance testers.
# If is it set; we run the acceptance tests, otherwise run rspec tests.
if [ "x$ACCEPTANCE" != "x" ]; then
# Find the Chef gem and cd there.
- OLD_PATH=$PATH
- PATH=/opt/$PROJECT_NAME/bin:/opt/$PROJECT_NAME/embedded/bin:$PATH
- cd /opt/$PROJECT_NAME
- CHEF_GEM=`bundle show chef`
PATH=$OLD_PATH
- cd $CHEF_GEM/acceptance
# On acceptance testers we have Chef DK. We will use its Ruby environment
# to cut down the gem installation time.
PATH=/opt/chefdk/bin:/opt/chefdk/embedded/bin:$PATH
export PATH
- # Test against the Chef bundle
- sudo env PATH=$PATH AWS_SSH_KEY_ID=$AWS_SSH_KEY_ID ARTIFACTORY_USERNAME=$ARTIFACTORY_USERNAME ARTIFACTORY_PASSWORD=$ARTIFACTORY_PASSWORD pwd
- sudo env PATH=$PATH AWS_SSH_KEY_ID=$AWS_SSH_KEY_ID ARTIFACTORY_USERNAME=$ARTIFACTORY_USERNAME ARTIFACTORY_PASSWORD=$ARTIFACTORY_PASSWORD bundle config
- sudo env PATH=$PATH AWS_SSH_KEY_ID=$AWS_SSH_KEY_ID ARTIFACTORY_USERNAME=$ARTIFACTORY_USERNAME ARTIFACTORY_PASSWORD=$ARTIFACTORY_PASSWORD bundle install
- sudo env PATH=$PATH AWS_SSH_KEY_ID=$AWS_SSH_KEY_ID ARTIFACTORY_USERNAME=$ARTIFACTORY_USERNAME ARTIFACTORY_PASSWORD=$ARTIFACTORY_PASSWORD KITCHEN_DRIVER=ec2 bundle exec chef-acceptance test --force-destroy
-else
- PATH=/opt/$PROJECT_NAME/bin:/opt/$PROJECT_NAME/embedded/bin:$PATH
- export PATH
+ # copy acceptance suites into workspace
+ SUITE_PATH=$WORKSPACE/acceptance
+ mkdir -p $SUITE_PATH
+ cp -R $CHEF_GEM/acceptance/. $SUITE_PATH
+ sudo chown -R $USER:$USER $SUITE_PATH
+
+ cd $SUITE_PATH
- # Test against the installed Chef gem
- cd /opt/$PROJECT_NAME
- CHEF_GEM=`bundle show chef`
+ env PATH=$PATH AWS_SSH_KEY_ID=$AWS_SSH_KEY_ID bundle install --deployment
+ env PATH=$PATH AWS_SSH_KEY_ID=$AWS_SSH_KEY_ID KITCHEN_DRIVER=ec2 KITCHEN_CHEF_CHANNEL=unstable bundle exec chef-acceptance test --force-destroy --data-path $WORKSPACE/chef-acceptance-data
+else
cd $CHEF_GEM
- if [ ! -f "Gemfile.lock" ]; then
- echo "Chef gem does not contain a Gemfile.lock! This is needed to run any tests."
- exit 1
- fi
- sudo env BUNDLE_GEMFILE=/opt/$PROJECT_NAME/Gemfile BUNDLE_IGNORE_CONFIG=true BUNDLE_FROZEN=1 PATH=$PATH TERM=xterm bundle exec rspec -r rspec_junit_formatter -f RspecJunitFormatter -o $WORKSPACE/test.xml -f documentation spec/functional
+ sudo bundle exec rspec -r rspec_junit_formatter -f RspecJunitFormatter -o $WORKSPACE/test.xml -f documentation spec/functional
fi
diff --git a/ci/version_bump.sh b/ci/version_bump.sh
new file mode 100755
index 0000000000..f0a635843f
--- /dev/null
+++ b/ci/version_bump.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# FIXME: this seems uselessly trivial, replace with a rake task and have ci call the rake task?
+
+set -evx
+
+export LANG=en_US.UTF-8
+
+bundle install --without omnibus_package test pry integration docgen maintenance travis aix bsd linux mac_os_x solaris windows development
+
+bundle exec rake ci_version_bump
diff --git a/ci/version_show.sh b/ci/version_show.sh
new file mode 100755
index 0000000000..5348f6f090
--- /dev/null
+++ b/ci/version_show.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+cat VERSION
diff --git a/distro/common/html/_sources/ctl_chef_client.txt b/distro/common/html/_sources/ctl_chef_client.txt
deleted file mode 100644
index f0af14e090..0000000000
--- a/distro/common/html/_sources/ctl_chef_client.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-=====================================================
-chef-client
-=====================================================
-
-.. include:: ../../includes_chef_client/includes_chef_client.rst
-
-.. include:: ../../includes_ctl_chef_client/includes_ctl_chef_client.rst
-
-.. note:: .. include:: ../../includes_config/includes_config_rb_client.rst
-
-Options
-=====================================================
-.. include:: ../../release_chef_12-0/includes_ctl_chef_client_options.rst
-
-Run with Elevated Privileges
-=====================================================
-.. include:: ../../includes_ctl_chef_client/includes_ctl_chef_client_elevated_privileges.rst
-
-Linux
------------------------------------------------------
-.. include:: ../../includes_ctl_chef_client/includes_ctl_chef_client_elevated_privileges_linux.rst
-
-Windows
------------------------------------------------------
-.. include:: ../../includes_ctl_chef_client/includes_ctl_chef_client_elevated_privileges_windows.rst
-
-Examples
-=====================================================
-
-**Start a Chef run when the chef-client is running as a daemon**
-
-.. include:: ../../step_ctl_chef_client/step_ctl_chef_client_start_chef_run_daemon.rst
-
-**Start a Chef run manually**
-
-.. include:: ../../step_ctl_chef_client/step_ctl_chef_client_start_chef_run_manual.rst \ No newline at end of file
diff --git a/distro/common/html/_sources/ctl_chef_server.txt b/distro/common/html/_sources/ctl_chef_server.txt
deleted file mode 100644
index a7f6ce81e7..0000000000
--- a/distro/common/html/_sources/ctl_chef_server.txt
+++ /dev/null
@@ -1,341 +0,0 @@
-=====================================================
-|chef server ctl| (executable)
-=====================================================
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server.rst
-
-backup-recover
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_backup_recover.rst
-
-cleanse
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_cleanse.rst
-
-gather-logs
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_gather_logs.rst
-
-ha-status
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_ha_status.rst
-
-help
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_help.rst
-
-install
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_install.rst
-
-**Syntax**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_install_syntax.rst
-
-**Options**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_install_options.rst
-
-Use Downloads
------------------------------------------------------
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_install_features_download.rst
-
-Use Local Packages
------------------------------------------------------
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_install_features_manual.rst
-
-master-recover
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_master_recover.rst
-
-org-create
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_org_create.rst
-
-**Syntax**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_org_create_syntax.rst
-
-**Options**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_org_create_options.rst
-
-**Examples**
-
-.. code-block:: bash
-
- $ chef-server-ctl org-create prod Production
-
-
-.. code-block:: bash
-
- $ chef-server-ctl org-create staging Staging -a chef-admin
-
-
-.. code-block:: bash
-
- $ chef-server-ctl org-create dev Development -f /tmp/id-dev.key
-
-org-delete
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_org_delete.rst
-
-**Syntax**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_org_delete_syntax.rst
-
-**Examples**
-
-.. code-block:: bash
-
- $ chef-server-ctl org-delete infra-testing-20140909
-
-
-.. code-block:: bash
-
- $ chef-server-ctl org-delete pedant-testing-org
-
-org-list
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_org_list.rst
-
-**Syntax**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_org_list_syntax.rst
-
-**Options**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_org_list_options.rst
-
-org-show
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_org_show.rst
-
-**Syntax**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_org_show_syntax.rst
-
-org-user-add
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_org_user_add.rst
-
-**Syntax**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_org_user_add_syntax.rst
-
-**Options**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_org_user_add_options.rst
-
-**Examples**
-
-.. code-block:: bash
-
- $ chef-server-ctl org-user-add prod john_smith
-
-.. code-block:: bash
-
- $ chef-server-ctl org-user-add preprod testmaster
-
-.. code-block:: bash
-
- $ chef-server-ctl org-user-add dev grantmc --admin
-
-
-org-user-remove
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_org_user_remove.rst
-
-**Syntax**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_org_user_remove_syntax.rst
-
-**Examples**
-
-.. code-block:: bash
-
- $ chef-server-ctl org-user-remove prod john_smith
-
-.. code-block:: bash
-
- $ chef-server-ctl org-user-remove prod testmaster
-
-
-password
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_password.rst
-
-reconfigure
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_reconfigure.rst
-
-show-config
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_show_config.rst
-
-uninstall
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_uninstall.rst
-
-upgrade
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_upgrade.rst
-
-**Syntax**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_upgrade_syntax.rst
-
-**Options**
-
-.. note:: Options for the ``upgrade`` subcommand may only be used when upgrading from |chef server osc| 11 to |chef server| 12.
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_upgrade_options.rst
-
-user-create
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_user_create.rst
-
-**Syntax**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_user_create_syntax.rst
-
-**Options**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_user_create_options.rst
-
-**Examples**
-
-.. code-block:: bash
-
- $ chef-server-ctl user-create john_smith John Smith john_smith@example.com insecure-passord
-
-
-.. code-block:: bash
-
- $ chef-server-ctl user-create jane_doe Jane Doe jane_doe@example.com PaSSword -f /tmp/jane_doe.key
-
-
-.. code-block:: bash
-
- $ chef-server-ctl user-create waldendude Henry David Thoreau waldendude@example.com excursions
-
-
-user-delete
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_user_delete.rst
-
-**Syntax**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_user_delete_syntax.rst
-
-**Examples**
-
-.. code-block:: bash
-
- $ chef-server-ctl user-delete john_smith
-
-
-.. code-block:: bash
-
- $ chef-server-ctl user-delete jane_doe
-
-user-edit
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_user_edit.rst
-
-**Syntax**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_user_edit_syntax.rst
-
-**Examples**
-
-.. code-block:: bash
-
- $ chef-server-ctl user-edit john_smith
-
-
-.. code-block:: bash
-
- $ chef-server-ctl user-edit jane_doe
-
-
-user-list
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_user_list.rst
-
-**Syntax**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_user_list_syntax.rst
-
-**Options**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_user_list_options.rst
-
-user-show
-=====================================================
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_user_show.rst
-
-**Syntax**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_user_show_syntax.rst
-
-**Options**
-
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_user_show_options.rst
-
-Service Subcommands
-=====================================================
-The |chef server| has a built in process supervisor, which ensures that all of the required services are in the appropriate state at any given time. The supervisor starts two processes per service.
-
-hup
------------------------------------------------------
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_hup.rst
-
-int
------------------------------------------------------
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_int.rst
-
-kill
------------------------------------------------------
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_kill.rst
-
-once
------------------------------------------------------
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_once.rst
-
-restart
------------------------------------------------------
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_restart.rst
-
-service-list
------------------------------------------------------
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_service_list.rst
-
-start
------------------------------------------------------
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_start.rst
-
-status
------------------------------------------------------
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_status.rst
-
-High Availability
-+++++++++++++++++++++++++++++++++++++++++++++++++++++
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_status_ha.rst
-
-Log Files
-+++++++++++++++++++++++++++++++++++++++++++++++++++++
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_status_logs.rst
-
-stop
------------------------------------------------------
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_stop.rst
-
-tail
------------------------------------------------------
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_tail.rst
-
-term
------------------------------------------------------
-.. include:: ../../includes_ctl_chef_server/includes_ctl_chef_server_term.rst
-
diff --git a/distro/common/html/_sources/ctl_chef_shell.txt b/distro/common/html/_sources/ctl_chef_shell.txt
deleted file mode 100644
index 75902f3141..0000000000
--- a/distro/common/html/_sources/ctl_chef_shell.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-=====================================================
-chef-shell
-=====================================================
-
-.. include:: ../../includes_chef_shell/includes_chef_shell.rst
-
-.. include:: ../../includes_ctl_chef_shell/includes_ctl_chef_shell.rst
-
-Modes
-=====================================================
-.. include:: ../../includes_chef_shell/includes_chef_shell_modes.rst
-
-Options
-=====================================================
-.. include:: ../../includes_ctl_chef_shell/includes_ctl_chef_shell_options.rst
diff --git a/distro/common/html/_sources/ctl_chef_solo.txt b/distro/common/html/_sources/ctl_chef_solo.txt
deleted file mode 100644
index 99f7b919de..0000000000
--- a/distro/common/html/_sources/ctl_chef_solo.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-=====================================================
-chef-solo
-=====================================================
-
-.. include:: ../../includes_chef_solo/includes_chef_solo.rst
-
-.. include:: ../../includes_ctl_chef_solo/includes_ctl_chef_solo.rst
-
-Options
-=====================================================
-.. include:: ../../release_chef_12-0/includes_ctl_chef_solo_options.rst
-
-Examples
-=====================================================
-**Use a URL**
-
-.. include:: ../../step_ctl_chef_solo/step_ctl_chef_solo_use_url.rst
-
-**Use a directory**
-
-.. include:: ../../step_ctl_chef_solo/step_ctl_chef_solo_use_directory.rst
-
-**Use a URL for cookbook and JSON data**
-
-.. include:: ../../step_ctl_chef_solo/step_ctl_chef_solo_url_for_cookbook_and_json.rst
diff --git a/distro/common/html/_sources/index.txt b/distro/common/html/_sources/index.txt
deleted file mode 100644
index e9b94cfda1..0000000000
--- a/distro/common/html/_sources/index.txt
+++ /dev/null
@@ -1,135 +0,0 @@
-=====================================================
-|chef client| Man Pages
-=====================================================
-
-The following command line interfaces are available in the |chef client|:
-
-* :doc:`ctl_chef_client`
-* :doc:`ctl_chef_server`
-* :doc:`ctl_chef_shell`
-* :doc:`ctl_chef_solo`
-* :doc:`knife`
-
-knife
-=====================================================
-.. include:: ../../includes_knife/includes_knife.rst
-
-The following sections describe functionality common to all |knife| subcommands:
-
-* :doc:`knife_using`
-* :doc:`knife_common_options`
-
-|knife| includes the following sub-commands:
-
-.. list-table::
- :widths: 150 450
- :header-rows: 1
-
- * - Sub-command
- - Description
- * - :doc:`knife_bootstrap`
- - .. include:: ../../includes_knife/includes_knife_bootstrap.rst
- * - :doc:`knife_client`
- - .. include:: ../../includes_knife/includes_knife_client.rst
- * - :doc:`knife_configure`
- - .. include:: ../../includes_knife/includes_knife_configure.rst
- * - :doc:`knife_cookbook`
- - .. include:: ../../includes_knife/includes_knife_cookbook.rst
- * - :doc:`knife_cookbook_site`
- - .. include:: ../../includes_knife/includes_knife_site_cookbook.rst
- * - :doc:`knife_data_bag`
- - .. include:: ../../includes_knife/includes_knife_data_bag.rst
- * - :doc:`knife_delete`
- - .. include:: ../../includes_knife/includes_knife_delete.rst
- * - :doc:`knife_deps`
- - .. include:: ../../includes_knife/includes_knife_deps.rst
- * - :doc:`knife_diff`
- - .. include:: ../../includes_knife/includes_knife_diff.rst
- * - :doc:`knife_download`
- - .. include:: ../../includes_knife/includes_knife_download.rst
- * - :doc:`knife_edit`
- - .. include:: ../../includes_knife/includes_knife_edit.rst
- * - :doc:`knife_environment`
- - .. include:: ../../includes_knife/includes_knife_environment.rst
- * - :doc:`knife_exec`
- - .. include:: ../../includes_knife/includes_knife_exec.rst
- * - :doc:`knife_index_rebuild`
- - .. include:: ../../includes_knife/includes_knife_index_rebuild.rst
- * - :doc:`knife_list`
- - .. include:: ../../includes_knife/includes_knife_list.rst
- * - :doc:`knife_node`
- - .. include:: ../../includes_knife/includes_knife_node.rst
- * - :doc:`knife_raw`
- - .. include:: ../../includes_knife/includes_knife_raw.rst
- * - :doc:`knife_recipe_list`
- - .. include:: ../../includes_knife/includes_knife_recipe_list.rst
- * - :doc:`knife_role`
- - .. include:: ../../includes_knife/includes_knife_role.rst
- * - :doc:`knife_search`
- - .. include:: ../../includes_knife/includes_knife_search.rst
- * - :doc:`knife_serve`
- - .. include:: ../../includes_knife/includes_knife_serve.rst
- * - :doc:`knife_show`
- - .. include:: ../../includes_knife/includes_knife_show.rst
- * - :doc:`knife_ssh`
- - .. include:: ../../includes_knife/includes_knife_ssh.rst
- * - :doc:`knife_ssl_check`
- - .. include:: ../../includes_knife/includes_knife_ssl_check.rst
- * - :doc:`knife_ssl_fetch`
- - .. include:: ../../includes_knife/includes_knife_ssl_fetch.rst
- * - :doc:`knife_status`
- - .. include:: ../../includes_knife/includes_knife_status.rst
- * - :doc:`knife_tag`
- - .. include:: ../../includes_knife/includes_knife_tag.rst
- * - :doc:`knife_upload`
- - .. include:: ../../includes_knife/includes_knife_upload.rst
- * - :doc:`knife_user`
- - .. include:: ../../includes_knife/includes_knife_user.rst
- * - :doc:`knife_xargs`
- - .. include:: ../../includes_knife/includes_knife_xargs.rst
-
-
-.. Hide the TOC from this file.
-
-.. toctree::
- :hidden:
-
- ctl_chef_client
- ctl_chef_server
- ctl_chef_shell
- ctl_chef_solo
- knife
- knife_common_options
- knife_bootstrap
- knife_client
- knife_configure
- knife_cookbook
- knife_cookbook_site
- knife_data_bag
- knife_delete
- knife_deps
- knife_diff
- knife_download
- knife_edit
- knife_environment
- knife_exec
- knife_index_rebuild
- knife_list
- knife_node
- knife_raw
- knife_recipe_list
- knife_role
- knife_search
- knife_serve
- knife_show
- knife_ssh
- knife_ssl_check
- knife_ssl_fetch
- knife_status
- knife_tag
- knife_upload
- knife_user
- knife_using
- knife_xargs
-
-
diff --git a/distro/common/html/_sources/knife.txt b/distro/common/html/_sources/knife.txt
deleted file mode 100644
index 6320825e83..0000000000
--- a/distro/common/html/_sources/knife.txt
+++ /dev/null
@@ -1,74 +0,0 @@
-=====================================================
-knife
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife.rst
-
-The following sections describe functionality common to all |knife| subcommands:
-
-* :doc:`knife_using`
-* :doc:`knife_common_options`
-
-|knife| includes the following sub-commands:
-
-.. list-table::
- :widths: 150 450
- :header-rows: 1
-
- * - Sub-command
- - Description
- * - :doc:`knife_bootstrap`
- - .. include:: ../../includes_knife/includes_knife_bootstrap.rst
- * - :doc:`knife_client`
- - .. include:: ../../includes_knife/includes_knife_client.rst
- * - :doc:`knife_configure`
- - .. include:: ../../includes_knife/includes_knife_configure.rst
- * - :doc:`knife_cookbook`
- - .. include:: ../../includes_knife/includes_knife_cookbook.rst
- * - :doc:`knife_cookbook_site`
- - .. include:: ../../includes_knife/includes_knife_site_cookbook.rst
- * - :doc:`knife_data_bag`
- - .. include:: ../../includes_knife/includes_knife_data_bag.rst
- * - :doc:`knife_delete`
- - .. include:: ../../includes_knife/includes_knife_delete.rst
- * - :doc:`knife_deps`
- - .. include:: ../../includes_knife/includes_knife_deps.rst
- * - :doc:`knife_diff`
- - .. include:: ../../includes_knife/includes_knife_diff.rst
- * - :doc:`knife_download`
- - .. include:: ../../includes_knife/includes_knife_download.rst
- * - :doc:`knife_edit`
- - .. include:: ../../includes_knife/includes_knife_edit.rst
- * - :doc:`knife_environment`
- - .. include:: ../../includes_knife/includes_knife_environment.rst
- * - :doc:`knife_exec`
- - .. include:: ../../includes_knife/includes_knife_exec.rst
- * - :doc:`knife_index_rebuild`
- - .. include:: ../../includes_knife/includes_knife_index_rebuild.rst
- * - :doc:`knife_list`
- - .. include:: ../../includes_knife/includes_knife_list.rst
- * - :doc:`knife_node`
- - .. include:: ../../includes_knife/includes_knife_node.rst
- * - :doc:`knife_raw`
- - .. include:: ../../includes_knife/includes_knife_raw.rst
- * - :doc:`knife_recipe_list`
- - .. include:: ../../includes_knife/includes_knife_recipe_list.rst
- * - :doc:`knife_role`
- - .. include:: ../../includes_knife/includes_knife_role.rst
- * - :doc:`knife_search`
- - .. include:: ../../includes_knife/includes_knife_search.rst
- * - :doc:`knife_show`
- - .. include:: ../../includes_knife/includes_knife_show.rst
- * - :doc:`knife_ssh`
- - .. include:: ../../includes_knife/includes_knife_ssh.rst
- * - :doc:`knife_status`
- - .. include:: ../../includes_knife/includes_knife_status.rst
- * - :doc:`knife_tag`
- - .. include:: ../../includes_knife/includes_knife_tag.rst
- * - :doc:`knife_upload`
- - .. include:: ../../includes_knife/includes_knife_upload.rst
- * - :doc:`knife_user`
- - .. include:: ../../includes_knife/includes_knife_user.rst
- * - :doc:`knife_xargs`
- - .. include:: ../../includes_knife/includes_knife_xargs.rst
-
diff --git a/distro/common/html/_sources/knife_bootstrap.txt b/distro/common/html/_sources/knife_bootstrap.txt
deleted file mode 100644
index 29af753b23..0000000000
--- a/distro/common/html/_sources/knife_bootstrap.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-=====================================================
-knife bootstrap
-=====================================================
-
-.. include:: ../../includes_chef/includes_chef_bootstrap.rst
-
-.. include:: ../../includes_knife/includes_knife_bootstrap.rst
-
-.. note:: To bootstrap the |chef client| on |windows| machines, the `knife-windows <http://docs.opscode.com/plugin_knife_windows.html>`_ plugins is required, which includes the necessary bootstrap scripts that are used to do the actual installation.
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_bootstrap_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../release_chef_12-0/includes_knife_bootstrap_options.rst
-
-Custom Templates
-=====================================================
-The ``chef-full`` distribution uses the |omnibus installer|. For most bootstrap operations, regardless of the platform on which the target node is running, using the ``chef-full`` distribution is the best approach for installing the |chef client| on a target node. In some situations, using another supported distribution is necessary. And in some situations, a custom template may be required. For example, the default bootstrap operation relies on an Internet connection to get the distribution to the target node. If a target node cannot access the Internet, then a custom template can be used to define a specific location for the distribution so that the target node may access it during the bootstrap operation.
-
-A custom bootstrap template file (``template_filename.erb``) must be located in a ``bootstrap/`` directory. Use the ``--distro`` option with the ``knife bootstrap`` subcommand to specify the bootstrap template file. For example, a bootstrap template file named "british_sea_power.erb":
-
-.. code-block:: bash
-
- $ knife bootstrap 123.456.7.8 -x username -P password --sudo --distro "british_sea_power.erb"
-
-The following examples show how a bootstrap template file can be customized for various platforms.
-
-Ubuntu 12.04
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_bootstrap_example_ubuntu.rst
-
-Debian and Apt
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_bootstrap_example_debian.rst
-
-Microsoft Windows
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_bootstrap_example_windows.rst
-
-Examples
-=====================================================
-The following examples show how to use this |knife| subcommand:
-
-**Use an SSH password**
-
-.. include:: ../../step_knife/step_knife_bootstrap_use_ssh_password.rst
-
-**Use a file that contains a private key**
-
-.. include:: ../../step_knife/step_knife_bootstrap_use_file_with_private_key.rst
-
diff --git a/distro/common/html/_sources/knife_client.txt b/distro/common/html/_sources/knife_client.txt
deleted file mode 100644
index d9d6ba62a1..0000000000
--- a/distro/common/html/_sources/knife_client.txt
+++ /dev/null
@@ -1,151 +0,0 @@
-=====================================================
-knife client
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_client.rst
-
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-bulk delete
-=====================================================
-.. include:: ../../includes_knife/includes_knife_client_bulk_delete.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_client_bulk_delete_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-None.
-
-create
-=====================================================
-.. include:: ../../includes_knife/includes_knife_client_create.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_client_create_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_client_create_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Create an admin client**
-
-.. include:: ../../step_knife/step_knife_client_create_admin.rst
-
-**Create an admin client for Enterprise Chef**
-
-.. include:: ../../step_knife/step_knife_client_create_hosted_and_private.rst
-
-delete
-=====================================================
-.. include:: ../../includes_knife/includes_knife_client_delete.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_client_delete_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Delete a client**
-
-.. include:: ../../step_knife/step_knife_client_delete.rst
-
-edit
-=====================================================
-.. include:: ../../includes_knife/includes_knife_client_edit.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_client_edit_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Edit a client**
-
-.. include:: ../../step_knife/step_knife_client_edit.rst
-
-list
-=====================================================
-.. include:: ../../includes_knife/includes_knife_client_list.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_client_list_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_client_list_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**View a list of clients**
-
-.. include:: ../../step_knife/step_knife_client_list_all.rst
-
-.. include:: ../../step_knife/step_knife_client_list_authenticate.rst
-
-reregister
-=====================================================
-.. include:: ../../includes_knife/includes_knife_client_reregister.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_client_reregister_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_client_reregister_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Reregister clients**
-
-.. include:: ../../step_knife/step_knife_client_reregister.rst
-
-show
-=====================================================
-.. include:: ../../includes_knife/includes_knife_client_show.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_client_show_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_client_show_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Show clients**
-
-.. include:: ../../step_knife/step_knife_client_show.rst
-
-.. include:: ../../step_knife/step_knife_common_view_json.rst \ No newline at end of file
diff --git a/distro/common/html/_sources/knife_common_options.txt b/distro/common/html/_sources/knife_common_options.txt
deleted file mode 100644
index 154db5c1ee..0000000000
--- a/distro/common/html/_sources/knife_common_options.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-=====================================================
-Common Options
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_common_options.rst
-
diff --git a/distro/common/html/_sources/knife_configure.txt b/distro/common/html/_sources/knife_configure.txt
deleted file mode 100644
index 1e0485e7fa..0000000000
--- a/distro/common/html/_sources/knife_configure.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-=====================================================
-knife configure
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_configure.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_configure_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../includes_knife/includes_knife_configure_options.rst
-
-Examples
-=====================================================
-The following examples show how to use this |knife| subcommand:
-
-**Configure knife.rb**
-
-.. include:: ../../step_knife/step_knife_configure_knife_rb.rst
-
-**Configure client.rb**
-
-.. include:: ../../step_knife/step_knife_configure_client_rb.rst
-
-
diff --git a/distro/common/html/_sources/knife_cookbook.txt b/distro/common/html/_sources/knife_cookbook.txt
deleted file mode 100644
index 462e4dd76c..0000000000
--- a/distro/common/html/_sources/knife_cookbook.txt
+++ /dev/null
@@ -1,236 +0,0 @@
-=====================================================
-knife cookbook
-=====================================================
-
-.. include:: ../../includes_cookbooks/includes_cookbooks.rst
-
-.. include:: ../../includes_knife/includes_knife_cookbook.rst
-
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-bulk delete
-=====================================================
-.. include:: ../../includes_knife/includes_knife_cookbook_bulk_delete.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_bulk_delete_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_bulk_delete_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Bulk delete many cookbooks**
-
-.. include:: ../../step_knife/step_knife_cookbook_bulk_delete.rst
-
-
-create
-=====================================================
-.. include:: ../../includes_knife/includes_knife_cookbook_create.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_create_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_create_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Create a cookbook**
-
-.. include:: ../../step_knife/step_knife_cookbook_create_with_options.rst
-
-
-delete
-=====================================================
-.. include:: ../../includes_knife/includes_knife_cookbook_delete.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_delete_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_delete_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Delete a cookbook**
-
-.. include:: ../../step_knife/step_knife_cookbook_delete.rst
-
-
-download
-=====================================================
-.. include:: ../../includes_knife/includes_knife_cookbook_download.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_download_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_download_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Download a cookbook**
-
-.. include:: ../../step_knife/step_knife_cookbook_download.rst
-
-
-list
-=====================================================
-.. include:: ../../includes_knife/includes_knife_cookbook_list.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_list_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_list_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**View a list of cookbooks**
-
-.. include:: ../../step_knife/step_knife_cookbook_list.rst
-
-
-metadata
-=====================================================
-.. include:: ../../includes_knife/includes_knife_cookbook_metadata.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_metadata_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_metadata_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Generate metadata**
-
-.. include:: ../../step_knife/step_knife_cookbook_metadata.rst
-
-
-metadata from file
-=====================================================
-.. include:: ../../includes_knife/includes_knife_cookbook_metadata_from_file.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_metadata_from_file_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**View metadata**
-
-.. include:: ../../step_knife/step_knife_cookbook_metadata_from_file.rst
-
-
-show
-=====================================================
-.. include:: ../../includes_knife/includes_knife_cookbook_show.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_show_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_show_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Show cookbook data**
-
-.. include:: ../../step_knife/step_knife_cookbook_show_cookbook_data.rst
-
-**Show cookbook versions**
-
-.. include:: ../../step_knife/step_knife_cookbook_show_cookbook_versions.rst
-
-**Show a cookbook version**
-
-.. include:: ../../step_knife/step_knife_cookbook_show_cookbook_version.rst
-
-**Show cookbook data as JSON**
-
-.. include:: ../../step_knife/step_knife_common_view_json.rst
-
-
-test
-=====================================================
-.. include:: ../../includes_knife/includes_knife_cookbook_test.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_test_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_test_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Test a cookbook**
-
-.. include:: ../../step_knife/step_knife_cookbook_test.rst
-
-
-upload
-=====================================================
-.. include:: ../../includes_knife/includes_knife_cookbook_upload.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_upload_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_cookbook_upload_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Upload a cookbook**
-
-.. include:: ../../step_knife/step_knife_cookbook_upload.rst
-
-**Freeze a cookbook**
-
-.. include:: ../../step_knife/step_knife_cookbook_upload_freeze.rst
-
-.. include:: ../../step_knife/step_knife_cookbook_upload_force.rst
diff --git a/distro/common/html/_sources/knife_cookbook_site.txt b/distro/common/html/_sources/knife_cookbook_site.txt
deleted file mode 100644
index d1b03f2fa0..0000000000
--- a/distro/common/html/_sources/knife_cookbook_site.txt
+++ /dev/null
@@ -1,157 +0,0 @@
-=====================================================
-knife cookbook site
-=====================================================
-
-.. include:: ../../includes_api_cookbooks_site/includes_api_cookbooks_site.rst
-
-.. include:: ../../includes_knife/includes_knife_site_cookbook.rst
-
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-download
-=====================================================
-.. include:: ../../includes_knife/includes_knife_site_cookbook_download.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_site_cookbook_download_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_site_cookbook_download_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Download a cookbook**
-
-.. include:: ../../step_knife/step_knife_site_cookbook_download.rst
-
-install
-=====================================================
-.. include:: ../../includes_knife/includes_knife_site_cookbook_install.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_site_cookbook_install_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_site_cookbook_install_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Install a cookbook**
-
-.. include:: ../../step_knife/step_knife_site_cookbook_install.rst
-
-list
-=====================================================
-.. include:: ../../includes_knife/includes_knife_site_cookbook_list.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_site_cookbook_list_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_site_cookbook_list_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**View a list of cookbooks**
-
-.. include:: ../../step_knife/step_knife_site_cookbook_list.rst
-
-search
-=====================================================
-.. include:: ../../includes_knife/includes_knife_site_cookbook_search.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_site_cookbook_search_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Search for cookbooks**
-
-.. include:: ../../step_knife/step_knife_site_cookbook_search.rst
-
-share
-=====================================================
-.. include:: ../../includes_knife/includes_knife_site_cookbook_share.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_site_cookbook_share_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../release_chef_12-0/includes_knife_site_cookbook_share_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Share a cookbook**
-
-.. include:: ../../step_knife/step_knife_site_cookbook_share.rst
-
-
-show
-=====================================================
-.. include:: ../../includes_knife/includes_knife_site_cookbook_show.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_site_cookbook_show_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_site_cookbook_show_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Show cookbook data**
-
-.. include:: ../../step_knife/step_knife_site_cookbook_show.rst
-
-**Show cookbook data as JSON**
-
-.. include:: ../../step_knife/step_knife_common_view_json.rst
-
-
-unshare
-=====================================================
-.. include:: ../../includes_knife/includes_knife_site_cookbook_unshare.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_site_cookbook_unshare_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Unshare a cookbook**
-
-.. include:: ../../step_knife/step_knife_site_cookbook_unshare.rst
-
-
diff --git a/distro/common/html/_sources/knife_data_bag.txt b/distro/common/html/_sources/knife_data_bag.txt
deleted file mode 100644
index 369baab2b7..0000000000
--- a/distro/common/html/_sources/knife_data_bag.txt
+++ /dev/null
@@ -1,160 +0,0 @@
-=====================================================
-knife data bag
-=====================================================
-
-.. include:: ../../includes_data_bag/includes_data_bag.rst
-
-.. include:: ../../includes_data_bag/includes_data_bag_encryption.rst
-
-.. include:: ../../includes_knife/includes_knife_data_bag.rst
-
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-create
-=====================================================
-.. include:: ../../includes_knife/includes_knife_data_bag_create.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_data_bag_create_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../release_chef_12-0/includes_knife_data_bag_create_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Create a data bag**
-
-.. include:: ../../step_knife/step_knife_data_bag_create.rst
-
-delete
-=====================================================
-.. include:: ../../includes_knife/includes_knife_data_bag_delete.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_data_bag_delete_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_data_bag_delete_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Delete a data bag**
-
-.. include:: ../../step_knife/step_knife_data_bag_delete.rst
-
-**Delete a data bag item**
-
-.. include:: ../../step_knife/step_knife_data_bag_delete_item.rst
-
-edit
-=====================================================
-.. include:: ../../includes_knife/includes_knife_data_bag_edit.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_data_bag_edit_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../release_chef_12-0/includes_knife_data_bag_edit_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Edit a data bag**
-
-.. include:: ../../step_knife/step_knife_data_bag_edit.rst
-
-**Edit a data bag item**
-
-.. include:: ../../step_knife/step_knife_data_bag_edit_item.rst
-
-from file
-=====================================================
-.. include:: ../../includes_knife/includes_knife_data_bag_from_file.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_data_bag_from_file_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../release_chef_12-0/includes_knife_data_bag_from_file_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Create a data bag from a file**
-
-.. include:: ../../step_knife/step_knife_data_bag_from_file_create.rst
-
-**Create an encrypted data bag from a file**
-
-.. include:: ../../step_knife/step_knife_data_bag_from_file_create_encrypted.rst
-
-
-list
-=====================================================
-.. include:: ../../includes_knife/includes_knife_data_bag_list.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_data_bag_list_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_data_bag_list_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**View a list of data bags**
-
-.. include:: ../../step_knife/step_knife_data_bag_list.rst
-
-show
-=====================================================
-.. include:: ../../includes_knife/includes_knife_data_bag_show.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_data_bag_show_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../release_chef_12-0/includes_knife_data_bag_show_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Show a data bag**
-
-.. include:: ../../step_knife/step_knife_data_bag_show.rst
-
-**Show a data bag item**
-
-.. include:: ../../step_knife/step_knife_data_bag_show_item.rst
-
-**Show a data bag, encrypted**
-
-.. include:: ../../step_knife/step_knife_data_bag_show_item_encrypted.rst
-
-**Show a data bag, decrypted**
-
-.. include:: ../../step_knife/step_knife_data_bag_show_item_decrypted.rst
-
-**Show a data bag as JSON**
-
-.. include:: ../../step_knife/step_knife_data_bag_show_as_json.rst \ No newline at end of file
diff --git a/distro/common/html/_sources/knife_delete.txt b/distro/common/html/_sources/knife_delete.txt
deleted file mode 100644
index 55e239792a..0000000000
--- a/distro/common/html/_sources/knife_delete.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-=====================================================
-knife delete
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_delete.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_delete_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../includes_knife/includes_knife_delete_options.rst
-
-Examples
-=====================================================
-None.
-
-
-
-
-
-
-
diff --git a/distro/common/html/_sources/knife_deps.txt b/distro/common/html/_sources/knife_deps.txt
deleted file mode 100644
index b9c7da5e9d..0000000000
--- a/distro/common/html/_sources/knife_deps.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-=====================================================
-knife deps
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_deps.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_deps_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../includes_knife/includes_knife_deps_options.rst
-
-Examples
-=====================================================
-The following examples show how to use this |knife| subcommand:
-
-**Find dependencies for a node**
-
-.. include:: ../../step_knife/step_knife_deps_node.rst
-
-**Find dependencies for a role**
-
-.. include:: ../../step_knife/step_knife_deps_role.rst
-
-**Find dependencies for a cookbook**
-
-.. include:: ../../step_knife/step_knife_deps_cookbook.rst
-
-**Find dependencies for an environment**
-
-.. include:: ../../step_knife/step_knife_deps_environment.rst
-
-**Find dependencies for a combination of nodes, roles, and so on**
-
-.. include:: ../../step_knife/step_knife_deps_combo.rst
-
-**Use a wildcard**
-
-.. include:: ../../step_knife/step_knife_deps_wildcard.rst
-
-**Return as tree**
-
-.. include:: ../../step_knife/step_knife_deps_return_as_tree.rst
-
-**Pass knife deps output to knife upload**
-
-.. include:: ../../step_knife/step_knife_deps_pass_output_to_knife_upload.rst
-
-**Pass knife deps output to knife xargs**
-
-.. include:: ../../step_knife/step_knife_deps_pass_output_to_knife_xargs.rst
-
-
-
-
-
-
diff --git a/distro/common/html/_sources/knife_diff.txt b/distro/common/html/_sources/knife_diff.txt
deleted file mode 100644
index de467b3887..0000000000
--- a/distro/common/html/_sources/knife_diff.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-=====================================================
-knife diff
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_diff.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_diff_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../includes_knife/includes_knife_diff_options.rst
-
-Examples
-=====================================================
-The following examples show how to use this |knife| subcommand:
-
-**Compare files that contain JSON data**
-
-.. include:: ../../step_knife/step_knife_diff_compare_json_files.rst
-
-**Compare the chef-repo and the server**
-
-.. include:: ../../step_knife/step_knife_diff_compare_repo_and_server.rst
-
-**Compare, then return results**
-
-.. include:: ../../step_knife/step_knife_diff_compare_then_return_results.rst
-
-
-
diff --git a/distro/common/html/_sources/knife_download.txt b/distro/common/html/_sources/knife_download.txt
deleted file mode 100644
index d2497b7d3a..0000000000
--- a/distro/common/html/_sources/knife_download.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-=====================================================
-knife download
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_download.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_download_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../includes_knife/includes_knife_download_options.rst
-
-Examples
-=====================================================
-The following examples show how to use this |knife| subcommand:
-
-**Download the entire chef-repo**
-
-.. include:: ../../step_knife/step_knife_download_repository.rst
-
-**Download the /cookbooks directory**
-
-.. include:: ../../step_knife/step_knife_download_directory_cookbooks.rst
-
-**Download the /environments directory**
-
-.. include:: ../../step_knife/step_knife_download_directory_environments.rst
-
-**Download an environment**
-
-.. include:: ../../step_knife/step_knife_download_directory_environment.rst
-
-**Download the /roles directory**
-
-.. include:: ../../step_knife/step_knife_download_directory_roles.rst
-
-**Download cookbooks and roles**
-
-.. include:: ../../step_knife/step_knife_download_directory_cookbooks_and_role.rst
-
diff --git a/distro/common/html/_sources/knife_edit.txt b/distro/common/html/_sources/knife_edit.txt
deleted file mode 100644
index d9e44f44bc..0000000000
--- a/distro/common/html/_sources/knife_edit.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-=====================================================
-knife edit
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_edit.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_edit_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../includes_knife/includes_knife_edit_options.rst
-
-Examples
-=====================================================
-None.
-
-
-
-
-
-
-
diff --git a/distro/common/html/_sources/knife_environment.txt b/distro/common/html/_sources/knife_environment.txt
deleted file mode 100644
index a16e102ea0..0000000000
--- a/distro/common/html/_sources/knife_environment.txt
+++ /dev/null
@@ -1,158 +0,0 @@
-=====================================================
-knife environment
-=====================================================
-
-.. include:: ../../includes_environment/includes_environment.rst
-
-.. include:: ../../includes_knife/includes_knife_environment.rst
-
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-compare
-=====================================================
-.. include:: ../../includes_knife/includes_knife_environment_compare.rst
-
-**Syntax**
-
-.. include:: ../../includes_knife/includes_knife_environment_compare_syntax.rst
-
-**Options**
-
-.. include:: ../../includes_knife/includes_knife_environment_compare_options.rst
-
-**Example**
-
-.. include:: ../../step_knife/step_knife_environment_compare_single.rst
-
-.. include:: ../../step_knife/step_knife_environment_compare_multiple.rst
-
-.. include:: ../../step_knife/step_knife_environment_compare_all.rst
-
-create
-=====================================================
-.. include:: ../../includes_knife/includes_knife_environment_create.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_environment_create_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_environment_create_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Create an environment**
-
-.. include:: ../../step_knife/step_knife_environment_create.rst
-
-
-delete
-=====================================================
-.. include:: ../../includes_knife/includes_knife_environment_delete.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_environment_delete_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Delete an environment**
-
-.. include:: ../../step_knife/step_knife_environment_delete.rst
-
-
-edit
-=====================================================
-.. include:: ../../includes_knife/includes_knife_environment_edit.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_environment_edit_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Edit an environment**
-
-.. include:: ../../step_knife/step_knife_environment_edit.rst
-
-
-from file
-=====================================================
-.. include:: ../../includes_knife/includes_knife_environment_from_file.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_environment_from_file_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_environment_from_file_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Create an environment from a JSON file**
-
-.. include:: ../../step_knife/step_knife_environment_from_file.rst
-
-
-list
-=====================================================
-.. include:: ../../includes_knife/includes_knife_environment_list.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_environment_list_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_environment_list_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**View a list of environments**
-
-.. include:: ../../step_knife/step_knife_environment_list.rst
-
-
-show
-=====================================================
-.. include:: ../../includes_knife/includes_knife_environment_show.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_environment_show_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_environment_show_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Show environments**
-
-.. include:: ../../step_knife/step_knife_environment_show.rst
-
-**Show environments as JSON**
-
-.. include:: ../../step_knife/step_knife_common_view_json.rst
diff --git a/distro/common/html/_sources/knife_exec.txt b/distro/common/html/_sources/knife_exec.txt
deleted file mode 100644
index 85e7d9f628..0000000000
--- a/distro/common/html/_sources/knife_exec.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-=====================================================
-knife exec
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_exec.rst
-
-Authenticated API Requests
-=====================================================
-.. include:: ../../includes_knife/includes_knife_exec_authenticated_api_requests.rst
-
-|ruby| Scripts
-=====================================================
-.. include:: ../../includes_knife/includes_knife_exec_ruby.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_exec_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../includes_knife/includes_knife_exec_options.rst
-
-Examples
-=====================================================
-The following examples show how to use this |knife| subcommand:
-
-**Run Ruby scripts**
-
-.. include:: ../../step_knife/step_knife_exec_run_ruby_scripts.rst
-
-**Chef Knife status**
-
-.. include:: ../../step_knife/step_knife_exec_check_knife_status.rst
-
-**List available free memory**
-
-.. include:: ../../step_knife/step_knife_exec_list_available_free_memory.rst
-
-**List available search indexes**
-
-.. include:: ../../step_knife/step_knife_exec_list_available_search_indexes.rst
-
-**Query for multiple attributes**
-
-.. include:: ../../step_knife/step_knife_exec_query_for_multiple_attributes.rst
diff --git a/distro/common/html/_sources/knife_index_rebuild.txt b/distro/common/html/_sources/knife_index_rebuild.txt
deleted file mode 100644
index 3107f89275..0000000000
--- a/distro/common/html/_sources/knife_index_rebuild.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-=====================================================
-knife index rebuild
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_index_rebuild.rst
-
-.. note:: This subcommand ONLY works when run against the open source |chef server| version 10.x. This subcommand will NOT run against open source |chef server| 11, |chef server oec| (including hosted |chef server oec|), or |chef private|.
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_index_rebuild_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-|no_options|
-
-Examples
-=====================================================
-None. \ No newline at end of file
diff --git a/distro/common/html/_sources/knife_list.txt b/distro/common/html/_sources/knife_list.txt
deleted file mode 100644
index bad0be1e09..0000000000
--- a/distro/common/html/_sources/knife_list.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-=====================================================
-knife list
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_list.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_list_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../includes_knife/includes_knife_list_options.rst
-
-Examples
-=====================================================
-The following examples show how to use this |knife| subcommand:
-
-**List roles**
-
-.. include:: ../../step_knife/step_knife_list_roles.rst
-
-**List roles and environments**
-
-.. include:: ../../step_knife/step_knife_list_roles_and_environments.rst
-
-**List everything**
-
-.. include:: ../../step_knife/step_knife_list_everything.rst
-
-
diff --git a/distro/common/html/_sources/knife_node.txt b/distro/common/html/_sources/knife_node.txt
deleted file mode 100644
index 165db5079f..0000000000
--- a/distro/common/html/_sources/knife_node.txt
+++ /dev/null
@@ -1,250 +0,0 @@
-=====================================================
-knife node
-=====================================================
-
-.. include:: ../../includes_node/includes_node.rst
-
-.. include:: ../../includes_knife/includes_knife_node.rst
-
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-
-bulk delete
-=====================================================
-.. include:: ../../includes_knife/includes_knife_node_bulk_delete.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_node_bulk_delete_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Bulk delete nodes**
-
-.. include:: ../../step_knife/step_knife_node_bulk_delete.rst
-
-
-create
-=====================================================
-.. include:: ../../includes_knife/includes_knife_node_create.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_node_create_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Create a node**
-
-.. include:: ../../step_knife/step_knife_node_create.rst
-
-
-delete
-=====================================================
-.. include:: ../../includes_knife/includes_knife_node_delete.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_node_delete_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Delete a node**
-
-.. include:: ../../step_knife/step_knife_node_delete.rst
-
-
-edit
-=====================================================
-.. include:: ../../includes_knife/includes_knife_node_edit.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_node_edit_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_node_edit_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Edit a node**
-
-.. include:: ../../step_knife/step_knife_node_edit.rst
-
-
-from file
-=====================================================
-.. include:: ../../includes_knife/includes_knife_node_from_file.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_node_from_file_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Create a node using a JSON file**
-
-.. include:: ../../step_knife/step_knife_node_from_file.rst
-
-
-list
-=====================================================
-.. include:: ../../includes_knife/includes_knife_node_list.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_node_list_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_node_list_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**View a list of nodes**
-
-.. include:: ../../step_knife/step_knife_node_list_all.rst
-
-
-run_list add
-=====================================================
-.. include:: ../../includes_node/includes_node_run_list.rst
-
-.. include:: ../../includes_knife/includes_knife_node_run_list_add.rst
-
-.. include:: ../../includes_node/includes_node_run_list_format.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_node_run_list_add_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_node_run_list_add_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Add a role**
-
-.. include:: ../../step_knife/step_knife_node_run_list_add_role.rst
-
-**Add roles and recipes**
-
-.. include:: ../../step_knife/step_knife_node_run_list_add_roles_and_recipes.rst
-
-**Add a recipe with a FQDN**
-
-.. include:: ../../step_knife/step_knife_node_run_list_add_recipe_with_fqdn.rst
-
-**Add a recipe with a cookbook**
-
-.. include:: ../../step_knife/step_knife_node_run_list_add_recipe_with_cookbook.rst
-
-**Add the default recipe**
-
-.. include:: ../../step_knife/step_knife_node_run_list_add_default_recipe.rst
-
-
-run_list remove
-=====================================================
-.. include:: ../../includes_knife/includes_knife_node_run_list_remove.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_node_run_list_remove_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Remove a role**
-
-.. include:: ../../step_knife/step_knife_node_run_list_remove_role.rst
-
-**Remove a run-list**
-
-.. include:: ../../step_knife/step_knife_node_run_list_remove_run_list.rst
-
-
-show
-=====================================================
-.. include:: ../../includes_knife/includes_knife_node_show.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_node_show_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_node_show_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Show all data about nodes**
-
-.. include:: ../../step_knife/step_knife_node_show_all_data.rst
-
-**Show basic information about nodes**
-
-.. include:: ../../step_knife/step_knife_node_show_all_data_basic.rst
-
-**Show all data about nodes, truncated**
-
-.. include:: ../../step_knife/step_knife_node_show_all_data_truncated.rst
-
-**Show attributes**
-
-.. include:: ../../step_knife/step_knife_node_show_attribute.rst
-
-**Show the FQDN**
-
-.. include:: ../../step_knife/step_knife_node_show_fqdn.rst
-
-**Show a run-list**
-
-.. include:: ../../step_knife/step_knife_node_show_run_list.rst
-
-**Show as JSON data**
-
-.. include:: ../../step_knife/step_knife_common_view_json.rst
-
-**Show as raw JSON data**
-
-.. include:: ../../step_knife/step_knife_common_view_json_raw.rst
diff --git a/distro/common/html/_sources/knife_raw.txt b/distro/common/html/_sources/knife_raw.txt
deleted file mode 100644
index 3a27c0456a..0000000000
--- a/distro/common/html/_sources/knife_raw.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-=====================================================
-knife raw
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_raw.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_raw_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../includes_knife/includes_knife_raw_options.rst
-
-Examples
-=====================================================
-The following examples show how to use this |knife| subcommand:
-
-**View a client**
-
-.. include:: ../../step_knife/step_knife_raw_view_client.rst
-
-**View a node**
-
-.. include:: ../../step_knife/step_knife_raw_view_node.rst
-
-**Delete a data bag**
-
-.. include:: ../../step_knife/step_knife_raw_delete_data_bag.rst
-
-
-
-
diff --git a/distro/common/html/_sources/knife_recipe_list.txt b/distro/common/html/_sources/knife_recipe_list.txt
deleted file mode 100644
index 51d8bad845..0000000000
--- a/distro/common/html/_sources/knife_recipe_list.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-=====================================================
-knife recipe list
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_recipe_list.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_recipe_list_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-|no_options|
-
-Examples
-=====================================================
-The following examples show how to use this |knife| subcommand:
-
-**View a list of recipes**
-
-.. include:: ../../step_knife/step_knife_recipe_list.rst
diff --git a/distro/common/html/_sources/knife_role.txt b/distro/common/html/_sources/knife_role.txt
deleted file mode 100644
index edd218ddc6..0000000000
--- a/distro/common/html/_sources/knife_role.txt
+++ /dev/null
@@ -1,157 +0,0 @@
-=====================================================
-knife role
-=====================================================
-
-.. include:: ../../includes_role/includes_role.rst
-
-.. include:: ../../includes_knife/includes_knife_role.rst
-
-.. note:: To add a role to a node and then build out the run-list for that node, use the :doc:`knife node </knife_node>` sub-command and its ``run_list add`` argument.
-
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-bulk delete
-=====================================================
-.. include:: ../../includes_knife/includes_knife_role_bulk_delete.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_role_bulk_delete_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Bulk delete roles**
-
-.. include:: ../../step_knife/step_knife_role_bulk_delete.rst
-
-create
-=====================================================
-.. include:: ../../includes_knife/includes_knife_role_create.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_role_create_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_role_create_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Create a role**
-
-.. include:: ../../step_knife/step_knife_role_create.rst
-
-delete
-=====================================================
-.. include:: ../../includes_knife/includes_knife_role_delete.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_role_delete_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Delete a role**
-
-.. include:: ../../step_knife/step_knife_role_delete.rst
-
-edit
-=====================================================
-.. include:: ../../includes_knife/includes_knife_role_edit.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_role_edit_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Edit a role**
-
-.. include:: ../../step_knife/step_knife_role_edit.rst
-
-from file
-=====================================================
-.. include:: ../../includes_knife/includes_knife_role_from_file.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_role_from_file_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Create a role using JSON data**
-
-.. include:: ../../step_knife/step_knife_role_from_file.rst
-
-list
-=====================================================
-.. include:: ../../includes_knife/includes_knife_role_list.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_role_list_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_role_list_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**View a list of roles**
-
-.. include:: ../../step_knife/step_knife_role_list.rst
-
-show
-=====================================================
-.. include:: ../../includes_knife/includes_knife_role_show.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_role_show_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_role_show_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Show as JSON data**
-
-.. include:: ../../step_knife/step_knife_common_view_json.rst
-
-**Show as raw JSON data**
-
-.. include:: ../../step_knife/step_knife_common_view_json_raw.rst
-
-
diff --git a/distro/common/html/_sources/knife_search.txt b/distro/common/html/_sources/knife_search.txt
deleted file mode 100644
index c55ffc0308..0000000000
--- a/distro/common/html/_sources/knife_search.txt
+++ /dev/null
@@ -1,53 +0,0 @@
-=====================================================
-knife search
-=====================================================
-
-.. include:: ../../includes_search/includes_search.rst
-
-.. include:: ../../includes_knife/includes_knife_search.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_search_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../includes_knife/includes_knife_search_options.rst
-
-Examples
-=====================================================
-The following examples show how to use this |knife| subcommand:
-
-**Search by platform ID**
-
-.. include:: ../../step_knife/step_knife_search_by_platform_ids.rst
-
-**Search by instance type**
-
-.. include:: ../../step_knife/step_knife_search_by_platform_instance_type.rst
-
-**Search by node**
-
-.. include:: ../../step_knife/step_knife_search_by_node.rst
-
-**Search by node and environment**
-
-.. include:: ../../step_knife/step_knife_search_by_node_and_environment.rst
-
-**Search for nested attributes**
-
-.. include:: ../../step_knife/step_knife_search_by_nested_attribute.rst
-
-**Search for multiple attributes**
-
-.. include:: ../../step_knife/step_knife_search_by_query_for_many_attributes.rst
-
-**Search for nested attributes using a search query**
-
-.. include:: ../../step_knife/step_knife_search_by_query_for_nested_attribute.rst
-
-**Use a test query**
-
-.. include:: ../../step_knife/step_knife_search_test_query_for_ssh.rst
diff --git a/distro/common/html/_sources/knife_serve.txt b/distro/common/html/_sources/knife_serve.txt
deleted file mode 100644
index e39b89a7fc..0000000000
--- a/distro/common/html/_sources/knife_serve.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-=====================================================
-knife serve
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_serve.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_serve_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-|no_options|
-
-Examples
-=====================================================
-None. \ No newline at end of file
diff --git a/distro/common/html/_sources/knife_show.txt b/distro/common/html/_sources/knife_show.txt
deleted file mode 100644
index c9ca6958e3..0000000000
--- a/distro/common/html/_sources/knife_show.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-=====================================================
-knife show
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_show.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_show_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../includes_knife/includes_knife_show_options.rst
-
-Examples
-=====================================================
-The following examples show how to use this |knife| subcommand:
-
-**Show all cookbooks**
-
-.. include:: ../../step_knife/step_knife_show_all_cookbooks.rst
-
-**Show roles and environments**
-
-.. include:: ../../step_knife/step_knife_show_roles_and_environments.rst \ No newline at end of file
diff --git a/distro/common/html/_sources/knife_ssh.txt b/distro/common/html/_sources/knife_ssh.txt
deleted file mode 100644
index bc7f4e5977..0000000000
--- a/distro/common/html/_sources/knife_ssh.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-=====================================================
-knife ssh
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_ssh.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_ssh_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../includes_knife/includes_knife_ssh_options.rst
-
-Examples
-=====================================================
-The following examples show how to use this |knife| subcommand:
-
-**Find server uptime**
-
-.. include:: ../../step_knife/step_knife_ssh_find_uptime.rst
-
-**Run the chef-client on all nodes**
-
-.. include:: ../../step_knife/step_knife_ssh_run_chef_client_on_all_nodes.rst
-
-**Force a chef-client run**
-
-.. include:: ../../step_knife/step_knife_ssh_force_chef_run.rst
-
-**Run a command based on search query**
-
-.. include:: ../../step_knife/step_knife_ssh_query_for_nodes.rst
-
-**Upgrade all nodes**
-
-.. include:: ../../step_knife/step_knife_ssh_upgrade_nodes.rst
-
-**Specify the shell type**
-
-.. include:: ../../step_knife/step_knife_ssh_shell_type.rst
diff --git a/distro/common/html/_sources/knife_ssl_check.txt b/distro/common/html/_sources/knife_ssl_check.txt
deleted file mode 100644
index 76ce4c6cfd..0000000000
--- a/distro/common/html/_sources/knife_ssl_check.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-=====================================================
-knife ssl check
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_ssl_check.rst
-
-**Syntax**
-
-.. include:: ../../includes_knife/includes_knife_ssl_check_syntax.rst
-
-**Options**
-
-.. include:: ../../includes_knife_manpage_options/includes_knife_ssl_check_options.rst
-
-**Examples**
-
-The following examples show how to use this |knife| subcommand:
-
-**Verify the SSL configuration for the Chef server**
-
-.. code-block:: bash
-
- $ knife ssl check
-
-**Verify the SSL configuration for the chef-client**
-
-.. code-block:: bash
-
- $ knife ssl check -c /etc/chef/client.rb
-
-**Verify an external server's SSL certificate**
-
-.. code-block:: bash
-
- $ knife ssl check URL_or_URI
-
-for example:
-
-.. code-block:: bash
-
- $ knife ssl check https://www.getchef.com
diff --git a/distro/common/html/_sources/knife_ssl_fetch.txt b/distro/common/html/_sources/knife_ssl_fetch.txt
deleted file mode 100644
index 9c3b5328e5..0000000000
--- a/distro/common/html/_sources/knife_ssl_fetch.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-=====================================================
-knife ssl fetch
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_ssl_fetch.rst
-
-**Syntax**
-
-.. include:: ../../includes_knife/includes_knife_ssl_fetch_syntax.rst
-
-**Options**
-
-.. include:: ../../includes_knife_manpage_options/includes_knife_ssl_fetch_options.rst
-
-**Examples**
-
-The following examples show how to use this |knife| subcommand:
-
-**Fetch the SSL certificates used by Knife from the Chef server**
-
-.. code-block:: bash
-
- $ knife ssl fetch
-
-**Fetch the SSL certificates used by the chef-client from the Chef server**
-
-.. code-block:: bash
-
- $ knife ssl fetch -c /etc/chef/client.rb
-
-**Fetch SSL certificates from a URL or URI**
-
-.. code-block:: bash
-
- $ knife ssl fetch URL_or_URI
-
-for example:
-
-.. code-block:: bash
-
- $ knife ssl fetch https://www.getchef.com
diff --git a/distro/common/html/_sources/knife_status.txt b/distro/common/html/_sources/knife_status.txt
deleted file mode 100644
index 7f00826b96..0000000000
--- a/distro/common/html/_sources/knife_status.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-=====================================================
-knife status
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_status.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_status_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../release_chef_12-0/includes_knife_status_options.rst
-
-Examples
-=====================================================
-The following examples show how to use this |knife| subcommand:
-
-**View status, include run-lists**
-
-.. include:: ../../step_knife/step_knife_status_include_run_lists.rst
-
-**View status using a date range**
-
-.. include:: ../../step_knife/step_knife_status_past_hour.rst
-
-**View status using a query**
-
-.. include:: ../../step_knife/step_knife_status_returned_by_query.rst
-
-**View status for all nodes**
-
-.. include:: ../../step_knife/step_knife_status_view_for_all_nodes.rst
-
-
diff --git a/distro/common/html/_sources/knife_tag.txt b/distro/common/html/_sources/knife_tag.txt
deleted file mode 100644
index 6fea1e321d..0000000000
--- a/distro/common/html/_sources/knife_tag.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-=====================================================
-knife tag
-=====================================================
-
-.. include:: ../../includes_chef/includes_chef_tags.rst
-
-.. include:: ../../includes_knife/includes_knife_tag.rst
-
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-create
-=====================================================
-.. include:: ../../includes_knife/includes_knife_tag_create.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_tag_create_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Create tags**
-
-.. include:: ../../step_knife/step_knife_tag_create.rst
-
-delete
-=====================================================
-.. include:: ../../includes_knife/includes_knife_tag_delete.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_tag_delete_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Delete tags**
-
-.. include:: ../../step_knife/step_knife_tag_delete.rst
-
-list
-=====================================================
-.. include:: ../../includes_knife/includes_knife_tag_list.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_tag_list_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**View a list of tags**
-
-.. include:: ../../step_knife/step_knife_tag_list.rst
diff --git a/distro/common/html/_sources/knife_upload.txt b/distro/common/html/_sources/knife_upload.txt
deleted file mode 100644
index bd37725108..0000000000
--- a/distro/common/html/_sources/knife_upload.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-=====================================================
-knife upload
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_upload.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_upload_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../includes_knife/includes_knife_upload_options.rst
-
-Examples
-=====================================================
-The following examples show how to use this |knife| subcommand:
-
-**Upload the entire chef-repo**
-
-.. include:: ../../step_knife/step_knife_upload_repository.rst
-
-**Upload the /cookbooks directory**
-
-.. include:: ../../step_knife/step_knife_upload_directory_cookbooks.rst
-
-**Upload the /environments directory**
-
-.. include:: ../../step_knife/step_knife_upload_directory_environments.rst
-
-**Upload a single environment**
-
-.. include:: ../../step_knife/step_knife_upload_directory_environment.rst
-
-**Upload the /roles directory**
-
-.. include:: ../../step_knife/step_knife_upload_directory_roles.rst
-
-**Upload cookbooks and roles**
-
-.. include:: ../../step_knife/step_knife_upload_directory_cookbooks_and_role.rst
-
-**Use output of knife deps to pass command to knife upload**
-
-.. include:: ../../step_knife/step_knife_upload_pass_to_knife_deps.rst
-
-
diff --git a/distro/common/html/_sources/knife_user.txt b/distro/common/html/_sources/knife_user.txt
deleted file mode 100644
index 1642d30f7b..0000000000
--- a/distro/common/html/_sources/knife_user.txt
+++ /dev/null
@@ -1,127 +0,0 @@
-=====================================================
-knife user
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_user.rst
-
-.. note:: This subcommand ONLY works when run against the open source |chef server| and will not run against |chef server oec| (including hosted |chef server oec|), or |chef private|.
-
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-create
-=====================================================
-.. include:: ../../includes_knife/includes_knife_user_create.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_user_create_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_user_create_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Create a user**
-
-.. include:: ../../step_knife/step_knife_user_create.rst
-
-delete
-=====================================================
-.. include:: ../../includes_knife/includes_knife_user_delete.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_user_delete_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Delete a user**
-
-.. include:: ../../step_knife/step_knife_user_delete.rst
-
-edit
-=====================================================
-.. include:: ../../includes_knife/includes_knife_user_edit.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_user_edit_syntax.rst
-
-Options
------------------------------------------------------
-|no_options|
-
-Examples
------------------------------------------------------
-None.
-
-
-list
-=====================================================
-.. include:: ../../includes_knife/includes_knife_user_list.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_user_list_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_user_list_options.rst
-
-Examples
------------------------------------------------------
-None.
-
-reregister
-=====================================================
-.. include:: ../../includes_knife/includes_knife_user_reregister.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_user_reregister_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_user_reregister_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Regenerate the RSA key-pair**
-
-.. include:: ../../step_knife/step_knife_user_reregister.rst
-
-show
-=====================================================
-.. include:: ../../includes_knife/includes_knife_user_show.rst
-
-Syntax
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_user_show_syntax.rst
-
-Options
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_user_show_options.rst
-
-Examples
------------------------------------------------------
-The following examples show how to use this |knife| subcommand:
-
-**Show user data**
-
-.. include:: ../../step_knife/step_knife_user_show.rst
-
-**Show user data as JSON**
-
-.. include:: ../../step_knife/step_knife_user_show_json.rst
-
diff --git a/distro/common/html/_sources/knife_using.txt b/distro/common/html/_sources/knife_using.txt
deleted file mode 100644
index 9484b167c4..0000000000
--- a/distro/common/html/_sources/knife_using.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-=====================================================
-Working with Knife
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_using.rst
-
-JSON Data Format
-=====================================================
-.. include:: ../../includes_knife/includes_knife_using_json.rst
-
-Set the Text Editor
------------------------------------------------------
-.. include:: ../../step_knife/step_knife_common_set_editor.rst
-
-Using Quotes
-=====================================================
-.. include:: ../../includes_knife/includes_knife_using_quotes.rst
-
-Sub-commands
-=====================================================
-.. include:: ../../includes_knife/includes_knife_using_subcommands.rst
-
-Verb Sub-commands
-=====================================================
-.. include:: ../../includes_knife/includes_knife_verbs.rst
-
-Wildcard Search
------------------------------------------------------
-.. include:: ../../includes_knife/includes_knife_verbs_wildcard.rst
-
-Plug-ins
-=====================================================
-.. include:: ../../includes_knife/includes_knife_using_plugins.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_using_syntax.rst
-
-Many Users, Same Repo
-=====================================================
-.. include:: ../../includes_repository/includes_repository_many_users_same_repo.rst
-
-
diff --git a/distro/common/html/_sources/knife_xargs.txt b/distro/common/html/_sources/knife_xargs.txt
deleted file mode 100644
index 852ba282aa..0000000000
--- a/distro/common/html/_sources/knife_xargs.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-=====================================================
-knife xargs
-=====================================================
-
-.. include:: ../../includes_knife/includes_knife_xargs.rst
-
-Syntax
-=====================================================
-.. include:: ../../includes_knife/includes_knife_xargs_syntax.rst
-
-Options
-=====================================================
-.. note:: Review the list of :doc:`common options </knife_common_options>` available to this (and all) |knife| subcommands and plugins.
-
-.. include:: ../../includes_knife/includes_knife_xargs_options.rst
-
-Examples
-=====================================================
-The following examples show how to use this |knife| subcommand:
-
-**Use output of knife deps to pass command to knife xargs**
-
-.. include:: ../../step_knife/step_knife_xargs_pass_command_to.rst
-
-
-
-
-
-
-
diff --git a/distro/common/html/_static/ajax-loader.gif b/distro/common/html/_static/ajax-loader.gif
deleted file mode 100644
index 61faf8cab2..0000000000
--- a/distro/common/html/_static/ajax-loader.gif
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/_static/basic.css b/distro/common/html/_static/basic.css
deleted file mode 100644
index a67b16ec88..0000000000
--- a/distro/common/html/_static/basic.css
+++ /dev/null
@@ -1,537 +0,0 @@
-/*
- * basic.css
- * ~~~~~~~~~
- *
- * Sphinx stylesheet -- basic theme.
- *
- * :copyright: Copyright 2007-2016, by the Sphinx team, see AUTHORS.
- * :license: BSD, see LICENSE for details.
- *
- */
-
-/* -- main layout ----------------------------------------------------------- */
-
-div.clearer {
- clear: both;
-}
-
-/* -- relbar ---------------------------------------------------------------- */
-
-div.related {
- width: 100%;
- font-size: 90%;
-}
-
-div.related h3 {
- display: none;
-}
-
-div.related ul {
- margin: 0;
- padding: 0 0 0 10px;
- list-style: none;
-}
-
-div.related li {
- display: inline;
-}
-
-div.related li.right {
- float: right;
- margin-right: 5px;
-}
-
-/* -- sidebar --------------------------------------------------------------- */
-
-div.sphinxsidebarwrapper {
- padding: 10px 5px 0 10px;
-}
-
-div.sphinxsidebar {
- float: left;
- width: 230px;
- margin-left: -100%;
- font-size: 90%;
-}
-
-div.sphinxsidebar ul {
- list-style: none;
-}
-
-div.sphinxsidebar ul ul,
-div.sphinxsidebar ul.want-points {
- margin-left: 20px;
- list-style: square;
-}
-
-div.sphinxsidebar ul ul {
- margin-top: 0;
- margin-bottom: 0;
-}
-
-div.sphinxsidebar form {
- margin-top: 10px;
-}
-
-div.sphinxsidebar input {
- border: 1px solid #98dbcc;
- font-family: sans-serif;
- font-size: 1em;
-}
-
-div.sphinxsidebar #searchbox input[type="text"] {
- width: 170px;
-}
-
-div.sphinxsidebar #searchbox input[type="submit"] {
- width: 30px;
-}
-
-img {
- border: 0;
- max-width: 100%;
-}
-
-/* -- search page ----------------------------------------------------------- */
-
-ul.search {
- margin: 10px 0 0 20px;
- padding: 0;
-}
-
-ul.search li {
- padding: 5px 0 5px 20px;
- background-image: url(file.png);
- background-repeat: no-repeat;
- background-position: 0 7px;
-}
-
-ul.search li a {
- font-weight: bold;
-}
-
-ul.search li div.context {
- color: #888;
- margin: 2px 0 0 30px;
- text-align: left;
-}
-
-ul.keywordmatches li.goodmatch a {
- font-weight: bold;
-}
-
-/* -- index page ------------------------------------------------------------ */
-
-table.contentstable {
- width: 90%;
-}
-
-table.contentstable p.biglink {
- line-height: 150%;
-}
-
-a.biglink {
- font-size: 1.3em;
-}
-
-span.linkdescr {
- font-style: italic;
- padding-top: 5px;
- font-size: 90%;
-}
-
-/* -- general index --------------------------------------------------------- */
-
-table.indextable {
- width: 100%;
-}
-
-table.indextable td {
- text-align: left;
- vertical-align: top;
-}
-
-table.indextable dl, table.indextable dd {
- margin-top: 0;
- margin-bottom: 0;
-}
-
-table.indextable tr.pcap {
- height: 10px;
-}
-
-table.indextable tr.cap {
- margin-top: 10px;
- background-color: #f2f2f2;
-}
-
-img.toggler {
- margin-right: 3px;
- margin-top: 3px;
- cursor: pointer;
-}
-
-div.modindex-jumpbox {
- border-top: 1px solid #ddd;
- border-bottom: 1px solid #ddd;
- margin: 1em 0 1em 0;
- padding: 0.4em;
-}
-
-div.genindex-jumpbox {
- border-top: 1px solid #ddd;
- border-bottom: 1px solid #ddd;
- margin: 1em 0 1em 0;
- padding: 0.4em;
-}
-
-/* -- general body styles --------------------------------------------------- */
-
-a.headerlink {
- visibility: hidden;
-}
-
-h1:hover > a.headerlink,
-h2:hover > a.headerlink,
-h3:hover > a.headerlink,
-h4:hover > a.headerlink,
-h5:hover > a.headerlink,
-h6:hover > a.headerlink,
-dt:hover > a.headerlink {
- visibility: visible;
-}
-
-div.body p.caption {
- text-align: inherit;
-}
-
-div.body td {
- text-align: left;
-}
-
-.field-list ul {
- padding-left: 1em;
-}
-
-.first {
- margin-top: 0 !important;
-}
-
-p.rubric {
- margin-top: 30px;
- font-weight: bold;
-}
-
-img.align-left, .figure.align-left, object.align-left {
- clear: left;
- float: left;
- margin-right: 1em;
-}
-
-img.align-right, .figure.align-right, object.align-right {
- clear: right;
- float: right;
- margin-left: 1em;
-}
-
-img.align-center, .figure.align-center, object.align-center {
- display: block;
- margin-left: auto;
- margin-right: auto;
-}
-
-.align-left {
- text-align: left;
-}
-
-.align-center {
- text-align: center;
-}
-
-.align-right {
- text-align: right;
-}
-
-/* -- sidebars -------------------------------------------------------------- */
-
-div.sidebar {
- margin: 0 0 0.5em 1em;
- border: 1px solid #ddb;
- padding: 7px 7px 0 7px;
- background-color: #ffe;
- width: 40%;
- float: right;
-}
-
-p.sidebar-title {
- font-weight: bold;
-}
-
-/* -- topics ---------------------------------------------------------------- */
-
-div.topic {
- border: 1px solid #ccc;
- padding: 7px 7px 0 7px;
- margin: 10px 0 10px 0;
-}
-
-p.topic-title {
- font-size: 1.1em;
- font-weight: bold;
- margin-top: 10px;
-}
-
-/* -- admonitions ----------------------------------------------------------- */
-
-div.admonition {
- margin-top: 10px;
- margin-bottom: 10px;
- padding: 7px;
-}
-
-div.admonition dt {
- font-weight: bold;
-}
-
-div.admonition dl {
- margin-bottom: 0;
-}
-
-p.admonition-title {
- margin: 0px 10px 5px 0px;
- font-weight: bold;
-}
-
-div.body p.centered {
- text-align: center;
- margin-top: 25px;
-}
-
-/* -- tables ---------------------------------------------------------------- */
-
-table.docutils {
- border: 0;
- border-collapse: collapse;
-}
-
-table.docutils td, table.docutils th {
- padding: 1px 8px 1px 5px;
- border-top: 0;
- border-left: 0;
- border-right: 0;
- border-bottom: 1px solid #aaa;
-}
-
-table.field-list td, table.field-list th {
- border: 0 !important;
-}
-
-table.footnote td, table.footnote th {
- border: 0 !important;
-}
-
-th {
- text-align: left;
- padding-right: 5px;
-}
-
-table.citation {
- border-left: solid 1px gray;
- margin-left: 1px;
-}
-
-table.citation td {
- border-bottom: none;
-}
-
-/* -- other body styles ----------------------------------------------------- */
-
-ol.arabic {
- list-style: decimal;
-}
-
-ol.loweralpha {
- list-style: lower-alpha;
-}
-
-ol.upperalpha {
- list-style: upper-alpha;
-}
-
-ol.lowerroman {
- list-style: lower-roman;
-}
-
-ol.upperroman {
- list-style: upper-roman;
-}
-
-dl {
- margin-bottom: 15px;
-}
-
-dd p {
- margin-top: 0px;
-}
-
-dd ul, dd table {
- margin-bottom: 10px;
-}
-
-dd {
- margin-top: 3px;
- margin-bottom: 10px;
- margin-left: 30px;
-}
-
-dt:target, .highlighted {
- background-color: #fbe54e;
-}
-
-dl.glossary dt {
- font-weight: bold;
- font-size: 1.1em;
-}
-
-.field-list ul {
- margin: 0;
- padding-left: 1em;
-}
-
-.field-list p {
- margin: 0;
-}
-
-.optional {
- font-size: 1.3em;
-}
-
-.versionmodified {
- font-style: italic;
-}
-
-.system-message {
- background-color: #fda;
- padding: 5px;
- border: 3px solid red;
-}
-
-.footnote:target {
- background-color: #ffa;
-}
-
-.line-block {
- display: block;
- margin-top: 1em;
- margin-bottom: 1em;
-}
-
-.line-block .line-block {
- margin-top: 0;
- margin-bottom: 0;
- margin-left: 1.5em;
-}
-
-.guilabel, .menuselection {
- font-family: sans-serif;
-}
-
-.accelerator {
- text-decoration: underline;
-}
-
-.classifier {
- font-style: oblique;
-}
-
-abbr, acronym {
- border-bottom: dotted 1px;
- cursor: help;
-}
-
-/* -- code displays --------------------------------------------------------- */
-
-pre {
- overflow: auto;
- overflow-y: hidden; /* fixes display issues on Chrome browsers */
-}
-
-td.linenos pre {
- padding: 5px 0px;
- border: 0;
- background-color: transparent;
- color: #aaa;
-}
-
-table.highlighttable {
- margin-left: 0.5em;
-}
-
-table.highlighttable td {
- padding: 0 0.5em 0 0.5em;
-}
-
-tt.descname {
- background-color: transparent;
- font-weight: bold;
- font-size: 1.2em;
-}
-
-tt.descclassname {
- background-color: transparent;
-}
-
-tt.xref, a tt {
- background-color: transparent;
- font-weight: bold;
-}
-
-h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
- background-color: transparent;
-}
-
-.viewcode-link {
- float: right;
-}
-
-.viewcode-back {
- float: right;
- font-family: sans-serif;
-}
-
-div.viewcode-block:target {
- margin: -1px -10px;
- padding: 0 10px;
-}
-
-/* -- math display ---------------------------------------------------------- */
-
-img.math {
- vertical-align: middle;
-}
-
-div.body div.math p {
- text-align: center;
-}
-
-span.eqno {
- float: right;
-}
-
-/* -- printout stylesheet --------------------------------------------------- */
-
-@media print {
- div.document,
- div.documentwrapper,
- div.bodywrapper {
- margin: 0 !important;
- width: 100%;
- }
-
- div.sphinxsidebar,
- div.related,
- div.footer,
- #top-link {
- display: none;
- }
-} \ No newline at end of file
diff --git a/distro/common/html/_static/chef.ico b/distro/common/html/_static/chef.ico
deleted file mode 100644
index 05e0b7e41c..0000000000
--- a/distro/common/html/_static/chef.ico
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/_static/chef_html_logo.png b/distro/common/html/_static/chef_html_logo.png
deleted file mode 100644
index 7e0389d95a..0000000000
--- a/distro/common/html/_static/chef_html_logo.png
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/_static/comment-bright.png b/distro/common/html/_static/comment-bright.png
deleted file mode 100644
index 551517b8c8..0000000000
--- a/distro/common/html/_static/comment-bright.png
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/_static/comment-close.png b/distro/common/html/_static/comment-close.png
deleted file mode 100644
index 09b54be46d..0000000000
--- a/distro/common/html/_static/comment-close.png
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/_static/comment.png b/distro/common/html/_static/comment.png
deleted file mode 100644
index 92feb52b88..0000000000
--- a/distro/common/html/_static/comment.png
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/_static/contents.png b/distro/common/html/_static/contents.png
deleted file mode 100644
index 7fb82154a1..0000000000
--- a/distro/common/html/_static/contents.png
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/_static/doctools.js b/distro/common/html/_static/doctools.js
deleted file mode 100644
index e380d88303..0000000000
--- a/distro/common/html/_static/doctools.js
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * doctools.js
- * ~~~~~~~~~~~
- *
- * Sphinx JavaScript utilities for all documentation.
- *
- * :copyright: Copyright 2007-2016, by the Sphinx team, see AUTHORS.
- * :license: BSD, see LICENSE for details.
- *
- */
-
-/**
- * select a different prefix for underscore
- */
-$u = _.noConflict();
-
-/**
- * make the code below compatible with browsers without
- * an installed firebug like debugger
-if (!window.console || !console.firebug) {
- var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
- "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace",
- "profile", "profileEnd"];
- window.console = {};
- for (var i = 0; i < names.length; ++i)
- window.console[names[i]] = function() {};
-}
- */
-
-/**
- * small helper function to urldecode strings
- */
-jQuery.urldecode = function(x) {
- return decodeURIComponent(x).replace(/\+/g, ' ');
-};
-
-/**
- * small helper function to urlencode strings
- */
-jQuery.urlencode = encodeURIComponent;
-
-/**
- * This function returns the parsed url parameters of the
- * current request. Multiple values per key are supported,
- * it will always return arrays of strings for the value parts.
- */
-jQuery.getQueryParameters = function(s) {
- if (typeof s == 'undefined')
- s = document.location.search;
- var parts = s.substr(s.indexOf('?') + 1).split('&');
- var result = {};
- for (var i = 0; i < parts.length; i++) {
- var tmp = parts[i].split('=', 2);
- var key = jQuery.urldecode(tmp[0]);
- var value = jQuery.urldecode(tmp[1]);
- if (key in result)
- result[key].push(value);
- else
- result[key] = [value];
- }
- return result;
-};
-
-/**
- * highlight a given string on a jquery object by wrapping it in
- * span elements with the given class name.
- */
-jQuery.fn.highlightText = function(text, className) {
- function highlight(node) {
- if (node.nodeType == 3) {
- var val = node.nodeValue;
- var pos = val.toLowerCase().indexOf(text);
- if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) {
- var span = document.createElement("span");
- span.className = className;
- span.appendChild(document.createTextNode(val.substr(pos, text.length)));
- node.parentNode.insertBefore(span, node.parentNode.insertBefore(
- document.createTextNode(val.substr(pos + text.length)),
- node.nextSibling));
- node.nodeValue = val.substr(0, pos);
- }
- }
- else if (!jQuery(node).is("button, select, textarea")) {
- jQuery.each(node.childNodes, function() {
- highlight(this);
- });
- }
- }
- return this.each(function() {
- highlight(this);
- });
-};
-
-/**
- * Small JavaScript module for the documentation.
- */
-var Documentation = {
-
- init : function() {
- this.fixFirefoxAnchorBug();
- this.highlightSearchWords();
- this.initIndexTable();
- },
-
- /**
- * i18n support
- */
- TRANSLATIONS : {},
- PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; },
- LOCALE : 'unknown',
-
- // gettext and ngettext don't access this so that the functions
- // can safely bound to a different name (_ = Documentation.gettext)
- gettext : function(string) {
- var translated = Documentation.TRANSLATIONS[string];
- if (typeof translated == 'undefined')
- return string;
- return (typeof translated == 'string') ? translated : translated[0];
- },
-
- ngettext : function(singular, plural, n) {
- var translated = Documentation.TRANSLATIONS[singular];
- if (typeof translated == 'undefined')
- return (n == 1) ? singular : plural;
- return translated[Documentation.PLURALEXPR(n)];
- },
-
- addTranslations : function(catalog) {
- for (var key in catalog.messages)
- this.TRANSLATIONS[key] = catalog.messages[key];
- this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')');
- this.LOCALE = catalog.locale;
- },
-
- /**
- * add context elements like header anchor links
- */
- addContextElements : function() {
- $('div[id] > :header:first').each(function() {
- $('<a class="headerlink">\u00B6</a>').
- attr('href', '#' + this.id).
- attr('title', _('Permalink to this headline')).
- appendTo(this);
- });
- $('dt[id]').each(function() {
- $('<a class="headerlink">\u00B6</a>').
- attr('href', '#' + this.id).
- attr('title', _('Permalink to this definition')).
- appendTo(this);
- });
- },
-
- /**
- * workaround a firefox stupidity
- */
- fixFirefoxAnchorBug : function() {
- if (document.location.hash && $.browser.mozilla)
- window.setTimeout(function() {
- document.location.href += '';
- }, 10);
- },
-
- /**
- * highlight the search words provided in the url in the text
- */
- highlightSearchWords : function() {
- var params = $.getQueryParameters();
- var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
- if (terms.length) {
- var body = $('div.body');
- if (!body.length) {
- body = $('body');
- }
- window.setTimeout(function() {
- $.each(terms, function() {
- body.highlightText(this.toLowerCase(), 'highlighted');
- });
- }, 10);
- $('<p class="highlight-link"><a href="javascript:Documentation.' +
- 'hideSearchWords()">' + _('Hide Search Matches') + '</a></p>')
- .appendTo($('#searchbox'));
- }
- },
-
- /**
- * init the domain index toggle buttons
- */
- initIndexTable : function() {
- var togglers = $('img.toggler').click(function() {
- var src = $(this).attr('src');
- var idnum = $(this).attr('id').substr(7);
- $('tr.cg-' + idnum).toggle();
- if (src.substr(-9) == 'minus.png')
- $(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
- else
- $(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
- }).css('display', '');
- if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) {
- togglers.click();
- }
- },
-
- /**
- * helper function to hide the search marks again
- */
- hideSearchWords : function() {
- $('#searchbox .highlight-link').fadeOut(300);
- $('span.highlighted').removeClass('highlighted');
- },
-
- /**
- * make the url absolute
- */
- makeURL : function(relativeURL) {
- return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
- },
-
- /**
- * get the current relative url
- */
- getCurrentURL : function() {
- var path = document.location.pathname;
- var parts = path.split(/\//);
- $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
- if (this == '..')
- parts.pop();
- });
- var url = parts.join('/');
- return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
- }
-};
-
-// quick alias for translations
-_ = Documentation.gettext;
-
-$(document).ready(function() {
- Documentation.init();
-});
diff --git a/distro/common/html/_static/down-pressed.png b/distro/common/html/_static/down-pressed.png
deleted file mode 100644
index 6f7ad78278..0000000000
--- a/distro/common/html/_static/down-pressed.png
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/_static/down.png b/distro/common/html/_static/down.png
deleted file mode 100644
index 3003a88770..0000000000
--- a/distro/common/html/_static/down.png
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/_static/file.png b/distro/common/html/_static/file.png
deleted file mode 100644
index d18082e397..0000000000
--- a/distro/common/html/_static/file.png
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/_static/guide.css b/distro/common/html/_static/guide.css
deleted file mode 100644
index 2163fdc4f4..0000000000
--- a/distro/common/html/_static/guide.css
+++ /dev/null
@@ -1,505 +0,0 @@
-/*
- * opscode.css_t
- * ~~~~~~~~~~~~~~~
- *
- * Sphinx stylesheet -- opscode theme. Originally created by
- * Opscode for docs.opscode.com.
- *
- * :copyright: None.
- * :license: This work is licensed under a Creative Commons
- * Attribution 3.0 Unported License
- *
- */
-
-/*
- * Colors
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * #f18a20 is Opscode orange.
- * #212c35 is Opscode blue.
- *
- * #212c35 is used for text color and header colors
- * #f18a20 for header underlines and border treatments
- *
- * Triads for #f18a20: #273fa4 (blue) and #18b246 (green)
- * Triads for #212c35: #524a31 (tan) and #523a31 (salmon)
- * Triads should only be used to accent the Opscode blue or orange
- *
- * For div.admonition: #6bb1e1
- * For div.warning: #fcb614
- *
- * For subheaders and other treatments: #7c858c, then #b5bec6, then #d8dde3
- * For table headers: #7c858c (with #000000 text color)
- * For links: #6d3528, rollover #59b6b2
- *
- * Alternate colors in the official color palette (but try not to use): #59b6b2, #6d3528
- *
- * For regular text: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
- * For monospace text: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
- *
- * For div.related, the 8px ensures that the | is spaced evenly.
- *
- */
-
-@import url("basic.css");
-
-/* -- the background color for the whole page and the borders for the topic page, header, footer */
-/* -- IMPORTANT -- color is the color of the text on div.document; must be black or Opscode blue */
-
-body {
- font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
- font-size: 14px;
- letter-spacing: -0.00em;
- line-height: 150%;
- text-align: center;
- background-color: #7c858c;
- color: #212c35;
- padding: 0;
- border: 4px solid #7c858c;
- border-top: 0px solid #7c858c;
- border-bottom: 0px solid #7c858c;
- margin: 0px 80px 0px 80px;
- min-width: 740px;
-}
-
-
-
-/* -- the actual topic background, must be white all the time no exceptions */
-
-div.document {
- background-color: #ffffff;
- text-align: left;
- background-image: url(contents.png);
- background-repeat: repeat-x;
-}
-
-
-
-/* -- the vertical line between the topics and the TOC */
-
-div.bodywrapper {
- margin: 0 240px 0 0;
- border-right: 4px solid #d8dde3;
-}
-
-div.body {
- margin: 0;
- padding: 0.5em 20px 20px 20px;
-}
-
-div.related {
- font-size: 1em;
-}
-
-
-
-/* -- the borders above and below the header and footer */
-
-div.related ul {
- background-color: #ffffff;
- height: 1.8em;
- border-top: 4px solid #d8dde3;
- border-bottom: 4px solid #d8dde3;
-}
-
-div.related ul li {
- margin: 0;
- padding: 0;
- height: 2em;
- float: left;
-}
-
-div.related ul li.right {
- float: right;
- margin-right: 5px;
-}
-
-
-
-/* -- the Sphinx-specific navigation text located just below the header and just above the footer */
-
-div.related ul li a {
- margin: 0;
- padding: 0 8px 0 5px;
- line-height: 1.75em;
- color: #7c858c;
-}
-
-div.related ul li a:hover {
- color: #b5bec6;
-}
-
-
-div.sphinxsidebarwrapper {
- padding: 0;
-}
-
-div.sphinxsidebar {
- margin: 0;
- padding: 0.5em 15px 15px 0;
- width: 210px;
- float: right;
- font-size: 1em;
- text-align: left;
-
-}
-
-
-
-/* -- The colors of the TOC sidebar; h3 is headers, a is the text links and both should be same color */
-
-div.sphinxsidebar h3, div.sphinxsidebar h4 {
- margin: 1em 0 0.5em 0;
- font-size: 1em;
- padding: 0.1em 0 0.1em 0.5em;
- color: #ffffff;
- border: 1px solid #ffffff;
- background-color: #7c858c;
-}
-
-div.sphinxsidebar a {
- color: #212c35;
- padding: 0.1em 0 0.1em 0.5em;
- text-decoration: none;
-}
-
-div.sphinxsidebar h3 a {
- color: #ffffff;
-}
-
-div.sphinxsidebar ul {
- padding-left: 1.5em;
- margin-top: 7px;
- padding: 0;
- line-height: 130%;
-}
-
-div.sphinxsidebar ul ul {
- margin-left: 20px;
-}
-
-
-
-
-/*
- * footer
- * ~~~~~~~~~~~~~~~~~~~
- *
- */
-
-
-div.footer {
- background-color: #ffffff;
- color: #ffffff;
- padding: 3px 8px 3px 0;
- clear: both;
- font-size: 0.8em;
- text-align: right;
-}
-
-div.footer a {
- color: #ffffff;
- text-decoration: none;
-}
-
-/* -- body styles ----------------------------------------------------------- */
-
-p {
- margin: 0.8em 0 0.5em 0;
-}
-
-
-a {
- color: #4d97c6;
- text-decoration: none;
-}
-
-a:hover {
- color: #7c858c;
-}
-
-div.body a {
- text-decoration: none;
-}
-
-em {
- font-style: normal;
-}
-
-/* -- header styles, basically Opscode blue with colored underlines and decreasing border bottom sizes */
-
-h1 {
- margin: 0.5;
- padding: 0.7em 0 0.3em 0;
- font-size: 1.75em;
- color: #212c35;
- border-bottom:solid 4px #f18a20;
-}
-
-h2 {
- margin: 1.3em 0 0.2em 0;
- font-size: 1.55em;
- color: #212c35;
- padding: 0.7em 0 0.3em 0;
- border-bottom:solid 2px #7c858c;
-}
-
-h3 {
- margin: 1em 0 -0.3em 0;
- font-size: 1.35em;
- color: #212c35;
- padding: 0.7em 0 0.3em 0;
- border-bottom:solid 1px #7c858c;
-}
-
-h4 {
- margin: 1em 0 -0.3em 0;
- font-size: 1.15em;
- color: #212c35;
- padding: 0.7em 0 0.3em 0;
- border-bottom:solid 0px #7c858c;
-}
-
-div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a {
- color: #ffffff!important;
-}
-
-h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor {
- display: none;
- margin: 0 0 0 0.3em;
- padding: 0 0.2em 0 0.2em;
- color: #ffffff!important;
-}
-
-h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor,
-h5:hover a.anchor, h6:hover a.anchor {
- display: inline;
-}
-
-h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover,
-h5 a.anchor:hover, h6 a.anchor:hover {
- color: #ffffff;
- background-color: #ffffff;
-}
-
-
-
-
-a.headerlink {
- color: #ffffff!important;
- font-size: 1em;
- margin-left: 6px;
- padding: 0 4px 0 4px;
- text-decoration: none!important;
-}
-
-a.headerlink:hover {
- background-color: #ffffff;
- color: #ffffff!important;
-}
-
-cite, code, tt {
- font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
- font-size: 0.95em;
- letter-spacing: 0.01em;
-}
-
-
-
-
-
-tt {
- background-color: #ebecf1;
- border-bottom: 1px solid #d8dde3;
- color: #212c35;
-}
-
-tt.descname, tt.descclassname, tt.xref {
- border: 0;
-}
-
-/* -- has Opscode orange for testing */
-
-hr {
- border: 1px solid #f18a20;
- margin: 2em;
-}
-
-/* -- has Opscode orange for testing */
-
-a tt {
- border: 0;
- color: #f18a20;
-}
-
-/* -- has Opscode orange for testing */
-
-a tt:hover {
- color: #f18a20;
-}
-
-/* -- has Opscode orange for testing */
-
-pre {
- font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
- font-size: 0.95em;
- letter-spacing: 0.015em;
- line-height: 120%;
- padding: 0.5em;
- border: 0.0px solid #d8dde3;
- background-color: #ebecf1;
-}
-
-pre a {
- color: inherit;
- text-decoration: underline;
-}
-
-td.linenos pre {
- padding: 0.5em 0;
-}
-
-/* -- has Opscode orange for testing */
-
-div.quotebar {
- background-color: #f18a20;
- max-width: 250px;
- float: right;
- padding: 2px 7px;
- border: 1px solid #f18a20;
-}
-
-/* -- has Opscode orange for testing */
-
-div.topic {
- background-color: #f18a20;
-}
-
-
-/* -- table styles */
-
-table.docutils {
- border: 0;
- border-collapse: separate;
- border-spacing:4px;
-
-}
-
-table.docutils th {
- padding: 1px 8px 1px 5px;
- border-top: 0;
- border-left: 0;
- border-right: 0;
- border-bottom: 0px solid #ffffff;
- background-color: #7c858c;
- color: #ffffff;
-}
-
-table.docutils td {
- padding: 5px 5px 5px 5px;
- border-top: 0;
- border-left: 0;
- border-right: 0;
- border-bottom: 1px dashed #7c858c;
-}
-
-table.field-list td, table.field-list th {
- border: 0 !important;
-}
-
-table.footnote td, table.footnote th {
- border: 0 !important;
-}
-
-th {
- text-align: left;
- padding-right: 5px;
-}
-
-table.citation {
- border-left: solid 1px gray;
- margin-left: 1px;
-}
-
-table.citation td {
- border-bottom: none;
-}
-
-
-
-
-
-
-
-div.admonition, div.warning {
- font-size: 0.9em;
- margin: 1em 0 1em 0;
- border: 1px solid #6bb1e1;
- background-color: #ffffff;
- padding: 0;
-}
-
-div.admonition p, div.warning p {
- margin: 0.5em 1em 0.5em 1em;
- padding: 0;
-}
-
-div.admonition pre, div.warning pre {
- margin: 0.4em 1em 0.4em 1em;
-}
-
-div.admonition p.admonition-title,
-div.warning p.admonition-title {
- margin: 0;
- padding: 0.1em 0 0.1em 0.5em;
- color: #ffffff;
- border-bottom: 1px solid #6bb1e1;
- font-weight: bold;
- background-color: #6bb1e1;
-}
-
-
-
-
-div.warning {
- border: 1px solid #fcb614;
-}
-
-div.warning p.admonition-title {
- background-color: #fcb614;
- border-bottom-color: #fcb614;
-}
-
-div.admonition ul, div.admonition ol,
-div.warning ul, div.warning ol {
- margin: 0.1em 0.5em 0.5em 3em;
- padding: 0;
-}
-
-
-
-
-div.versioninfo {
- margin: 1em 0 0 0;
- border: 1px solid #ffffff;
- background-color: #ffffff;
- padding: 8px;
- line-height: 1.3em;
- font-size: 0.9em;
-}
-
-
-
-
-.viewcode-back {
- font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
-}
-
-
-
-
-div.viewcode-block:target {
- background-color: #ffffff;
- border-top: 1px solid #ffffff;
- border-bottom: 1px solid #ffffff;
-} \ No newline at end of file
diff --git a/distro/common/html/_static/jquery.js b/distro/common/html/_static/jquery.js
deleted file mode 100644
index 83589daa70..0000000000
--- a/distro/common/html/_static/jquery.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! jQuery v1.8.3 jquery.com | jquery.org/license */
-(function(e,t){function _(e){var t=M[e]={};return v.each(e.split(y),function(e,n){t[n]=!0}),t}function H(e,n,r){if(r===t&&e.nodeType===1){var i="data-"+n.replace(P,"-$1").toLowerCase();r=e.getAttribute(i);if(typeof r=="string"){try{r=r==="true"?!0:r==="false"?!1:r==="null"?null:+r+""===r?+r:D.test(r)?v.parseJSON(r):r}catch(s){}v.data(e,n,r)}else r=t}return r}function B(e){var t;for(t in e){if(t==="data"&&v.isEmptyObject(e[t]))continue;if(t!=="toJSON")return!1}return!0}function et(){return!1}function tt(){return!0}function ut(e){return!e||!e.parentNode||e.parentNode.nodeType===11}function at(e,t){do e=e[t];while(e&&e.nodeType!==1);return e}function ft(e,t,n){t=t||0;if(v.isFunction(t))return v.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return v.grep(e,function(e,r){return e===t===n});if(typeof t=="string"){var r=v.grep(e,function(e){return e.nodeType===1});if(it.test(t))return v.filter(t,r,!n);t=v.filter(t,r)}return v.grep(e,function(e,r){return v.inArray(e,t)>=0===n})}function lt(e){var t=ct.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function At(e,t){if(t.nodeType!==1||!v.hasData(e))return;var n,r,i,s=v._data(e),o=v._data(t,s),u=s.events;if(u){delete o.handle,o.events={};for(n in u)for(r=0,i=u[n].length;r<i;r++)v.event.add(t,n,u[n][r])}o.data&&(o.data=v.extend({},o.data))}function Ot(e,t){var n;if(t.nodeType!==1)return;t.clearAttributes&&t.clearAttributes(),t.mergeAttributes&&t.mergeAttributes(e),n=t.nodeName.toLowerCase(),n==="object"?(t.parentNode&&(t.outerHTML=e.outerHTML),v.support.html5Clone&&e.innerHTML&&!v.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):n==="input"&&Et.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):n==="option"?t.selected=e.defaultSelected:n==="input"||n==="textarea"?t.defaultValue=e.defaultValue:n==="script"&&t.text!==e.text&&(t.text=e.text),t.removeAttribute(v.expando)}function Mt(e){return typeof e.getElementsByTagName!="undefined"?e.getElementsByTagName("*"):typeof e.querySelectorAll!="undefined"?e.querySelectorAll("*"):[]}function _t(e){Et.test(e.type)&&(e.defaultChecked=e.checked)}function Qt(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=Jt.length;while(i--){t=Jt[i]+n;if(t in e)return t}return r}function Gt(e,t){return e=t||e,v.css(e,"display")==="none"||!v.contains(e.ownerDocument,e)}function Yt(e,t){var n,r,i=[],s=0,o=e.length;for(;s<o;s++){n=e[s];if(!n.style)continue;i[s]=v._data(n,"olddisplay"),t?(!i[s]&&n.style.display==="none"&&(n.style.display=""),n.style.display===""&&Gt(n)&&(i[s]=v._data(n,"olddisplay",nn(n.nodeName)))):(r=Dt(n,"display"),!i[s]&&r!=="none"&&v._data(n,"olddisplay",r))}for(s=0;s<o;s++){n=e[s];if(!n.style)continue;if(!t||n.style.display==="none"||n.style.display==="")n.style.display=t?i[s]||"":"none"}return e}function Zt(e,t,n){var r=Rt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function en(e,t,n,r){var i=n===(r?"border":"content")?4:t==="width"?1:0,s=0;for(;i<4;i+=2)n==="margin"&&(s+=v.css(e,n+$t[i],!0)),r?(n==="content"&&(s-=parseFloat(Dt(e,"padding"+$t[i]))||0),n!=="margin"&&(s-=parseFloat(Dt(e,"border"+$t[i]+"Width"))||0)):(s+=parseFloat(Dt(e,"padding"+$t[i]))||0,n!=="padding"&&(s+=parseFloat(Dt(e,"border"+$t[i]+"Width"))||0));return s}function tn(e,t,n){var r=t==="width"?e.offsetWidth:e.offsetHeight,i=!0,s=v.support.boxSizing&&v.css(e,"boxSizing")==="border-box";if(r<=0||r==null){r=Dt(e,t);if(r<0||r==null)r=e.style[t];if(Ut.test(r))return r;i=s&&(v.support.boxSizingReliable||r===e.style[t]),r=parseFloat(r)||0}return r+en(e,t,n||(s?"border":"content"),i)+"px"}function nn(e){if(Wt[e])return Wt[e];var t=v("<"+e+">").appendTo(i.body),n=t.css("display");t.remove();if(n==="none"||n===""){Pt=i.body.appendChild(Pt||v.extend(i.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!Ht||!Pt.createElement)Ht=(Pt.contentWindow||Pt.contentDocument).document,Ht.write("<!doctype html><html><body>"),Ht.close();t=Ht.body.appendChild(Ht.createElement(e)),n=Dt(t,"display"),i.body.removeChild(Pt)}return Wt[e]=n,n}function fn(e,t,n,r){var i;if(v.isArray(t))v.each(t,function(t,i){n||sn.test(e)?r(e,i):fn(e+"["+(typeof i=="object"?t:"")+"]",i,n,r)});else if(!n&&v.type(t)==="object")for(i in t)fn(e+"["+i+"]",t[i],n,r);else r(e,t)}function Cn(e){return function(t,n){typeof t!="string"&&(n=t,t="*");var r,i,s,o=t.toLowerCase().split(y),u=0,a=o.length;if(v.isFunction(n))for(;u<a;u++)r=o[u],s=/^\+/.test(r),s&&(r=r.substr(1)||"*"),i=e[r]=e[r]||[],i[s?"unshift":"push"](n)}}function kn(e,n,r,i,s,o){s=s||n.dataTypes[0],o=o||{},o[s]=!0;var u,a=e[s],f=0,l=a?a.length:0,c=e===Sn;for(;f<l&&(c||!u);f++)u=a[f](n,r,i),typeof u=="string"&&(!c||o[u]?u=t:(n.dataTypes.unshift(u),u=kn(e,n,r,i,u,o)));return(c||!u)&&!o["*"]&&(u=kn(e,n,r,i,"*",o)),u}function Ln(e,n){var r,i,s=v.ajaxSettings.flatOptions||{};for(r in n)n[r]!==t&&((s[r]?e:i||(i={}))[r]=n[r]);i&&v.extend(!0,e,i)}function An(e,n,r){var i,s,o,u,a=e.contents,f=e.dataTypes,l=e.responseFields;for(s in l)s in r&&(n[l[s]]=r[s]);while(f[0]==="*")f.shift(),i===t&&(i=e.mimeType||n.getResponseHeader("content-type"));if(i)for(s in a)if(a[s]&&a[s].test(i)){f.unshift(s);break}if(f[0]in r)o=f[0];else{for(s in r){if(!f[0]||e.converters[s+" "+f[0]]){o=s;break}u||(u=s)}o=o||u}if(o)return o!==f[0]&&f.unshift(o),r[o]}function On(e,t){var n,r,i,s,o=e.dataTypes.slice(),u=o[0],a={},f=0;e.dataFilter&&(t=e.dataFilter(t,e.dataType));if(o[1])for(n in e.converters)a[n.toLowerCase()]=e.converters[n];for(;i=o[++f];)if(i!=="*"){if(u!=="*"&&u!==i){n=a[u+" "+i]||a["* "+i];if(!n)for(r in a){s=r.split(" ");if(s[1]===i){n=a[u+" "+s[0]]||a["* "+s[0]];if(n){n===!0?n=a[r]:a[r]!==!0&&(i=s[0],o.splice(f--,0,i));break}}}if(n!==!0)if(n&&e["throws"])t=n(t);else try{t=n(t)}catch(l){return{state:"parsererror",error:n?l:"No conversion from "+u+" to "+i}}}u=i}return{state:"success",data:t}}function Fn(){try{return new e.XMLHttpRequest}catch(t){}}function In(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}function $n(){return setTimeout(function(){qn=t},0),qn=v.now()}function Jn(e,t){v.each(t,function(t,n){var r=(Vn[t]||[]).concat(Vn["*"]),i=0,s=r.length;for(;i<s;i++)if(r[i].call(e,t,n))return})}function Kn(e,t,n){var r,i=0,s=0,o=Xn.length,u=v.Deferred().always(function(){delete a.elem}),a=function(){var t=qn||$n(),n=Math.max(0,f.startTime+f.duration-t),r=n/f.duration||0,i=1-r,s=0,o=f.tweens.length;for(;s<o;s++)f.tweens[s].run(i);return u.notifyWith(e,[f,i,n]),i<1&&o?n:(u.resolveWith(e,[f]),!1)},f=u.promise({elem:e,props:v.extend({},t),opts:v.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:qn||$n(),duration:n.duration,tweens:[],createTween:function(t,n,r){var i=v.Tween(e,f.opts,t,n,f.opts.specialEasing[t]||f.opts.easing);return f.tweens.push(i),i},stop:function(t){var n=0,r=t?f.tweens.length:0;for(;n<r;n++)f.tweens[n].run(1);return t?u.resolveWith(e,[f,t]):u.rejectWith(e,[f,t]),this}}),l=f.props;Qn(l,f.opts.specialEasing);for(;i<o;i++){r=Xn[i].call(f,e,l,f.opts);if(r)return r}return Jn(f,l),v.isFunction(f.opts.start)&&f.opts.start.call(e,f),v.fx.timer(v.extend(a,{anim:f,queue:f.opts.queue,elem:e})),f.progress(f.opts.progress).done(f.opts.done,f.opts.complete).fail(f.opts.fail).always(f.opts.always)}function Qn(e,t){var n,r,i,s,o;for(n in e){r=v.camelCase(n),i=t[r],s=e[n],v.isArray(s)&&(i=s[1],s=e[n]=s[0]),n!==r&&(e[r]=s,delete e[n]),o=v.cssHooks[r];if(o&&"expand"in o){s=o.expand(s),delete e[r];for(n in s)n in e||(e[n]=s[n],t[n]=i)}else t[r]=i}}function Gn(e,t,n){var r,i,s,o,u,a,f,l,c,h=this,p=e.style,d={},m=[],g=e.nodeType&&Gt(e);n.queue||(l=v._queueHooks(e,"fx"),l.unqueued==null&&(l.unqueued=0,c=l.empty.fire,l.empty.fire=function(){l.unqueued||c()}),l.unqueued++,h.always(function(){h.always(function(){l.unqueued--,v.queue(e,"fx").length||l.empty.fire()})})),e.nodeType===1&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],v.css(e,"display")==="inline"&&v.css(e,"float")==="none"&&(!v.support.inlineBlockNeedsLayout||nn(e.nodeName)==="inline"?p.display="inline-block":p.zoom=1)),n.overflow&&(p.overflow="hidden",v.support.shrinkWrapBlocks||h.done(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t){s=t[r];if(Un.exec(s)){delete t[r],a=a||s==="toggle";if(s===(g?"hide":"show"))continue;m.push(r)}}o=m.length;if(o){u=v._data(e,"fxshow")||v._data(e,"fxshow",{}),"hidden"in u&&(g=u.hidden),a&&(u.hidden=!g),g?v(e).show():h.done(function(){v(e).hide()}),h.done(function(){var t;v.removeData(e,"fxshow",!0);for(t in d)v.style(e,t,d[t])});for(r=0;r<o;r++)i=m[r],f=h.createTween(i,g?u[i]:0),d[i]=u[i]||v.style(e,i),i in u||(u[i]=f.start,g&&(f.end=f.start,f.start=i==="width"||i==="height"?1:0))}}function Yn(e,t,n,r,i){return new Yn.prototype.init(e,t,n,r,i)}function Zn(e,t){var n,r={height:e},i=0;t=t?1:0;for(;i<4;i+=2-t)n=$t[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}function tr(e){return v.isWindow(e)?e:e.nodeType===9?e.defaultView||e.parentWindow:!1}var n,r,i=e.document,s=e.location,o=e.navigator,u=e.jQuery,a=e.$,f=Array.prototype.push,l=Array.prototype.slice,c=Array.prototype.indexOf,h=Object.prototype.toString,p=Object.prototype.hasOwnProperty,d=String.prototype.trim,v=function(e,t){return new v.fn.init(e,t,n)},m=/[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,g=/\S/,y=/\s+/,b=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,w=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,E=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,S=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,T=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,N=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,C=/^-ms-/,k=/-([\da-z])/gi,L=function(e,t){return(t+"").toUpperCase()},A=function(){i.addEventListener?(i.removeEventListener("DOMContentLoaded",A,!1),v.ready()):i.readyState==="complete"&&(i.detachEvent("onreadystatechange",A),v.ready())},O={};v.fn=v.prototype={constructor:v,init:function(e,n,r){var s,o,u,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)==="<"&&e.charAt(e.length-1)===">"&&e.length>=3?s=[null,e,null]:s=w.exec(e);if(s&&(s[1]||!n)){if(s[1])return n=n instanceof v?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:i,e=v.parseHTML(s[1],a,!0),E.test(s[1])&&v.isPlainObject(n)&&this.attr.call(e,n,!0),v.merge(this,e);o=i.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return r.find(e);this.length=1,this[0]=o}return this.context=i,this.selector=e,this}return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e)}return v.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),v.makeArray(e,this))},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return l.call(this)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=v.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,t==="find"?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return v.each(this,e,t)},ready:function(e){return v.ready.promise().done(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(l.apply(this,arguments),"slice",l.call(arguments).join(","))},map:function(e){return this.pushStack(v.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},v.fn.init.prototype=v.fn,v.extend=v.fn.extend=function(){var e,n,r,i,s,o,u=arguments[0]||{},a=1,f=arguments.length,l=!1;typeof u=="boolean"&&(l=u,u=arguments[1]||{},a=2),typeof u!="object"&&!v.isFunction(u)&&(u={}),f===a&&(u=this,--a);for(;a<f;a++)if((e=arguments[a])!=null)for(n in e){r=u[n],i=e[n];if(u===i)continue;l&&i&&(v.isPlainObject(i)||(s=v.isArray(i)))?(s?(s=!1,o=r&&v.isArray(r)?r:[]):o=r&&v.isPlainObject(r)?r:{},u[n]=v.extend(l,o,i)):i!==t&&(u[n]=i)}return u},v.extend({noConflict:function(t){return e.$===v&&(e.$=a),t&&e.jQuery===v&&(e.jQuery=u),v},isReady:!1,readyWait:1,holdReady:function(e){e?v.readyWait++:v.ready(!0)},ready:function(e){if(e===!0?--v.readyWait:v.isReady)return;if(!i.body)return setTimeout(v.ready,1);v.isReady=!0;if(e!==!0&&--v.readyWait>0)return;r.resolveWith(i,[v]),v.fn.trigger&&v(i).trigger("ready").off("ready")},isFunction:function(e){return v.type(e)==="function"},isArray:Array.isArray||function(e){return v.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):O[h.call(e)]||"object"},isPlainObject:function(e){if(!e||v.type(e)!=="object"||e.nodeType||v.isWindow(e))return!1;try{if(e.constructor&&!p.call(e,"constructor")&&!p.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||p.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw new Error(e)},parseHTML:function(e,t,n){var r;return!e||typeof e!="string"?null:(typeof t=="boolean"&&(n=t,t=0),t=t||i,(r=E.exec(e))?[t.createElement(r[1])]:(r=v.buildFragment([e],t,n?null:[]),v.merge([],(r.cacheable?v.clone(r.fragment):r.fragment).childNodes)))},parseJSON:function(t){if(!t||typeof t!="string")return null;t=v.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(S.test(t.replace(T,"@").replace(N,"]").replace(x,"")))return(new Function("return "+t))();v.error("Invalid JSON: "+t)},parseXML:function(n){var r,i;if(!n||typeof n!="string")return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(s){r=t}return(!r||!r.documentElement||r.getElementsByTagName("parsererror").length)&&v.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&g.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(C,"ms-").replace(k,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var i,s=0,o=e.length,u=o===t||v.isFunction(e);if(r){if(u){for(i in e)if(n.apply(e[i],r)===!1)break}else for(;s<o;)if(n.apply(e[s++],r)===!1)break}else if(u){for(i in e)if(n.call(e[i],i,e[i])===!1)break}else for(;s<o;)if(n.call(e[s],s,e[s++])===!1)break;return e},trim:d&&!d.call("\ufeff\u00a0")?function(e){return e==null?"":d.call(e)}:function(e){return e==null?"":(e+"").replace(b,"")},makeArray:function(e,t){var n,r=t||[];return e!=null&&(n=v.type(e),e.length==null||n==="string"||n==="function"||n==="regexp"||v.isWindow(e)?f.call(r,e):v.merge(r,e)),r},inArray:function(e,t,n){var r;if(t){if(c)return c.call(t,e,n);r=t.length,n=n?n<0?Math.max(0,r+n):n:0;for(;n<r;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,s=0;if(typeof r=="number")for(;s<r;s++)e[i++]=n[s];else while(n[s]!==t)e[i++]=n[s++];return e.length=i,e},grep:function(e,t,n){var r,i=[],s=0,o=e.length;n=!!n;for(;s<o;s++)r=!!t(e[s],s),n!==r&&i.push(e[s]);return i},map:function(e,n,r){var i,s,o=[],u=0,a=e.length,f=e instanceof v||a!==t&&typeof a=="number"&&(a>0&&e[0]&&e[a-1]||a===0||v.isArray(e));if(f)for(;u<a;u++)i=n(e[u],u,r),i!=null&&(o[o.length]=i);else for(s in e)i=n(e[s],s,r),i!=null&&(o[o.length]=i);return o.concat.apply([],o)},guid:1,proxy:function(e,n){var r,i,s;return typeof n=="string"&&(r=e[n],n=e,e=r),v.isFunction(e)?(i=l.call(arguments,2),s=function(){return e.apply(n,i.concat(l.call(arguments)))},s.guid=e.guid=e.guid||v.guid++,s):t},access:function(e,n,r,i,s,o,u){var a,f=r==null,l=0,c=e.length;if(r&&typeof r=="object"){for(l in r)v.access(e,n,l,r[l],1,o,i);s=1}else if(i!==t){a=u===t&&v.isFunction(i),f&&(a?(a=n,n=function(e,t,n){return a.call(v(e),n)}):(n.call(e,i),n=null));if(n)for(;l<c;l++)n(e[l],r,a?i.call(e[l],l,n(e[l],r)):i,u);s=1}return s?e:f?n.call(e):c?n(e[0],r):o},now:function(){return(new Date).getTime()}}),v.ready.promise=function(t){if(!r){r=v.Deferred();if(i.readyState==="complete")setTimeout(v.ready,1);else if(i.addEventListener)i.addEventListener("DOMContentLoaded",A,!1),e.addEventListener("load",v.ready,!1);else{i.attachEvent("onreadystatechange",A),e.attachEvent("onload",v.ready);var n=!1;try{n=e.frameElement==null&&i.documentElement}catch(s){}n&&n.doScroll&&function o(){if(!v.isReady){try{n.doScroll("left")}catch(e){return setTimeout(o,50)}v.ready()}}()}}return r.promise(t)},v.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(e,t){O["[object "+t+"]"]=t.toLowerCase()}),n=v(i);var M={};v.Callbacks=function(e){e=typeof e=="string"?M[e]||_(e):v.extend({},e);var n,r,i,s,o,u,a=[],f=!e.once&&[],l=function(t){n=e.memory&&t,r=!0,u=s||0,s=0,o=a.length,i=!0;for(;a&&u<o;u++)if(a[u].apply(t[0],t[1])===!1&&e.stopOnFalse){n=!1;break}i=!1,a&&(f?f.length&&l(f.shift()):n?a=[]:c.disable())},c={add:function(){if(a){var t=a.length;(function r(t){v.each(t,function(t,n){var i=v.type(n);i==="function"?(!e.unique||!c.has(n))&&a.push(n):n&&n.length&&i!=="string"&&r(n)})})(arguments),i?o=a.length:n&&(s=t,l(n))}return this},remove:function(){return a&&v.each(arguments,function(e,t){var n;while((n=v.inArray(t,a,n))>-1)a.splice(n,1),i&&(n<=o&&o--,n<=u&&u--)}),this},has:function(e){return v.inArray(e,a)>-1},empty:function(){return a=[],this},disable:function(){return a=f=n=t,this},disabled:function(){return!a},lock:function(){return f=t,n||c.disable(),this},locked:function(){return!f},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],a&&(!r||f)&&(i?f.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},v.extend({Deferred:function(e){var t=[["resolve","done",v.Callbacks("once memory"),"resolved"],["reject","fail",v.Callbacks("once memory"),"rejected"],["notify","progress",v.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return v.Deferred(function(n){v.each(t,function(t,r){var s=r[0],o=e[t];i[r[1]](v.isFunction(o)?function(){var e=o.apply(this,arguments);e&&v.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===i?n:this,[e])}:n[s])}),e=null}).promise()},promise:function(e){return e!=null?v.extend(e,r):r}},i={};return r.pipe=r.then,v.each(t,function(e,s){var o=s[2],u=s[3];r[s[1]]=o.add,u&&o.add(function(){n=u},t[e^1][2].disable,t[2][2].lock),i[s[0]]=o.fire,i[s[0]+"With"]=o.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=l.call(arguments),r=n.length,i=r!==1||e&&v.isFunction(e.promise)?r:0,s=i===1?e:v.Deferred(),o=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?l.call(arguments):r,n===u?s.notifyWith(t,n):--i||s.resolveWith(t,n)}},u,a,f;if(r>1){u=new Array(r),a=new Array(r),f=new Array(r);for(;t<r;t++)n[t]&&v.isFunction(n[t].promise)?n[t].promise().done(o(t,f,n)).fail(s.reject).progress(o(t,a,u)):--i}return i||s.resolveWith(f,n),s.promise()}}),v.support=function(){var t,n,r,s,o,u,a,f,l,c,h,p=i.createElement("div");p.setAttribute("className","t"),p.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!r||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="<table><tr><td></td><td>t</td></tr></table>",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="<div></div>",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i<s;i++)delete r[t[i]];if(!(n?B:v.isEmptyObject)(r))return}}if(!n){delete u[a].data;if(!B(u[a]))return}o?v.cleanData([e],!0):v.support.deleteExpando||u!=u.window?delete u[a]:u[a]=null},_data:function(e,t,n){return v.data(e,t,n,!0)},acceptData:function(e){var t=e.nodeName&&v.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),v.fn.extend({data:function(e,n){var r,i,s,o,u,a=this[0],f=0,l=null;if(e===t){if(this.length){l=v.data(a);if(a.nodeType===1&&!v._data(a,"parsedAttrs")){s=a.attributes;for(u=s.length;f<u;f++)o=s[f].name,o.indexOf("data-")||(o=v.camelCase(o.substring(5)),H(a,o,l[o]));v._data(a,"parsedAttrs",!0)}}return l}return typeof e=="object"?this.each(function(){v.data(this,e)}):(r=e.split(".",2),r[1]=r[1]?"."+r[1]:"",i=r[1]+"!",v.access(this,function(n){if(n===t)return l=this.triggerHandler("getData"+i,[r[0]]),l===t&&a&&(l=v.data(a,e),l=H(a,e,l)),l===t&&r[1]?this.data(r[0]):l;r[1]=n,this.each(function(){var t=v(this);t.triggerHandler("setData"+i,r),v.data(this,e,n),t.triggerHandler("changeData"+i,r)})},null,n,arguments.length>1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length<r?v.queue(this[0],e):n===t?this:this.each(function(){var t=v.queue(this,e,n);v._queueHooks(this,e),e==="fx"&&t[0]!=="inprogress"&&v.dequeue(this,e)})},dequeue:function(e){return this.each(function(){v.dequeue(this,e)})},delay:function(e,t){return e=v.fx?v.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,s=v.Deferred(),o=this,u=this.length,a=function(){--i||s.resolveWith(o,[o])};typeof e!="string"&&(n=e,e=t),e=e||"fx";while(u--)r=v._data(o[u],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(a));return a(),s.promise(n)}});var j,F,I,q=/[\t\r\n]/g,R=/\r/g,U=/^(?:button|input)$/i,z=/^(?:button|input|object|select|textarea)$/i,W=/^a(?:rea|)$/i,X=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,V=v.support.getSetAttribute;v.fn.extend({attr:function(e,t){return v.access(this,v.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n<r;n++){i=this[n];if(i.nodeType===1)if(!i.className&&t.length===1)i.className=e;else{s=" "+i.className+" ";for(o=0,u=t.length;o<u;o++)s.indexOf(" "+t[o]+" ")<0&&(s+=t[o]+" ");i.className=v.trim(s)}}}return this},removeClass:function(e){var n,r,i,s,o,u,a;if(v.isFunction(e))return this.each(function(t){v(this).removeClass(e.call(this,t,this.className))});if(e&&typeof e=="string"||e===t){n=(e||"").split(y);for(u=0,a=this.length;u<a;u++){i=this[u];if(i.nodeType===1&&i.className){r=(" "+i.className+" ").replace(q," ");for(s=0,o=n.length;s<o;s++)while(r.indexOf(" "+n[s]+" ")>=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n<r;n++)if(this[n].nodeType===1&&(" "+this[n].className+" ").replace(q," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,s=e.type==="select-one"||i<0,o=s?null:[],u=s?i+1:r.length,a=i<0?u:s?i:0;for(;a<u;a++){n=r[a];if((n.selected||a===i)&&(v.support.optDisabled?!n.disabled:n.getAttribute("disabled")===null)&&(!n.parentNode.disabled||!v.nodeName(n.parentNode,"optgroup"))){t=v(n).val();if(s)return t;o.push(t)}}return o},set:function(e,t){var n=v.makeArray(t);return v(e).find("option").each(function(){this.selected=v.inArray(v(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o<r.length;o++)i=r[o],i&&(n=v.propFix[i]||i,s=X.test(i),s||v.attr(e,i,""),e.removeAttribute(V?i:n),s&&n in e&&(e[n]=!1))}},attrHooks:{type:{set:function(e,t){if(U.test(e.nodeName)&&e.parentNode)v.error("type property can't be changed");else if(!v.support.radioValue&&t==="radio"&&v.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}},value:{get:function(e,t){return j&&v.nodeName(e,"button")?j.get(e,t):t in e?e.value:null},set:function(e,t,n){if(j&&v.nodeName(e,"button"))return j.set(e,t,n);e.value=t}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,s,o,u=e.nodeType;if(!e||u===3||u===8||u===2)return;return o=u!==1||!v.isXMLDoc(e),o&&(n=v.propFix[n]||n,s=v.propHooks[n]),r!==t?s&&"set"in s&&(i=s.set(e,r,n))!==t?i:e[n]=r:s&&"get"in s&&(i=s.get(e,n))!==null?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):z.test(e.nodeName)||W.test(e.nodeName)&&e.href?0:t}}}}),F={get:function(e,n){var r,i=v.prop(e,n);return i===!0||typeof i!="boolean"&&(r=e.getAttributeNode(n))&&r.nodeValue!==!1?n.toLowerCase():t},set:function(e,t,n){var r;return t===!1?v.removeAttr(e,n):(r=v.propFix[n]||n,r in e&&(e[r]=!0),e.setAttribute(n,n.toLowerCase())),n}},V||(I={name:!0,id:!0,coords:!0},j=v.valHooks.button={get:function(e,n){var r;return r=e.getAttributeNode(n),r&&(I[n]?r.value!=="":r.specified)?r.value:t},set:function(e,t,n){var r=e.getAttributeNode(n);return r||(r=i.createAttribute(n),e.setAttributeNode(r)),r.value=t+""}},v.each(["width","height"],function(e,t){v.attrHooks[t]=v.extend(v.attrHooks[t],{set:function(e,n){if(n==="")return e.setAttribute(t,"auto"),n}})}),v.attrHooks.contenteditable={get:j.get,set:function(e,t,n){t===""&&(t="false"),j.set(e,t,n)}}),v.support.hrefNormalized||v.each(["href","src","width","height"],function(e,n){v.attrHooks[n]=v.extend(v.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return r===null?t:r}})}),v.support.style||(v.attrHooks.style={get:function(e){return e.style.cssText.toLowerCase()||t},set:function(e,t){return e.style.cssText=t+""}}),v.support.optSelected||(v.propHooks.selected=v.extend(v.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),v.support.enctype||(v.propFix.enctype="encoding"),v.support.checkOn||v.each(["radio","checkbox"],function(){v.valHooks[this]={get:function(e){return e.getAttribute("value")===null?"on":e.value}}}),v.each(["radio","checkbox"],function(){v.valHooks[this]=v.extend(v.valHooks[this],{set:function(e,t){if(v.isArray(t))return e.checked=v.inArray(v(e).val(),t)>=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f<n.length;f++){l=J.exec(n[f])||[],c=l[1],h=(l[2]||"").split(".").sort(),g=v.event.special[c]||{},c=(s?g.delegateType:g.bindType)||c,g=v.event.special[c]||{},p=v.extend({type:c,origType:l[1],data:i,handler:r,guid:r.guid,selector:s,needsContext:s&&v.expr.match.needsContext.test(s),namespace:h.join(".")},d),m=a[c];if(!m){m=a[c]=[],m.delegateCount=0;if(!g.setup||g.setup.call(e,i,h,u)===!1)e.addEventListener?e.addEventListener(c,u,!1):e.attachEvent&&e.attachEvent("on"+c,u)}g.add&&(g.add.call(e,p),p.handler.guid||(p.handler.guid=r.guid)),s?m.splice(m.delegateCount++,0,p):m.push(p),v.event.global[c]=!0}e=null},global:{},remove:function(e,t,n,r,i){var s,o,u,a,f,l,c,h,p,d,m,g=v.hasData(e)&&v._data(e);if(!g||!(h=g.events))return;t=v.trim(Z(t||"")).split(" ");for(s=0;s<t.length;s++){o=J.exec(t[s])||[],u=a=o[1],f=o[2];if(!u){for(u in h)v.event.remove(e,u+t[s],n,r,!0);continue}p=v.event.special[u]||{},u=(r?p.delegateType:p.bindType)||u,d=h[u]||[],l=d.length,f=f?new RegExp("(^|\\.)"+f.split(".").sort().join("\\.(?:.*\\.|)")+"(\\.|$)"):null;for(c=0;c<d.length;c++)m=d[c],(i||a===m.origType)&&(!n||n.guid===m.guid)&&(!f||f.test(m.namespace))&&(!r||r===m.selector||r==="**"&&m.selector)&&(d.splice(c--,1),m.selector&&d.delegateCount--,p.remove&&p.remove.call(e,m));d.length===0&&l!==d.length&&((!p.teardown||p.teardown.call(e,f,g.handle)===!1)&&v.removeEvent(e,u,g.handle),delete h[u])}v.isEmptyObject(h)&&(delete g.handle,v.removeData(e,"events",!0))},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(n,r,s,o){if(!s||s.nodeType!==3&&s.nodeType!==8){var u,a,f,l,c,h,p,d,m,g,y=n.type||n,b=[];if(Y.test(y+v.event.triggered))return;y.indexOf("!")>=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f<m.length&&!n.isPropagationStopped();f++)l=m[f][0],n.type=m[f][1],d=(v._data(l,"events")||{})[n.type]&&v._data(l,"handle"),d&&d.apply(l,r),d=h&&l[h],d&&v.acceptData(l)&&d.apply&&d.apply(l,r)===!1&&n.preventDefault();return n.type=y,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(s.ownerDocument,r)===!1)&&(y!=="click"||!v.nodeName(s,"a"))&&v.acceptData(s)&&h&&s[y]&&(y!=="focus"&&y!=="blur"||n.target.offsetWidth!==0)&&!v.isWindow(s)&&(c=s[h],c&&(s[h]=null),v.event.triggered=y,s[y](),v.event.triggered=t,c&&(s[h]=c)),n.result}return},dispatch:function(n){n=v.event.fix(n||e.event);var r,i,s,o,u,a,f,c,h,p,d=(v._data(this,"events")||{})[n.type]||[],m=d.delegateCount,g=l.call(arguments),y=!n.exclusive&&!n.namespace,b=v.event.special[n.type]||{},w=[];g[0]=n,n.delegateTarget=this;if(b.preDispatch&&b.preDispatch.call(this,n)===!1)return;if(m&&(!n.button||n.type!=="click"))for(s=n.target;s!=this;s=s.parentNode||this)if(s.disabled!==!0||n.type!=="click"){u={},f=[];for(r=0;r<m;r++)c=d[r],h=c.selector,u[h]===t&&(u[h]=c.needsContext?v(h,this).index(s)>=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r<w.length&&!n.isPropagationStopped();r++){a=w[r],n.currentTarget=a.elem;for(i=0;i<a.matches.length&&!n.isImmediatePropagationStopped();i++){c=a.matches[i];if(y||!n.namespace&&!c.namespace||n.namespace_re&&n.namespace_re.test(c.namespace))n.data=c.data,n.handleObj=c,o=((v.event.special[c.origType]||{}).handle||c.handler).apply(a.elem,g),o!==t&&(n.result=o,o===!1&&(n.preventDefault(),n.stopPropagation()))}}return b.postDispatch&&b.postDispatch.call(this,n),n.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return e.which==null&&(e.which=t.charCode!=null?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,s,o,u=n.button,a=n.fromElement;return e.pageX==null&&n.clientX!=null&&(r=e.target.ownerDocument||i,s=r.documentElement,o=r.body,e.pageX=n.clientX+(s&&s.scrollLeft||o&&o.scrollLeft||0)-(s&&s.clientLeft||o&&o.clientLeft||0),e.pageY=n.clientY+(s&&s.scrollTop||o&&o.scrollTop||0)-(s&&s.clientTop||o&&o.clientTop||0)),!e.relatedTarget&&a&&(e.relatedTarget=a===e.target?n.toElement:a),!e.which&&u!==t&&(e.which=u&1?1:u&2?3:u&4?2:0),e}},fix:function(e){if(e[v.expando])return e;var t,n,r=e,s=v.event.fixHooks[e.type]||{},o=s.props?this.props.concat(s.props):this.props;e=v.Event(r);for(t=o.length;t;)n=o[--t],e[n]=r[n];return e.target||(e.target=r.srcElement||i),e.target.nodeType===3&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,r):e},special:{load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(e,t,n){v.isWindow(this)&&(this.onbeforeunload=n)},teardown:function(e,t){this.onbeforeunload===t&&(this.onbeforeunload=null)}}},simulate:function(e,t,n,r){var i=v.extend(new v.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?v.event.trigger(i,null,t):v.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},v.event.handle=v.event.dispatch,v.removeEvent=i.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]=="undefined"&&(e[r]=null),e.detachEvent(r,n))},v.Event=function(e,t){if(!(this instanceof v.Event))return new v.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?tt:et):this.type=e,t&&v.extend(this,t),this.timeStamp=e&&e.timeStamp||v.now(),this[v.expando]=!0},v.Event.prototype={preventDefault:function(){this.isDefaultPrevented=tt;var e=this.originalEvent;if(!e)return;e.preventDefault?e.preventDefault():e.returnValue=!1},stopPropagation:function(){this.isPropagationStopped=tt;var e=this.originalEvent;if(!e)return;e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=tt,this.stopPropagation()},isDefaultPrevented:et,isPropagationStopped:et,isImmediatePropagationStopped:et},v.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){v.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,s=e.handleObj,o=s.selector;if(!i||i!==r&&!v.contains(r,i))e.type=s.origType,n=s.handler.apply(this,arguments),e.type=t;return n}}}),v.support.submitBubbles||(v.event.special.submit={setup:function(){if(v.nodeName(this,"form"))return!1;v.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=v.nodeName(n,"input")||v.nodeName(n,"button")?n.form:t;r&&!v._data(r,"_submit_attached")&&(v.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),v._data(r,"_submit_attached",!0))})},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&v.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){if(v.nodeName(this,"form"))return!1;v.event.remove(this,"._submit")}}),v.support.changeBubbles||(v.event.special.change={setup:function(){if($.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")v.event.add(this,"propertychange._change",function(e){e.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),v.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),v.event.simulate("change",this,e,!0)});return!1}v.event.add(this,"beforeactivate._change",function(e){var t=e.target;$.test(t.nodeName)&&!v._data(t,"_change_attached")&&(v.event.add(t,"change._change",function(e){this.parentNode&&!e.isSimulated&&!e.isTrigger&&v.event.simulate("change",this.parentNode,e,!0)}),v._data(t,"_change_attached",!0))})},handle:function(e){var t=e.target;if(this!==t||e.isSimulated||e.isTrigger||t.type!=="radio"&&t.type!=="checkbox")return e.handleObj.handler.apply(this,arguments)},teardown:function(){return v.event.remove(this,"._change"),!$.test(this.nodeName)}}),v.support.focusinBubbles||v.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){v.event.simulate(t,e.target,v.event.fix(e),!0)};v.event.special[t]={setup:function(){n++===0&&i.addEventListener(e,r,!0)},teardown:function(){--n===0&&i.removeEventListener(e,r,!0)}}}),v.fn.extend({on:function(e,n,r,i,s){var o,u;if(typeof e=="object"){typeof n!="string"&&(r=r||n,n=t);for(u in e)this.on(u,n,r,e[u],s);return this}r==null&&i==null?(i=n,r=n=t):i==null&&(typeof n=="string"?(i=r,r=t):(i=r,r=n,n=t));if(i===!1)i=et;else if(!i)return this;return s===1&&(o=i,i=function(e){return v().off(e),o.apply(this,arguments)},i.guid=o.guid||(o.guid=v.guid++)),this.each(function(){v.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,s;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,v(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if(typeof e=="object"){for(s in e)this.off(s,n,e[s]);return this}if(n===!1||typeof n=="function")r=n,n=t;return r===!1&&(r=et),this.each(function(){v.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},live:function(e,t,n){return v(this.context).on(e,this.selector,t,n),this},die:function(e,t){return v(this.context).off(e,this.selector||"**",t),this},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return arguments.length===1?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){v.event.trigger(e,t,this)})},triggerHandler:function(e,t){if(this[0])return v.event.trigger(e,t,this[0],!0)},toggle:function(e){var t=arguments,n=e.guid||v.guid++,r=0,i=function(n){var i=(v._data(this,"lastToggle"+e.guid)||0)%r;return v._data(this,"lastToggle"+e.guid,i+1),n.preventDefault(),t[i].apply(this,arguments)||!1};i.guid=n;while(r<t.length)t[r++].guid=n;return this.click(i)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),v.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){v.fn[t]=function(e,n){return n==null&&(n=e,e=null),arguments.length>0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e+" "];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)||u),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u<a;u++)if(s=e[u])if(!n||n(s,r,i))o.push(s),f&&t.push(u);return o}function ct(e,t,n,r,i,s){return r&&!r[d]&&(r=ct(r)),i&&!i[d]&&(i=ct(i,s)),N(function(s,o,u,a){var f,l,c,h=[],p=[],d=o.length,v=s||dt(t||"*",u.nodeType?[u]:u,[]),m=e&&(s||!t)?lt(v,h,e,u,a):v,g=n?i||(s?e:d||r)?[]:o:m;n&&n(m,g,u,a);if(r){f=lt(g,p),r(f,[],u,a),l=f.length;while(l--)if(c=f[l])g[p[l]]=!(m[p[l]]=c)}if(s){if(i||e){if(i){f=[],l=g.length;while(l--)(c=g[l])&&f.push(m[l]=c);i(null,g=[],f,a)}l=g.length;while(l--)(c=g[l])&&(f=i?T.call(s,c):h[l])>-1&&(s[f]=!(o[f]=c))}}else g=lt(g===o?g.splice(d,g.length):g),i?i(null,o,g,a):S.apply(o,g)})}function ht(e){var t,n,r,s=e.length,o=i.relative[e[0].type],u=o||i.relative[" "],a=o?1:0,f=at(function(e){return e===t},u,!0),l=at(function(e){return T.call(t,e)>-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a<s;a++)if(n=i.relative[e[a].type])h=[at(ft(h),n)];else{n=i.filter[e[a].type].apply(null,e[a].matches);if(n[d]){r=++a;for(;r<s;r++)if(i.relative[e[r].type])break;return ct(a>1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a<r&&ht(e.slice(a,r)),r<s&&ht(e=e.slice(r)),r<s&&e.join(""))}h.push(n)}return ft(h)}function pt(e,t){var r=t.length>0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n){var r=0,i=t.length;for(;r<i;r++)nt(e,t[r],n);return n}function vt(e,t,n,r,s){var o,u,f,l,c,h=ut(e),p=h.length;if(!r&&h.length===1){u=h[0]=h[0].slice(0);if(u.length>2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;t<n;t++)if(this[t]===e)return t;return-1},N=function(e,t){return e[d]=t==null||t,e},C=function(){var e={},t=[];return N(function(n,r){return t.push(n)>i.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML="<a href='#'></a>",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML="<select></select>";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="<a name='"+d+"'></a><div name='"+d+"'></div>",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(){return[0]}),last:st(function(e,t){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:st(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:st(function(e,t,n){for(var r=n<0?n+t:n;--r>=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}},f=y.compareDocumentPosition?function(e,t){return e===t?(l=!0,0):(!e.compareDocumentPosition||!t.compareDocumentPosition?e.compareDocumentPosition:e.compareDocumentPosition(t)&4)?-1:1}:function(e,t){if(e===t)return l=!0,0;if(e.sourceIndex&&t.sourceIndex)return e.sourceIndex-t.sourceIndex;var n,r,i=[],s=[],o=e.parentNode,u=t.parentNode,a=o;if(o===u)return ot(e,t);if(!o)return-1;if(!u)return 1;while(a)i.unshift(a),a=a.parentNode;a=u;while(a)s.unshift(a),a=a.parentNode;n=i.length,r=s.length;for(var f=0;f<n&&f<r;f++)if(i[f]!==s[f])return ot(i[f],s[f]);return f===n?ot(e,s[f],-1):ot(i[f],t,1)},[0,0].sort(f),h=!l,nt.uniqueSort=function(e){var t,n=[],r=1,i=0;l=h,e.sort(f);if(l){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e},nt.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},a=nt.compile=function(e,t){var n,r=[],i=[],s=A[d][e+" "];if(!s){t||(t=ut(e)),n=t.length;while(n--)s=ht(t[n]),s[d]?r.push(s):i.push(s);s=A(e,pt(i,r))}return s},g.querySelectorAll&&function(){var e,t=vt,n=/'|\\/g,r=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,i=[":focus"],s=[":active"],u=y.matchesSelector||y.mozMatchesSelector||y.webkitMatchesSelector||y.oMatchesSelector||y.msMatchesSelector;K(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="<p test=''></p>",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML="<input type='hidden'/>",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&!i.test(e)){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&!i.test(n))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t<n;t++)if(v.contains(u[t],this))return!0});o=this.pushStack("","find",e);for(t=0,n=this.length;t<n;t++){r=o.length,v.find(e,this[t],o);if(t>0)for(i=r;i<o.length;i++)for(s=0;s<r;s++)if(o[s]===o[i]){o.splice(i--,1);break}}return o},has:function(e){var t,n=v(e,this),r=n.length;return this.filter(function(){for(t=0;t<r;t++)if(v.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1),"not",e)},filter:function(e){return this.pushStack(ft(this,e,!0),"filter",e)},is:function(e){return!!e&&(typeof e=="string"?st.test(e)?v(e,this.context).index(this[0])>=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r<i;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&n.nodeType!==11){if(o?o.index(n)>-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/<tbody/i,gt=/<|&#?\w+;/,yt=/<(?:script|style|link)/i,bt=/<(?:script|object|embed|option|style)/i,wt=new RegExp("<(?:"+ct+")[\\s/>]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*<!(?:\[CDATA\[|\-\-)|[\]\-]{2}>\s*$/g,Nt={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X<div>","</div>"]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1></$2>");try{for(;r<i;r++)n=this[r]||{},n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),n.innerHTML=e);n=0}catch(s){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){return ut(this[0])?this.length?this.pushStack(v(v.isFunction(e)?e():e),"replaceWith",e):this:v.isFunction(e)?this.each(function(t){var n=v(this),r=n.html();n.replaceWith(e.call(this,t,r))}):(typeof e!="string"&&(e=v(e).detach()),this.each(function(){var t=this.nextSibling,n=this.parentNode;v(this).remove(),t?v(t).before(e):v(n).append(e)}))},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=[].concat.apply([],e);var i,s,o,u,a=0,f=e[0],l=[],c=this.length;if(!v.support.checkClone&&c>1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a<c;a++)r.call(n&&v.nodeName(this[a],"table")?Lt(this[a],"tbody"):this[a],a===u?o:v.clone(o,!0,!0))}o=s=null,l.length&&v.each(l,function(e,t){t.src?v.ajax?v.ajax({url:t.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):v.error("no ajax"):v.globalEval((t.text||t.textContent||t.innerHTML||"").replace(Tt,"")),t.parentNode&&t.parentNode.removeChild(t)})}return this}}),v.buildFragment=function(e,n,r){var s,o,u,a=e[0];return n=n||i,n=!n.nodeType&&n[0]||n,n=n.ownerDocument||n,e.length===1&&typeof a=="string"&&a.length<512&&n===i&&a.charAt(0)==="<"&&!bt.test(a)&&(v.support.checkClone||!St.test(a))&&(v.support.html5Clone||!wt.test(a))&&(o=!0,s=v.fragments[a],u=s!==t),s||(s=n.createDocumentFragment(),v.clean(e,n,s,r),o&&(v.fragments[a]=u&&s)),{fragment:s,cacheable:o}},v.fragments={},v.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){v.fn[e]=function(n){var r,i=0,s=[],o=v(n),u=o.length,a=this.length===1&&this[0].parentNode;if((a==null||a&&a.nodeType===11&&a.childNodes.length===1)&&u===1)return o[t](this[0]),this;for(;i<u;i++)r=(i>0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1></$2>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]==="<table>"&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}}),function(){var e,t;v.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=v.uaMatch(o.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),v.browser=t,v.sub=function(){function e(t,n){return new e.fn.init(t,n)}v.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(r,i){return i&&i instanceof v&&!(i instanceof e)&&(i=e(i)),v.fn.init.call(this,r,i,t)},e.fn.init.prototype=e.fn;var t=e(i);return e}}();var Dt,Pt,Ht,Bt=/alpha\([^)]*\)/i,jt=/opacity=([^)]*)/,Ft=/^(top|right|bottom|left)$/,It=/^(none|table(?!-c[ea]).+)/,qt=/^margin/,Rt=new RegExp("^("+m+")(.*)$","i"),Ut=new RegExp("^("+m+")(?!px)[a-z%]+$","i"),zt=new RegExp("^([-+])=("+m+")","i"),Wt={BODY:"block"},Xt={position:"absolute",visibility:"hidden",display:"block"},Vt={letterSpacing:0,fontWeight:400},$t=["Top","Right","Bottom","Left"],Jt=["Webkit","O","Moz","ms"],Kt=v.fn.toggle;v.fn.extend({css:function(e,n){return v.access(this,function(e,n,r){return r!==t?v.style(e,n,r):v.css(e,n)},e,n,arguments.length>1)},show:function(){return Yt(this,!0)},hide:function(){return Yt(this)},toggle:function(e,t){var n=typeof e=="boolean";return v.isFunction(e)&&v.isFunction(t)?Kt.apply(this,arguments):this.each(function(){(n?e:Gt(this))?v(this).show():v(this).hide()})}}),v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Dt(e,"opacity");return n===""?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":v.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!e||e.nodeType===3||e.nodeType===8||!e.style)return;var s,o,u,a=v.camelCase(n),f=e.style;n=v.cssProps[a]||(v.cssProps[a]=Qt(f,a)),u=v.cssHooks[n]||v.cssHooks[a];if(r===t)return u&&"get"in u&&(s=u.get(e,!1,i))!==t?s:f[n];o=typeof r,o==="string"&&(s=zt.exec(r))&&(r=(s[1]+1)*s[2]+parseFloat(v.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!v.cssNumber[a]&&(r+="px");if(!u||!("set"in u)||(r=u.set(e,r,i))!==t)try{f[n]=r}catch(l){}},css:function(e,n,r,i){var s,o,u,a=v.camelCase(n);return n=v.cssProps[a]||(v.cssProps[a]=Qt(e.style,a)),u=v.cssHooks[n]||v.cssHooks[a],u&&"get"in u&&(s=u.get(e,!0,i)),s===t&&(s=Dt(e,n)),s==="normal"&&n in Vt&&(s=Vt[n]),r||i!==t?(o=parseFloat(s),r||v.isNumeric(o)?o||0:s):s},swap:function(e,t,n){var r,i,s={};for(i in t)s[i]=e.style[i],e.style[i]=t[i];r=n.call(e);for(i in t)e.style[i]=s[i];return r}}),e.getComputedStyle?Dt=function(t,n){var r,i,s,o,u=e.getComputedStyle(t,null),a=t.style;return u&&(r=u.getPropertyValue(n)||u[n],r===""&&!v.contains(t.ownerDocument,t)&&(r=v.style(t,n)),Ut.test(r)&&qt.test(n)&&(i=a.width,s=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=r,r=u.width,a.width=i,a.minWidth=s,a.maxWidth=o)),r}:i.documentElement.currentStyle&&(Dt=function(e,t){var n,r,i=e.currentStyle&&e.currentStyle[t],s=e.style;return i==null&&s&&s[t]&&(i=s[t]),Ut.test(i)&&!Ft.test(t)&&(n=s.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),s.left=t==="fontSize"?"1em":i,i=s.pixelLeft+"px",s.left=n,r&&(e.runtimeStyle.left=r)),i===""?"auto":i}),v.each(["height","width"],function(e,t){v.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth===0&&It.test(Dt(e,"display"))?v.swap(e,Xt,function(){return tn(e,t,r)}):tn(e,t,r)},set:function(e,n,r){return Zt(e,n,r?en(e,t,r,v.support.boxSizing&&v.css(e,"boxSizing")==="border-box"):0)}}}),v.support.opacity||(v.cssHooks.opacity={get:function(e,t){return jt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=v.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&v.trim(s.replace(Bt,""))===""&&n.removeAttribute){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=Bt.test(s)?s.replace(Bt,i):s+" "+i}}),v(function(){v.support.reliableMarginRight||(v.cssHooks.marginRight={get:function(e,t){return v.swap(e,{display:"inline-block"},function(){if(t)return Dt(e,"marginRight")})}}),!v.support.pixelPosition&&v.fn.position&&v.each(["top","left"],function(e,t){v.cssHooks[t]={get:function(e,n){if(n){var r=Dt(e,t);return Ut.test(r)?v(e).position()[t]+"px":r}}}})}),v.expr&&v.expr.filters&&(v.expr.filters.hidden=function(e){return e.offsetWidth===0&&e.offsetHeight===0||!v.support.reliableHiddenOffsets&&(e.style&&e.style.display||Dt(e,"display"))==="none"},v.expr.filters.visible=function(e){return!v.expr.filters.hidden(e)}),v.each({margin:"",padding:"",border:"Width"},function(e,t){v.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+$t[r]+t]=i[r]||i[r-2]||i[0];return s}},qt.test(e)||(v.cssHooks[e+t].set=Zt)});var rn=/%20/g,sn=/\[\]$/,on=/\r?\n/g,un=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,an=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||an.test(this.nodeName)||un.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)fn(r,e[r],n,s);return i.join("&").replace(rn,"+")};var ln,cn,hn=/#.*$/,pn=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,dn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,vn=/^(?:GET|HEAD)$/,mn=/^\/\//,gn=/\?/,yn=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bn=/([?&])_=[^&]*/,wn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,En=v.fn.load,Sn={},xn={},Tn=["*/"]+["*"];try{cn=s.href}catch(Nn){cn=i.createElement("a"),cn.href="",cn=cn.href}ln=wn.exec(cn.toLowerCase())||[],v.fn.load=function(e,n,r){if(typeof e!="string"&&En)return En.apply(this,arguments);if(!this.length)return this;var i,s,o,u=this,a=e.indexOf(" ");return a>=0&&(i=e.slice(a,e.length),e=e.slice(0,a)),v.isFunction(n)?(r=n,n=t):n&&typeof n=="object"&&(s="POST"),v.ajax({url:e,type:s,dataType:"html",data:n,complete:function(e,t){r&&u.each(r,o||[e.responseText,t,e])}}).done(function(e){o=arguments,u.html(i?v("<div>").append(e.replace(yn,"")).find(i):e)}),this},v.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){v.fn[t]=function(e){return this.on(t,e)}}),v.each(["get","post"],function(e,n){v[n]=function(e,r,i,s){return v.isFunction(r)&&(s=s||i,i=r,r=t),v.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),v.extend({getScript:function(e,n){return v.get(e,t,n,"script")},getJSON:function(e,t,n){return v.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?Ln(e,v.ajaxSettings):(t=e,e=v.ajaxSettings),Ln(e,t),e},ajaxSettings:{url:cn,isLocal:dn.test(ln[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Tn},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":v.parseJSON,"text xml":v.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:Cn(Sn),ajaxTransport:Cn(xn),ajax:function(e,n){function T(e,n,s,a){var l,y,b,w,S,T=n;if(E===2)return;E=2,u&&clearTimeout(u),o=t,i=a||"",x.readyState=e>0?4:0,s&&(w=An(c,x,s));if(e>=200&&e<300||e===304)c.ifModified&&(S=x.getResponseHeader("Last-Modified"),S&&(v.lastModified[r]=S),S=x.getResponseHeader("Etag"),S&&(v.etag[r]=S)),e===304?(T="notmodified",l=!0):(l=On(c,w),T=l.state,y=l.data,b=l.error,l=!b);else{b=T;if(!T||e)T="error",e<0&&(e=0)}x.status=e,x.statusText=(n||T)+"",l?d.resolveWith(h,[y,T,x]):d.rejectWith(h,[x,T,b]),x.statusCode(g),g=t,f&&p.trigger("ajax"+(l?"Success":"Error"),[x,c,l?y:b]),m.fireWith(h,[x,T]),f&&(p.trigger("ajaxComplete",[x,c]),--v.active||v.event.trigger("ajaxStop"))}typeof e=="object"&&(n=e,e=t),n=n||{};var r,i,s,o,u,a,f,l,c=v.ajaxSetup({},n),h=c.context||c,p=h!==c&&(h.nodeType||h instanceof v)?v(h):v.event,d=v.Deferred(),m=v.Callbacks("once memory"),g=c.statusCode||{},b={},w={},E=0,S="canceled",x={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=w[n]=w[n]||e,b[e]=t}return this},getAllResponseHeaders:function(){return E===2?i:null},getResponseHeader:function(e){var n;if(E===2){if(!s){s={};while(n=pn.exec(i))s[n[1].toLowerCase()]=n[2]}n=s[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(c.mimeType=e),this},abort:function(e){return e=e||S,o&&o.abort(e),T(0,e),this}};d.promise(x),x.success=x.done,x.error=x.fail,x.complete=m.add,x.statusCode=function(e){if(e){var t;if(E<2)for(t in e)g[t]=[g[t],e[t]];else t=e[x.status],x.always(t)}return this},c.url=((e||c.url)+"").replace(hn,"").replace(mn,ln[1]+"//"),c.dataTypes=v.trim(c.dataType||"*").toLowerCase().split(y),c.crossDomain==null&&(a=wn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===ln[1]&&a[2]===ln[2]&&(a[3]||(a[1]==="http:"?80:443))==(ln[3]||(ln[1]==="http:"?80:443)))),c.data&&c.processData&&typeof c.data!="string"&&(c.data=v.param(c.data,c.traditional)),kn(Sn,c,n,x);if(E===2)return x;f=c.global,c.type=c.type.toUpperCase(),c.hasContent=!vn.test(c.type),f&&v.active++===0&&v.event.trigger("ajaxStart");if(!c.hasContent){c.data&&(c.url+=(gn.test(c.url)?"&":"?")+c.data,delete c.data),r=c.url;if(c.cache===!1){var N=v.now(),C=c.url.replace(bn,"$1_="+N);c.url=C+(C===c.url?(gn.test(c.url)?"&":"?")+"_="+N:"")}}(c.data&&c.hasContent&&c.contentType!==!1||n.contentType)&&x.setRequestHeader("Content-Type",c.contentType),c.ifModified&&(r=r||c.url,v.lastModified[r]&&x.setRequestHeader("If-Modified-Since",v.lastModified[r]),v.etag[r]&&x.setRequestHeader("If-None-Match",v.etag[r])),x.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+(c.dataTypes[0]!=="*"?", "+Tn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)x.setRequestHeader(l,c.headers[l]);if(!c.beforeSend||c.beforeSend.call(h,x,c)!==!1&&E!==2){S="abort";for(l in{success:1,error:1,complete:1})x[l](c[l]);o=kn(xn,c,n,x);if(!o)T(-1,"No Transport");else{x.readyState=1,f&&p.trigger("ajaxSend",[x,c]),c.async&&c.timeout>0&&(u=setTimeout(function(){x.abort("timeout")},c.timeout));try{E=1,o.send(b,T)}catch(k){if(!(E<2))throw k;T(-1,k)}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var Mn=[],_n=/\?/,Dn=/(=)\?(?=&|$)|\?\?/,Pn=v.now();v.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Mn.pop()||v.expando+"_"+Pn++;return this[e]=!0,e}}),v.ajaxPrefilter("json jsonp",function(n,r,i){var s,o,u,a=n.data,f=n.url,l=n.jsonp!==!1,c=l&&Dn.test(f),h=l&&!c&&typeof a=="string"&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Dn.test(a);if(n.dataTypes[0]==="jsonp"||c||h)return s=n.jsonpCallback=v.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,o=e[s],c?n.url=f.replace(Dn,"$1"+s):h?n.data=a.replace(Dn,"$1"+s):l&&(n.url+=(_n.test(f)?"&":"?")+n.jsonp+"="+s),n.converters["script json"]=function(){return u||v.error(s+" was not called"),u[0]},n.dataTypes[0]="json",e[s]=function(){u=arguments},i.always(function(){e[s]=o,n[s]&&(n.jsonpCallback=r.jsonpCallback,Mn.push(s)),u&&v.isFunction(o)&&o(u[0]),u=o=t}),"script"}),v.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return v.globalEval(e),e}}}),v.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),v.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=i.head||i.getElementsByTagName("head")[0]||i.documentElement;return{send:function(s,o){n=i.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,i){if(i||!n.readyState||/loaded|complete/.test(n.readyState))n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,i||o(200,"success")},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var Hn,Bn=e.ActiveXObject?function(){for(var e in Hn)Hn[e](0,1)}:!1,jn=0;v.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&Fn()||In()}:Fn,function(e){v.extend(v.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(v.ajaxSettings.xhr()),v.support.ajax&&v.ajaxTransport(function(n){if(!n.crossDomain||v.support.cors){var r;return{send:function(i,s){var o,u,a=n.xhr();n.username?a.open(n.type,n.url,n.async,n.username,n.password):a.open(n.type,n.url,n.async);if(n.xhrFields)for(u in n.xhrFields)a[u]=n.xhrFields[u];n.mimeType&&a.overrideMimeType&&a.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(u in i)a.setRequestHeader(u,i[u])}catch(f){}a.send(n.hasContent&&n.data||null),r=function(e,i){var u,f,l,c,h;try{if(r&&(i||a.readyState===4)){r=t,o&&(a.onreadystatechange=v.noop,Bn&&delete Hn[o]);if(i)a.readyState!==4&&a.abort();else{u=a.status,l=a.getAllResponseHeaders(),c={},h=a.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=a.responseText}catch(p){}try{f=a.statusText}catch(p){f=""}!u&&n.isLocal&&!n.crossDomain?u=c.text?200:404:u===1223&&(u=204)}}}catch(d){i||s(-1,d)}c&&s(u,f,c,l)},n.async?a.readyState===4?setTimeout(r,0):(o=++jn,Bn&&(Hn||(Hn={},v(e).unload(Bn)),Hn[o]=r),a.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}});var qn,Rn,Un=/^(?:toggle|show|hide)$/,zn=new RegExp("^(?:([-+])=|)("+m+")([a-z%]*)$","i"),Wn=/queueHooks$/,Xn=[Gn],Vn={"*":[function(e,t){var n,r,i=this.createTween(e,t),s=zn.exec(t),o=i.cur(),u=+o||0,a=1,f=20;if(s){n=+s[2],r=s[3]||(v.cssNumber[e]?"":"px");if(r!=="px"&&u){u=v.css(i.elem,e,!0)||n||1;do a=a||".5",u/=a,v.style(i.elem,e,u+r);while(a!==(a=i.cur()/o)&&a!==1&&--f)}i.unit=r,i.start=u,i.end=s[1]?u+(s[1]+1)*n:n}return i}]};v.Animation=v.extend(Kn,{tweener:function(e,t){v.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;r<i;r++)n=e[r],Vn[n]=Vn[n]||[],Vn[n].unshift(t)},prefilter:function(e,t){t?Xn.unshift(e):Xn.push(e)}}),v.Tween=Yn,Yn.prototype={constructor:Yn,init:function(e,t,n,r,i,s){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=s||(v.cssNumber[n]?"":"px")},cur:function(){var e=Yn.propHooks[this.prop];return e&&e.get?e.get(this):Yn.propHooks._default.get(this)},run:function(e){var t,n=Yn.propHooks[this.prop];return this.options.duration?this.pos=t=v.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):Yn.propHooks._default.set(this),this}},Yn.prototype.init.prototype=Yn.prototype,Yn.propHooks={_default:{get:function(e){var t;return e.elem[e.prop]==null||!!e.elem.style&&e.elem.style[e.prop]!=null?(t=v.css(e.elem,e.prop,!1,""),!t||t==="auto"?0:t):e.elem[e.prop]},set:function(e){v.fx.step[e.prop]?v.fx.step[e.prop](e):e.elem.style&&(e.elem.style[v.cssProps[e.prop]]!=null||v.cssHooks[e.prop])?v.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},Yn.propHooks.scrollTop=Yn.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},v.each(["toggle","show","hide"],function(e,t){var n=v.fn[t];v.fn[t]=function(r,i,s){return r==null||typeof r=="boolean"||!e&&v.isFunction(r)&&v.isFunction(i)?n.apply(this,arguments):this.animate(Zn(t,!0),r,i,s)}}),v.fn.extend({fadeTo:function(e,t,n,r){return this.filter(Gt).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=v.isEmptyObject(e),s=v.speed(t,n,r),o=function(){var t=Kn(this,v.extend({},e),s);i&&t.stop(!0)};return i||s.queue===!1?this.each(o):this.queue(s.queue,o)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return typeof e!="string"&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=e!=null&&e+"queueHooks",s=v.timers,o=v._data(this);if(n)o[n]&&o[n].stop&&i(o[n]);else for(n in o)o[n]&&o[n].stop&&Wn.test(n)&&i(o[n]);for(n=s.length;n--;)s[n].elem===this&&(e==null||s[n].queue===e)&&(s[n].anim.stop(r),t=!1,s.splice(n,1));(t||!r)&&v.dequeue(this,e)})}}),v.each({slideDown:Zn("show"),slideUp:Zn("hide"),slideToggle:Zn("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){v.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),v.speed=function(e,t,n){var r=e&&typeof e=="object"?v.extend({},e):{complete:n||!n&&t||v.isFunction(e)&&e,duration:e,easing:n&&t||t&&!v.isFunction(t)&&t};r.duration=v.fx.off?0:typeof r.duration=="number"?r.duration:r.duration in v.fx.speeds?v.fx.speeds[r.duration]:v.fx.speeds._default;if(r.queue==null||r.queue===!0)r.queue="fx";return r.old=r.complete,r.complete=function(){v.isFunction(r.old)&&r.old.call(this),r.queue&&v.dequeue(this,r.queue)},r},v.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},v.timers=[],v.fx=Yn.prototype.init,v.fx.tick=function(){var e,n=v.timers,r=0;qn=v.now();for(;r<n.length;r++)e=n[r],!e()&&n[r]===e&&n.splice(r--,1);n.length||v.fx.stop(),qn=t},v.fx.timer=function(e){e()&&v.timers.push(e)&&!Rn&&(Rn=setInterval(v.fx.tick,v.fx.interval))},v.fx.interval=13,v.fx.stop=function(){clearInterval(Rn),Rn=null},v.fx.speeds={slow:600,fast:200,_default:400},v.fx.step={},v.expr&&v.expr.filters&&(v.expr.filters.animated=function(e){return v.grep(v.timers,function(t){return e===t.elem}).length});var er=/^(?:body|html)$/i;v.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){v.offset.setOffset(this,e,t)});var n,r,i,s,o,u,a,f={top:0,left:0},l=this[0],c=l&&l.ownerDocument;if(!c)return;return(r=c.body)===l?v.offset.bodyOffset(l):(n=c.documentElement,v.contains(n,l)?(typeof l.getBoundingClientRect!="undefined"&&(f=l.getBoundingClientRect()),i=tr(c),s=n.clientTop||r.clientTop||0,o=n.clientLeft||r.clientLeft||0,u=i.pageYOffset||n.scrollTop,a=i.pageXOffset||n.scrollLeft,{top:f.top+u-s,left:f.left+a-o}):f)},v.offset={bodyOffset:function(e){var t=e.offsetTop,n=e.offsetLeft;return v.support.doesNotIncludeMarginInBodyOffset&&(t+=parseFloat(v.css(e,"marginTop"))||0,n+=parseFloat(v.css(e,"marginLeft"))||0),{top:t,left:n}},setOffset:function(e,t,n){var r=v.css(e,"position");r==="static"&&(e.style.position="relative");var i=v(e),s=i.offset(),o=v.css(e,"top"),u=v.css(e,"left"),a=(r==="absolute"||r==="fixed")&&v.inArray("auto",[o,u])>-1,f={},l={},c,h;a?(l=i.position(),c=l.top,h=l.left):(c=parseFloat(o)||0,h=parseFloat(u)||0),v.isFunction(t)&&(t=t.call(e,n,s)),t.top!=null&&(f.top=t.top-s.top+c),t.left!=null&&(f.left=t.left-s.left+h),"using"in t?t.using.call(e,f):i.css(f)}},v.fn.extend({position:function(){if(!this[0])return;var e=this[0],t=this.offsetParent(),n=this.offset(),r=er.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(v.css(e,"marginTop"))||0,n.left-=parseFloat(v.css(e,"marginLeft"))||0,r.top+=parseFloat(v.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(v.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||i.body;while(e&&!er.test(e.nodeName)&&v.css(e,"position")==="static")e=e.offsetParent;return e||i.body})}}),v.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);v.fn[e]=function(i){return v.access(this,function(e,i,s){var o=tr(e);if(s===t)return o?n in o?o[n]:o.document.documentElement[i]:e[i];o?o.scrollTo(r?v(o).scrollLeft():s,r?s:v(o).scrollTop()):e[i]=s},e,i,arguments.length,null)}}),v.each({Height:"height",Width:"width"},function(e,n){v.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){v.fn[i]=function(i,s){var o=arguments.length&&(r||typeof i!="boolean"),u=r||(i===!0||s===!0?"margin":"border");return v.access(this,function(n,r,i){var s;return v.isWindow(n)?n.document.documentElement["client"+e]:n.nodeType===9?(s=n.documentElement,Math.max(n.body["scroll"+e],s["scroll"+e],n.body["offset"+e],s["offset"+e],s["client"+e])):i===t?v.css(n,r,i,u):v.style(n,r,i,u)},n,o?i:t,o,null)}})}),e.jQuery=e.$=v,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return v})})(window); \ No newline at end of file
diff --git a/distro/common/html/_static/minus.png b/distro/common/html/_static/minus.png
deleted file mode 100644
index da1c5620d1..0000000000
--- a/distro/common/html/_static/minus.png
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/_static/navigation.png b/distro/common/html/_static/navigation.png
deleted file mode 100644
index 1081dc1439..0000000000
--- a/distro/common/html/_static/navigation.png
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/_static/plus.png b/distro/common/html/_static/plus.png
deleted file mode 100644
index b3cb37425e..0000000000
--- a/distro/common/html/_static/plus.png
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/_static/pygments.css b/distro/common/html/_static/pygments.css
deleted file mode 100644
index 7b9d788e91..0000000000
--- a/distro/common/html/_static/pygments.css
+++ /dev/null
@@ -1,62 +0,0 @@
-.highlight .hll { background-color: #ffffcc }
-.highlight { background: #f8f8f8; }
-.highlight .c { color: #008800; font-style: italic } /* Comment */
-.highlight .err { border: 1px solid #FF0000 } /* Error */
-.highlight .k { color: #AA22FF; font-weight: bold } /* Keyword */
-.highlight .o { color: #666666 } /* Operator */
-.highlight .cm { color: #008800; font-style: italic } /* Comment.Multiline */
-.highlight .cp { color: #008800 } /* Comment.Preproc */
-.highlight .c1 { color: #008800; font-style: italic } /* Comment.Single */
-.highlight .cs { color: #008800; font-weight: bold } /* Comment.Special */
-.highlight .gd { color: #A00000 } /* Generic.Deleted */
-.highlight .ge { font-style: italic } /* Generic.Emph */
-.highlight .gr { color: #FF0000 } /* Generic.Error */
-.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
-.highlight .gi { color: #00A000 } /* Generic.Inserted */
-.highlight .go { color: #888888 } /* Generic.Output */
-.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
-.highlight .gs { font-weight: bold } /* Generic.Strong */
-.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
-.highlight .gt { color: #0044DD } /* Generic.Traceback */
-.highlight .kc { color: #AA22FF; font-weight: bold } /* Keyword.Constant */
-.highlight .kd { color: #AA22FF; font-weight: bold } /* Keyword.Declaration */
-.highlight .kn { color: #AA22FF; font-weight: bold } /* Keyword.Namespace */
-.highlight .kp { color: #AA22FF } /* Keyword.Pseudo */
-.highlight .kr { color: #AA22FF; font-weight: bold } /* Keyword.Reserved */
-.highlight .kt { color: #00BB00; font-weight: bold } /* Keyword.Type */
-.highlight .m { color: #666666 } /* Literal.Number */
-.highlight .s { color: #BB4444 } /* Literal.String */
-.highlight .na { color: #BB4444 } /* Name.Attribute */
-.highlight .nb { color: #AA22FF } /* Name.Builtin */
-.highlight .nc { color: #0000FF } /* Name.Class */
-.highlight .no { color: #880000 } /* Name.Constant */
-.highlight .nd { color: #AA22FF } /* Name.Decorator */
-.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
-.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
-.highlight .nf { color: #00A000 } /* Name.Function */
-.highlight .nl { color: #A0A000 } /* Name.Label */
-.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
-.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
-.highlight .nv { color: #B8860B } /* Name.Variable */
-.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
-.highlight .w { color: #bbbbbb } /* Text.Whitespace */
-.highlight .mf { color: #666666 } /* Literal.Number.Float */
-.highlight .mh { color: #666666 } /* Literal.Number.Hex */
-.highlight .mi { color: #666666 } /* Literal.Number.Integer */
-.highlight .mo { color: #666666 } /* Literal.Number.Oct */
-.highlight .sb { color: #BB4444 } /* Literal.String.Backtick */
-.highlight .sc { color: #BB4444 } /* Literal.String.Char */
-.highlight .sd { color: #BB4444; font-style: italic } /* Literal.String.Doc */
-.highlight .s2 { color: #BB4444 } /* Literal.String.Double */
-.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
-.highlight .sh { color: #BB4444 } /* Literal.String.Heredoc */
-.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
-.highlight .sx { color: #008000 } /* Literal.String.Other */
-.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
-.highlight .s1 { color: #BB4444 } /* Literal.String.Single */
-.highlight .ss { color: #B8860B } /* Literal.String.Symbol */
-.highlight .bp { color: #AA22FF } /* Name.Builtin.Pseudo */
-.highlight .vc { color: #B8860B } /* Name.Variable.Class */
-.highlight .vg { color: #B8860B } /* Name.Variable.Global */
-.highlight .vi { color: #B8860B } /* Name.Variable.Instance */
-.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file
diff --git a/distro/common/html/_static/searchtools.js b/distro/common/html/_static/searchtools.js
deleted file mode 100644
index f2377af09b..0000000000
--- a/distro/common/html/_static/searchtools.js
+++ /dev/null
@@ -1,622 +0,0 @@
-/*
- * searchtools.js_t
- * ~~~~~~~~~~~~~~~~
- *
- * Sphinx JavaScript utilties for the full-text search.
- *
- * :copyright: Copyright 2007-2016, by the Sphinx team, see AUTHORS.
- * :license: BSD, see LICENSE for details.
- *
- */
-
-
-/**
- * Porter Stemmer
- */
-var Stemmer = function() {
-
- var step2list = {
- ational: 'ate',
- tional: 'tion',
- enci: 'ence',
- anci: 'ance',
- izer: 'ize',
- bli: 'ble',
- alli: 'al',
- entli: 'ent',
- eli: 'e',
- ousli: 'ous',
- ization: 'ize',
- ation: 'ate',
- ator: 'ate',
- alism: 'al',
- iveness: 'ive',
- fulness: 'ful',
- ousness: 'ous',
- aliti: 'al',
- iviti: 'ive',
- biliti: 'ble',
- logi: 'log'
- };
-
- var step3list = {
- icate: 'ic',
- ative: '',
- alize: 'al',
- iciti: 'ic',
- ical: 'ic',
- ful: '',
- ness: ''
- };
-
- var c = "[^aeiou]"; // consonant
- var v = "[aeiouy]"; // vowel
- var C = c + "[^aeiouy]*"; // consonant sequence
- var V = v + "[aeiou]*"; // vowel sequence
-
- var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
- var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
- var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
- var s_v = "^(" + C + ")?" + v; // vowel in stem
-
- this.stemWord = function (w) {
- var stem;
- var suffix;
- var firstch;
- var origword = w;
-
- if (w.length < 3)
- return w;
-
- var re;
- var re2;
- var re3;
- var re4;
-
- firstch = w.substr(0,1);
- if (firstch == "y")
- w = firstch.toUpperCase() + w.substr(1);
-
- // Step 1a
- re = /^(.+?)(ss|i)es$/;
- re2 = /^(.+?)([^s])s$/;
-
- if (re.test(w))
- w = w.replace(re,"$1$2");
- else if (re2.test(w))
- w = w.replace(re2,"$1$2");
-
- // Step 1b
- re = /^(.+?)eed$/;
- re2 = /^(.+?)(ed|ing)$/;
- if (re.test(w)) {
- var fp = re.exec(w);
- re = new RegExp(mgr0);
- if (re.test(fp[1])) {
- re = /.$/;
- w = w.replace(re,"");
- }
- }
- else if (re2.test(w)) {
- var fp = re2.exec(w);
- stem = fp[1];
- re2 = new RegExp(s_v);
- if (re2.test(stem)) {
- w = stem;
- re2 = /(at|bl|iz)$/;
- re3 = new RegExp("([^aeiouylsz])\\1$");
- re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
- if (re2.test(w))
- w = w + "e";
- else if (re3.test(w)) {
- re = /.$/;
- w = w.replace(re,"");
- }
- else if (re4.test(w))
- w = w + "e";
- }
- }
-
- // Step 1c
- re = /^(.+?)y$/;
- if (re.test(w)) {
- var fp = re.exec(w);
- stem = fp[1];
- re = new RegExp(s_v);
- if (re.test(stem))
- w = stem + "i";
- }
-
- // Step 2
- re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
- if (re.test(w)) {
- var fp = re.exec(w);
- stem = fp[1];
- suffix = fp[2];
- re = new RegExp(mgr0);
- if (re.test(stem))
- w = stem + step2list[suffix];
- }
-
- // Step 3
- re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
- if (re.test(w)) {
- var fp = re.exec(w);
- stem = fp[1];
- suffix = fp[2];
- re = new RegExp(mgr0);
- if (re.test(stem))
- w = stem + step3list[suffix];
- }
-
- // Step 4
- re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
- re2 = /^(.+?)(s|t)(ion)$/;
- if (re.test(w)) {
- var fp = re.exec(w);
- stem = fp[1];
- re = new RegExp(mgr1);
- if (re.test(stem))
- w = stem;
- }
- else if (re2.test(w)) {
- var fp = re2.exec(w);
- stem = fp[1] + fp[2];
- re2 = new RegExp(mgr1);
- if (re2.test(stem))
- w = stem;
- }
-
- // Step 5
- re = /^(.+?)e$/;
- if (re.test(w)) {
- var fp = re.exec(w);
- stem = fp[1];
- re = new RegExp(mgr1);
- re2 = new RegExp(meq1);
- re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
- if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
- w = stem;
- }
- re = /ll$/;
- re2 = new RegExp(mgr1);
- if (re.test(w) && re2.test(w)) {
- re = /.$/;
- w = w.replace(re,"");
- }
-
- // and turn initial Y back to y
- if (firstch == "y")
- w = firstch.toLowerCase() + w.substr(1);
- return w;
- }
-}
-
-
-
-/**
- * Simple result scoring code.
- */
-var Scorer = {
- // Implement the following function to further tweak the score for each result
- // The function takes a result array [filename, title, anchor, descr, score]
- // and returns the new score.
- /*
- score: function(result) {
- return result[4];
- },
- */
-
- // query matches the full name of an object
- objNameMatch: 11,
- // or matches in the last dotted part of the object name
- objPartialMatch: 6,
- // Additive scores depending on the priority of the object
- objPrio: {0: 15, // used to be importantResults
- 1: 5, // used to be objectResults
- 2: -5}, // used to be unimportantResults
- // Used when the priority is not in the mapping.
- objPrioDefault: 0,
-
- // query found in title
- title: 15,
- // query found in terms
- term: 5
-};
-
-
-/**
- * Search Module
- */
-var Search = {
-
- _index : null,
- _queued_query : null,
- _pulse_status : -1,
-
- init : function() {
- var params = $.getQueryParameters();
- if (params.q) {
- var query = params.q[0];
- $('input[name="q"]')[0].value = query;
- this.performSearch(query);
- }
- },
-
- loadIndex : function(url) {
- $.ajax({type: "GET", url: url, data: null,
- dataType: "script", cache: true,
- complete: function(jqxhr, textstatus) {
- if (textstatus != "success") {
- document.getElementById("searchindexloader").src = url;
- }
- }});
- },
-
- setIndex : function(index) {
- var q;
- this._index = index;
- if ((q = this._queued_query) !== null) {
- this._queued_query = null;
- Search.query(q);
- }
- },
-
- hasIndex : function() {
- return this._index !== null;
- },
-
- deferQuery : function(query) {
- this._queued_query = query;
- },
-
- stopPulse : function() {
- this._pulse_status = 0;
- },
-
- startPulse : function() {
- if (this._pulse_status >= 0)
- return;
- function pulse() {
- var i;
- Search._pulse_status = (Search._pulse_status + 1) % 4;
- var dotString = '';
- for (i = 0; i < Search._pulse_status; i++)
- dotString += '.';
- Search.dots.text(dotString);
- if (Search._pulse_status > -1)
- window.setTimeout(pulse, 500);
- }
- pulse();
- },
-
- /**
- * perform a search for something (or wait until index is loaded)
- */
- performSearch : function(query) {
- // create the required interface elements
- this.out = $('#search-results');
- this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);
- this.dots = $('<span></span>').appendTo(this.title);
- this.status = $('<p style="display: none"></p>').appendTo(this.out);
- this.output = $('<ul class="search"/>').appendTo(this.out);
-
- $('#search-progress').text(_('Preparing search...'));
- this.startPulse();
-
- // index already loaded, the browser was quick!
- if (this.hasIndex())
- this.query(query);
- else
- this.deferQuery(query);
- },
-
- /**
- * execute search (requires search index to be loaded)
- */
- query : function(query) {
- var i;
- var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"];
-
- // stem the searchterms and add them to the correct list
- var stemmer = new Stemmer();
- var searchterms = [];
- var excluded = [];
- var hlterms = [];
- var tmp = query.split(/\s+/);
- var objectterms = [];
- for (i = 0; i < tmp.length; i++) {
- if (tmp[i] !== "") {
- objectterms.push(tmp[i].toLowerCase());
- }
-
- if ($u.indexOf(stopwords, tmp[i].toLowerCase()) != -1 || tmp[i].match(/^\d+$/) ||
- tmp[i] === "") {
- // skip this "word"
- continue;
- }
- // stem the word
- var word = stemmer.stemWord(tmp[i].toLowerCase());
- var toAppend;
- // select the correct list
- if (word[0] == '-') {
- toAppend = excluded;
- word = word.substr(1);
- }
- else {
- toAppend = searchterms;
- hlterms.push(tmp[i].toLowerCase());
- }
- // only add if not already in the list
- if (!$u.contains(toAppend, word))
- toAppend.push(word);
- }
- var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
-
- // console.debug('SEARCH: searching for:');
- // console.info('required: ', searchterms);
- // console.info('excluded: ', excluded);
-
- // prepare search
- var terms = this._index.terms;
- var titleterms = this._index.titleterms;
-
- // array of [filename, title, anchor, descr, score]
- var results = [];
- $('#search-progress').empty();
-
- // lookup as object
- for (i = 0; i < objectterms.length; i++) {
- var others = [].concat(objectterms.slice(0, i),
- objectterms.slice(i+1, objectterms.length));
- results = results.concat(this.performObjectSearch(objectterms[i], others));
- }
-
- // lookup as search terms in fulltext
- results = results.concat(this.performTermsSearch(searchterms, excluded, terms, Scorer.term))
- .concat(this.performTermsSearch(searchterms, excluded, titleterms, Scorer.title));
-
- // let the scorer override scores with a custom scoring function
- if (Scorer.score) {
- for (i = 0; i < results.length; i++)
- results[i][4] = Scorer.score(results[i]);
- }
-
- // now sort the results by score (in opposite order of appearance, since the
- // display function below uses pop() to retrieve items) and then
- // alphabetically
- results.sort(function(a, b) {
- var left = a[4];
- var right = b[4];
- if (left > right) {
- return 1;
- } else if (left < right) {
- return -1;
- } else {
- // same score: sort alphabetically
- left = a[1].toLowerCase();
- right = b[1].toLowerCase();
- return (left > right) ? -1 : ((left < right) ? 1 : 0);
- }
- });
-
- // for debugging
- //Search.lastresults = results.slice(); // a copy
- //console.info('search results:', Search.lastresults);
-
- // print the results
- var resultCount = results.length;
- function displayNextItem() {
- // results left, load the summary and display it
- if (results.length) {
- var item = results.pop();
- var listItem = $('<li style="display:none"></li>');
- if (DOCUMENTATION_OPTIONS.FILE_SUFFIX === '') {
- // dirhtml builder
- var dirname = item[0] + '/';
- if (dirname.match(/\/index\/$/)) {
- dirname = dirname.substring(0, dirname.length-6);
- } else if (dirname == 'index/') {
- dirname = '';
- }
- listItem.append($('<a/>').attr('href',
- DOCUMENTATION_OPTIONS.URL_ROOT + dirname +
- highlightstring + item[2]).html(item[1]));
- } else {
- // normal html builders
- listItem.append($('<a/>').attr('href',
- item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
- highlightstring + item[2]).html(item[1]));
- }
- if (item[3]) {
- listItem.append($('<span> (' + item[3] + ')</span>'));
- Search.output.append(listItem);
- listItem.slideDown(5, function() {
- displayNextItem();
- });
- } else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
- $.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[0] + '.txt',
- dataType: "text",
- complete: function(jqxhr, textstatus) {
- var data = jqxhr.responseText;
- if (data !== '') {
- listItem.append(Search.makeSearchSummary(data, searchterms, hlterms));
- }
- Search.output.append(listItem);
- listItem.slideDown(5, function() {
- displayNextItem();
- });
- }});
- } else {
- // no source available, just display title
- Search.output.append(listItem);
- listItem.slideDown(5, function() {
- displayNextItem();
- });
- }
- }
- // search finished, update title and status message
- else {
- Search.stopPulse();
- Search.title.text(_('Search Results'));
- if (!resultCount)
- Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.'));
- else
- Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
- Search.status.fadeIn(500);
- }
- }
- displayNextItem();
- },
-
- /**
- * search for object names
- */
- performObjectSearch : function(object, otherterms) {
- var filenames = this._index.filenames;
- var objects = this._index.objects;
- var objnames = this._index.objnames;
- var titles = this._index.titles;
-
- var i;
- var results = [];
-
- for (var prefix in objects) {
- for (var name in objects[prefix]) {
- var fullname = (prefix ? prefix + '.' : '') + name;
- if (fullname.toLowerCase().indexOf(object) > -1) {
- var score = 0;
- var parts = fullname.split('.');
- // check for different match types: exact matches of full name or
- // "last name" (i.e. last dotted part)
- if (fullname == object || parts[parts.length - 1] == object) {
- score += Scorer.objNameMatch;
- // matches in last name
- } else if (parts[parts.length - 1].indexOf(object) > -1) {
- score += Scorer.objPartialMatch;
- }
- var match = objects[prefix][name];
- var objname = objnames[match[1]][2];
- var title = titles[match[0]];
- // If more than one term searched for, we require other words to be
- // found in the name/title/description
- if (otherterms.length > 0) {
- var haystack = (prefix + ' ' + name + ' ' +
- objname + ' ' + title).toLowerCase();
- var allfound = true;
- for (i = 0; i < otherterms.length; i++) {
- if (haystack.indexOf(otherterms[i]) == -1) {
- allfound = false;
- break;
- }
- }
- if (!allfound) {
- continue;
- }
- }
- var descr = objname + _(', in ') + title;
-
- var anchor = match[3];
- if (anchor === '')
- anchor = fullname;
- else if (anchor == '-')
- anchor = objnames[match[1]][1] + '-' + fullname;
- // add custom score for some objects according to scorer
- if (Scorer.objPrio.hasOwnProperty(match[2])) {
- score += Scorer.objPrio[match[2]];
- } else {
- score += Scorer.objPrioDefault;
- }
- results.push([filenames[match[0]], fullname, '#'+anchor, descr, score]);
- }
- }
- }
-
- return results;
- },
-
- /**
- * search for full-text terms in the index
- */
- performTermsSearch : function(searchterms, excluded, terms, score) {
- var filenames = this._index.filenames;
- var titles = this._index.titles;
-
- var i, j, file, files;
- var fileMap = {};
- var results = [];
-
- // perform the search on the required terms
- for (i = 0; i < searchterms.length; i++) {
- var word = searchterms[i];
- // no match but word was a required one
- if ((files = terms[word]) === undefined)
- break;
- if (files.length === undefined) {
- files = [files];
- }
- // create the mapping
- for (j = 0; j < files.length; j++) {
- file = files[j];
- if (file in fileMap)
- fileMap[file].push(word);
- else
- fileMap[file] = [word];
- }
- }
-
- // now check if the files don't contain excluded terms
- for (file in fileMap) {
- var valid = true;
-
- // check if all requirements are matched
- if (fileMap[file].length != searchterms.length)
- continue;
-
- // ensure that none of the excluded terms is in the search result
- for (i = 0; i < excluded.length; i++) {
- if (terms[excluded[i]] == file ||
- $u.contains(terms[excluded[i]] || [], file)) {
- valid = false;
- break;
- }
- }
-
- // if we have still a valid result we can add it to the result list
- if (valid) {
- results.push([filenames[file], titles[file], '', null, score]);
- }
- }
- return results;
- },
-
- /**
- * helper function to return a node containing the
- * search summary for a given text. keywords is a list
- * of stemmed words, hlwords is the list of normal, unstemmed
- * words. the first one is used to find the occurrence, the
- * latter for highlighting it.
- */
- makeSearchSummary : function(text, keywords, hlwords) {
- var textLower = text.toLowerCase();
- var start = 0;
- $.each(keywords, function() {
- var i = textLower.indexOf(this.toLowerCase());
- if (i > -1)
- start = i;
- });
- start = Math.max(start - 120, 0);
- var excerpt = ((start > 0) ? '...' : '') +
- $.trim(text.substr(start, 240)) +
- ((start + 240 - text.length) ? '...' : '');
- var rv = $('<div class="context"></div>').text(excerpt);
- $.each(hlwords, function() {
- rv = rv.highlightText(this, 'highlighted');
- });
- return rv;
- }
-};
-
-$(document).ready(function() {
- Search.init();
-}); \ No newline at end of file
diff --git a/distro/common/html/_static/underscore.js b/distro/common/html/_static/underscore.js
deleted file mode 100644
index 5b55f32bea..0000000000
--- a/distro/common/html/_static/underscore.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// Underscore.js 1.3.1
-// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
-// Underscore is freely distributable under the MIT license.
-// Portions of Underscore are inspired or borrowed from Prototype,
-// Oliver Steele's Functional, and John Resig's Micro-Templating.
-// For all details and documentation:
-// http://documentcloud.github.com/underscore
-(function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
-c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,
-h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each=
-b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===n)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===n)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(x&&a.map===x)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==
-null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=
-function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e=
-e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
-function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&(e={value:a,computed:b})});
-return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){f==0?b[0]=a:(d=Math.floor(Math.random()*(f+1)),b[f]=b[d],b[d]=a)});return b};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,g){return{value:a,criteria:c.call(d,a,b,g)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,
-c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:a.toArray?a.toArray():b.isArray(a)?i.call(a):b.isArguments(a)?i.call(a):b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=
-b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,h){if(0==h||(c===true?b.last(d)!=g:!b.include(d,g)))d[d.length]=g,e[e.length]=a[h];return d},[]);
-return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,
-d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(p&&a.indexOf===p)return a.indexOf(c);for(d=0,e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(D&&a.lastIndexOf===D)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g};
-var F=function(){};b.bind=function(a,c){var d,e;if(a.bind===s&&s)return s.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));F.prototype=a.prototype;var b=new F,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,
-c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i=b.debounce(function(){h=g=false},c);return function(){d=this;e=arguments;var b;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);i()},c));g?h=true:
-a.apply(d,e);i();g=true}};b.debounce=function(a,b){var d;return function(){var e=this,f=arguments;clearTimeout(d);d=setTimeout(function(){d=null;a.apply(e,f)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};
-b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments,
-1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};
-b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};
-b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};b.mixin=function(a){j(b.functions(a),
-function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+
-u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]=
-function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=
-true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);
diff --git a/distro/common/html/_static/up-pressed.png b/distro/common/html/_static/up-pressed.png
deleted file mode 100644
index 8bd587afee..0000000000
--- a/distro/common/html/_static/up-pressed.png
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/_static/up.png b/distro/common/html/_static/up.png
deleted file mode 100644
index b94625680b..0000000000
--- a/distro/common/html/_static/up.png
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/_static/websupport.js b/distro/common/html/_static/websupport.js
deleted file mode 100644
index 9186bee898..0000000000
--- a/distro/common/html/_static/websupport.js
+++ /dev/null
@@ -1,808 +0,0 @@
-/*
- * websupport.js
- * ~~~~~~~~~~~~~
- *
- * sphinx.websupport utilties for all documentation.
- *
- * :copyright: Copyright 2007-2016, by the Sphinx team, see AUTHORS.
- * :license: BSD, see LICENSE for details.
- *
- */
-
-(function($) {
- $.fn.autogrow = function() {
- return this.each(function() {
- var textarea = this;
-
- $.fn.autogrow.resize(textarea);
-
- $(textarea)
- .focus(function() {
- textarea.interval = setInterval(function() {
- $.fn.autogrow.resize(textarea);
- }, 500);
- })
- .blur(function() {
- clearInterval(textarea.interval);
- });
- });
- };
-
- $.fn.autogrow.resize = function(textarea) {
- var lineHeight = parseInt($(textarea).css('line-height'), 10);
- var lines = textarea.value.split('\n');
- var columns = textarea.cols;
- var lineCount = 0;
- $.each(lines, function() {
- lineCount += Math.ceil(this.length / columns) || 1;
- });
- var height = lineHeight * (lineCount + 1);
- $(textarea).css('height', height);
- };
-})(jQuery);
-
-(function($) {
- var comp, by;
-
- function init() {
- initEvents();
- initComparator();
- }
-
- function initEvents() {
- $('a.comment-close').live("click", function(event) {
- event.preventDefault();
- hide($(this).attr('id').substring(2));
- });
- $('a.vote').live("click", function(event) {
- event.preventDefault();
- handleVote($(this));
- });
- $('a.reply').live("click", function(event) {
- event.preventDefault();
- openReply($(this).attr('id').substring(2));
- });
- $('a.close-reply').live("click", function(event) {
- event.preventDefault();
- closeReply($(this).attr('id').substring(2));
- });
- $('a.sort-option').live("click", function(event) {
- event.preventDefault();
- handleReSort($(this));
- });
- $('a.show-proposal').live("click", function(event) {
- event.preventDefault();
- showProposal($(this).attr('id').substring(2));
- });
- $('a.hide-proposal').live("click", function(event) {
- event.preventDefault();
- hideProposal($(this).attr('id').substring(2));
- });
- $('a.show-propose-change').live("click", function(event) {
- event.preventDefault();
- showProposeChange($(this).attr('id').substring(2));
- });
- $('a.hide-propose-change').live("click", function(event) {
- event.preventDefault();
- hideProposeChange($(this).attr('id').substring(2));
- });
- $('a.accept-comment').live("click", function(event) {
- event.preventDefault();
- acceptComment($(this).attr('id').substring(2));
- });
- $('a.delete-comment').live("click", function(event) {
- event.preventDefault();
- deleteComment($(this).attr('id').substring(2));
- });
- $('a.comment-markup').live("click", function(event) {
- event.preventDefault();
- toggleCommentMarkupBox($(this).attr('id').substring(2));
- });
- }
-
- /**
- * Set comp, which is a comparator function used for sorting and
- * inserting comments into the list.
- */
- function setComparator() {
- // If the first three letters are "asc", sort in ascending order
- // and remove the prefix.
- if (by.substring(0,3) == 'asc') {
- var i = by.substring(3);
- comp = function(a, b) { return a[i] - b[i]; };
- } else {
- // Otherwise sort in descending order.
- comp = function(a, b) { return b[by] - a[by]; };
- }
-
- // Reset link styles and format the selected sort option.
- $('a.sel').attr('href', '#').removeClass('sel');
- $('a.by' + by).removeAttr('href').addClass('sel');
- }
-
- /**
- * Create a comp function. If the user has preferences stored in
- * the sortBy cookie, use those, otherwise use the default.
- */
- function initComparator() {
- by = 'rating'; // Default to sort by rating.
- // If the sortBy cookie is set, use that instead.
- if (document.cookie.length > 0) {
- var start = document.cookie.indexOf('sortBy=');
- if (start != -1) {
- start = start + 7;
- var end = document.cookie.indexOf(";", start);
- if (end == -1) {
- end = document.cookie.length;
- by = unescape(document.cookie.substring(start, end));
- }
- }
- }
- setComparator();
- }
-
- /**
- * Show a comment div.
- */
- function show(id) {
- $('#ao' + id).hide();
- $('#ah' + id).show();
- var context = $.extend({id: id}, opts);
- var popup = $(renderTemplate(popupTemplate, context)).hide();
- popup.find('textarea[name="proposal"]').hide();
- popup.find('a.by' + by).addClass('sel');
- var form = popup.find('#cf' + id);
- form.submit(function(event) {
- event.preventDefault();
- addComment(form);
- });
- $('#s' + id).after(popup);
- popup.slideDown('fast', function() {
- getComments(id);
- });
- }
-
- /**
- * Hide a comment div.
- */
- function hide(id) {
- $('#ah' + id).hide();
- $('#ao' + id).show();
- var div = $('#sc' + id);
- div.slideUp('fast', function() {
- div.remove();
- });
- }
-
- /**
- * Perform an ajax request to get comments for a node
- * and insert the comments into the comments tree.
- */
- function getComments(id) {
- $.ajax({
- type: 'GET',
- url: opts.getCommentsURL,
- data: {node: id},
- success: function(data, textStatus, request) {
- var ul = $('#cl' + id);
- var speed = 100;
- $('#cf' + id)
- .find('textarea[name="proposal"]')
- .data('source', data.source);
-
- if (data.comments.length === 0) {
- ul.html('<li>No comments yet.</li>');
- ul.data('empty', true);
- } else {
- // If there are comments, sort them and put them in the list.
- var comments = sortComments(data.comments);
- speed = data.comments.length * 100;
- appendComments(comments, ul);
- ul.data('empty', false);
- }
- $('#cn' + id).slideUp(speed + 200);
- ul.slideDown(speed);
- },
- error: function(request, textStatus, error) {
- showError('Oops, there was a problem retrieving the comments.');
- },
- dataType: 'json'
- });
- }
-
- /**
- * Add a comment via ajax and insert the comment into the comment tree.
- */
- function addComment(form) {
- var node_id = form.find('input[name="node"]').val();
- var parent_id = form.find('input[name="parent"]').val();
- var text = form.find('textarea[name="comment"]').val();
- var proposal = form.find('textarea[name="proposal"]').val();
-
- if (text == '') {
- showError('Please enter a comment.');
- return;
- }
-
- // Disable the form that is being submitted.
- form.find('textarea,input').attr('disabled', 'disabled');
-
- // Send the comment to the server.
- $.ajax({
- type: "POST",
- url: opts.addCommentURL,
- dataType: 'json',
- data: {
- node: node_id,
- parent: parent_id,
- text: text,
- proposal: proposal
- },
- success: function(data, textStatus, error) {
- // Reset the form.
- if (node_id) {
- hideProposeChange(node_id);
- }
- form.find('textarea')
- .val('')
- .add(form.find('input'))
- .removeAttr('disabled');
- var ul = $('#cl' + (node_id || parent_id));
- if (ul.data('empty')) {
- $(ul).empty();
- ul.data('empty', false);
- }
- insertComment(data.comment);
- var ao = $('#ao' + node_id);
- ao.find('img').attr({'src': opts.commentBrightImage});
- if (node_id) {
- // if this was a "root" comment, remove the commenting box
- // (the user can get it back by reopening the comment popup)
- $('#ca' + node_id).slideUp();
- }
- },
- error: function(request, textStatus, error) {
- form.find('textarea,input').removeAttr('disabled');
- showError('Oops, there was a problem adding the comment.');
- }
- });
- }
-
- /**
- * Recursively append comments to the main comment list and children
- * lists, creating the comment tree.
- */
- function appendComments(comments, ul) {
- $.each(comments, function() {
- var div = createCommentDiv(this);
- ul.append($(document.createElement('li')).html(div));
- appendComments(this.children, div.find('ul.comment-children'));
- // To avoid stagnating data, don't store the comments children in data.
- this.children = null;
- div.data('comment', this);
- });
- }
-
- /**
- * After adding a new comment, it must be inserted in the correct
- * location in the comment tree.
- */
- function insertComment(comment) {
- var div = createCommentDiv(comment);
-
- // To avoid stagnating data, don't store the comments children in data.
- comment.children = null;
- div.data('comment', comment);
-
- var ul = $('#cl' + (comment.node || comment.parent));
- var siblings = getChildren(ul);
-
- var li = $(document.createElement('li'));
- li.hide();
-
- // Determine where in the parents children list to insert this comment.
- for(i=0; i < siblings.length; i++) {
- if (comp(comment, siblings[i]) <= 0) {
- $('#cd' + siblings[i].id)
- .parent()
- .before(li.html(div));
- li.slideDown('fast');
- return;
- }
- }
-
- // If we get here, this comment rates lower than all the others,
- // or it is the only comment in the list.
- ul.append(li.html(div));
- li.slideDown('fast');
- }
-
- function acceptComment(id) {
- $.ajax({
- type: 'POST',
- url: opts.acceptCommentURL,
- data: {id: id},
- success: function(data, textStatus, request) {
- $('#cm' + id).fadeOut('fast');
- $('#cd' + id).removeClass('moderate');
- },
- error: function(request, textStatus, error) {
- showError('Oops, there was a problem accepting the comment.');
- }
- });
- }
-
- function deleteComment(id) {
- $.ajax({
- type: 'POST',
- url: opts.deleteCommentURL,
- data: {id: id},
- success: function(data, textStatus, request) {
- var div = $('#cd' + id);
- if (data == 'delete') {
- // Moderator mode: remove the comment and all children immediately
- div.slideUp('fast', function() {
- div.remove();
- });
- return;
- }
- // User mode: only mark the comment as deleted
- div
- .find('span.user-id:first')
- .text('[deleted]').end()
- .find('div.comment-text:first')
- .text('[deleted]').end()
- .find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id +
- ', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id)
- .remove();
- var comment = div.data('comment');
- comment.username = '[deleted]';
- comment.text = '[deleted]';
- div.data('comment', comment);
- },
- error: function(request, textStatus, error) {
- showError('Oops, there was a problem deleting the comment.');
- }
- });
- }
-
- function showProposal(id) {
- $('#sp' + id).hide();
- $('#hp' + id).show();
- $('#pr' + id).slideDown('fast');
- }
-
- function hideProposal(id) {
- $('#hp' + id).hide();
- $('#sp' + id).show();
- $('#pr' + id).slideUp('fast');
- }
-
- function showProposeChange(id) {
- $('#pc' + id).hide();
- $('#hc' + id).show();
- var textarea = $('#pt' + id);
- textarea.val(textarea.data('source'));
- $.fn.autogrow.resize(textarea[0]);
- textarea.slideDown('fast');
- }
-
- function hideProposeChange(id) {
- $('#hc' + id).hide();
- $('#pc' + id).show();
- var textarea = $('#pt' + id);
- textarea.val('').removeAttr('disabled');
- textarea.slideUp('fast');
- }
-
- function toggleCommentMarkupBox(id) {
- $('#mb' + id).toggle();
- }
-
- /** Handle when the user clicks on a sort by link. */
- function handleReSort(link) {
- var classes = link.attr('class').split(/\s+/);
- for (var i=0; i<classes.length; i++) {
- if (classes[i] != 'sort-option') {
- by = classes[i].substring(2);
- }
- }
- setComparator();
- // Save/update the sortBy cookie.
- var expiration = new Date();
- expiration.setDate(expiration.getDate() + 365);
- document.cookie= 'sortBy=' + escape(by) +
- ';expires=' + expiration.toUTCString();
- $('ul.comment-ul').each(function(index, ul) {
- var comments = getChildren($(ul), true);
- comments = sortComments(comments);
- appendComments(comments, $(ul).empty());
- });
- }
-
- /**
- * Function to process a vote when a user clicks an arrow.
- */
- function handleVote(link) {
- if (!opts.voting) {
- showError("You'll need to login to vote.");
- return;
- }
-
- var id = link.attr('id');
- if (!id) {
- // Didn't click on one of the voting arrows.
- return;
- }
- // If it is an unvote, the new vote value is 0,
- // Otherwise it's 1 for an upvote, or -1 for a downvote.
- var value = 0;
- if (id.charAt(1) != 'u') {
- value = id.charAt(0) == 'u' ? 1 : -1;
- }
- // The data to be sent to the server.
- var d = {
- comment_id: id.substring(2),
- value: value
- };
-
- // Swap the vote and unvote links.
- link.hide();
- $('#' + id.charAt(0) + (id.charAt(1) == 'u' ? 'v' : 'u') + d.comment_id)
- .show();
-
- // The div the comment is displayed in.
- var div = $('div#cd' + d.comment_id);
- var data = div.data('comment');
-
- // If this is not an unvote, and the other vote arrow has
- // already been pressed, unpress it.
- if ((d.value !== 0) && (data.vote === d.value * -1)) {
- $('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id).hide();
- $('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id).show();
- }
-
- // Update the comments rating in the local data.
- data.rating += (data.vote === 0) ? d.value : (d.value - data.vote);
- data.vote = d.value;
- div.data('comment', data);
-
- // Change the rating text.
- div.find('.rating:first')
- .text(data.rating + ' point' + (data.rating == 1 ? '' : 's'));
-
- // Send the vote information to the server.
- $.ajax({
- type: "POST",
- url: opts.processVoteURL,
- data: d,
- error: function(request, textStatus, error) {
- showError('Oops, there was a problem casting that vote.');
- }
- });
- }
-
- /**
- * Open a reply form used to reply to an existing comment.
- */
- function openReply(id) {
- // Swap out the reply link for the hide link
- $('#rl' + id).hide();
- $('#cr' + id).show();
-
- // Add the reply li to the children ul.
- var div = $(renderTemplate(replyTemplate, {id: id})).hide();
- $('#cl' + id)
- .prepend(div)
- // Setup the submit handler for the reply form.
- .find('#rf' + id)
- .submit(function(event) {
- event.preventDefault();
- addComment($('#rf' + id));
- closeReply(id);
- })
- .find('input[type=button]')
- .click(function() {
- closeReply(id);
- });
- div.slideDown('fast', function() {
- $('#rf' + id).find('textarea').focus();
- });
- }
-
- /**
- * Close the reply form opened with openReply.
- */
- function closeReply(id) {
- // Remove the reply div from the DOM.
- $('#rd' + id).slideUp('fast', function() {
- $(this).remove();
- });
-
- // Swap out the hide link for the reply link
- $('#cr' + id).hide();
- $('#rl' + id).show();
- }
-
- /**
- * Recursively sort a tree of comments using the comp comparator.
- */
- function sortComments(comments) {
- comments.sort(comp);
- $.each(comments, function() {
- this.children = sortComments(this.children);
- });
- return comments;
- }
-
- /**
- * Get the children comments from a ul. If recursive is true,
- * recursively include childrens' children.
- */
- function getChildren(ul, recursive) {
- var children = [];
- ul.children().children("[id^='cd']")
- .each(function() {
- var comment = $(this).data('comment');
- if (recursive)
- comment.children = getChildren($(this).find('#cl' + comment.id), true);
- children.push(comment);
- });
- return children;
- }
-
- /** Create a div to display a comment in. */
- function createCommentDiv(comment) {
- if (!comment.displayed && !opts.moderator) {
- return $('<div class="moderate">Thank you! Your comment will show up '
- + 'once it is has been approved by a moderator.</div>');
- }
- // Prettify the comment rating.
- comment.pretty_rating = comment.rating + ' point' +
- (comment.rating == 1 ? '' : 's');
- // Make a class (for displaying not yet moderated comments differently)
- comment.css_class = comment.displayed ? '' : ' moderate';
- // Create a div for this comment.
- var context = $.extend({}, opts, comment);
- var div = $(renderTemplate(commentTemplate, context));
-
- // If the user has voted on this comment, highlight the correct arrow.
- if (comment.vote) {
- var direction = (comment.vote == 1) ? 'u' : 'd';
- div.find('#' + direction + 'v' + comment.id).hide();
- div.find('#' + direction + 'u' + comment.id).show();
- }
-
- if (opts.moderator || comment.text != '[deleted]') {
- div.find('a.reply').show();
- if (comment.proposal_diff)
- div.find('#sp' + comment.id).show();
- if (opts.moderator && !comment.displayed)
- div.find('#cm' + comment.id).show();
- if (opts.moderator || (opts.username == comment.username))
- div.find('#dc' + comment.id).show();
- }
- return div;
- }
-
- /**
- * A simple template renderer. Placeholders such as <%id%> are replaced
- * by context['id'] with items being escaped. Placeholders such as <#id#>
- * are not escaped.
- */
- function renderTemplate(template, context) {
- var esc = $(document.createElement('div'));
-
- function handle(ph, escape) {
- var cur = context;
- $.each(ph.split('.'), function() {
- cur = cur[this];
- });
- return escape ? esc.text(cur || "").html() : cur;
- }
-
- return template.replace(/<([%#])([\w\.]*)\1>/g, function() {
- return handle(arguments[2], arguments[1] == '%' ? true : false);
- });
- }
-
- /** Flash an error message briefly. */
- function showError(message) {
- $(document.createElement('div')).attr({'class': 'popup-error'})
- .append($(document.createElement('div'))
- .attr({'class': 'error-message'}).text(message))
- .appendTo('body')
- .fadeIn("slow")
- .delay(2000)
- .fadeOut("slow");
- }
-
- /** Add a link the user uses to open the comments popup. */
- $.fn.comment = function() {
- return this.each(function() {
- var id = $(this).attr('id').substring(1);
- var count = COMMENT_METADATA[id];
- var title = count + ' comment' + (count == 1 ? '' : 's');
- var image = count > 0 ? opts.commentBrightImage : opts.commentImage;
- var addcls = count == 0 ? ' nocomment' : '';
- $(this)
- .append(
- $(document.createElement('a')).attr({
- href: '#',
- 'class': 'sphinx-comment-open' + addcls,
- id: 'ao' + id
- })
- .append($(document.createElement('img')).attr({
- src: image,
- alt: 'comment',
- title: title
- }))
- .click(function(event) {
- event.preventDefault();
- show($(this).attr('id').substring(2));
- })
- )
- .append(
- $(document.createElement('a')).attr({
- href: '#',
- 'class': 'sphinx-comment-close hidden',
- id: 'ah' + id
- })
- .append($(document.createElement('img')).attr({
- src: opts.closeCommentImage,
- alt: 'close',
- title: 'close'
- }))
- .click(function(event) {
- event.preventDefault();
- hide($(this).attr('id').substring(2));
- })
- );
- });
- };
-
- var opts = {
- processVoteURL: '/_process_vote',
- addCommentURL: '/_add_comment',
- getCommentsURL: '/_get_comments',
- acceptCommentURL: '/_accept_comment',
- deleteCommentURL: '/_delete_comment',
- commentImage: '/static/_static/comment.png',
- closeCommentImage: '/static/_static/comment-close.png',
- loadingImage: '/static/_static/ajax-loader.gif',
- commentBrightImage: '/static/_static/comment-bright.png',
- upArrow: '/static/_static/up.png',
- downArrow: '/static/_static/down.png',
- upArrowPressed: '/static/_static/up-pressed.png',
- downArrowPressed: '/static/_static/down-pressed.png',
- voting: false,
- moderator: false
- };
-
- if (typeof COMMENT_OPTIONS != "undefined") {
- opts = jQuery.extend(opts, COMMENT_OPTIONS);
- }
-
- var popupTemplate = '\
- <div class="sphinx-comments" id="sc<%id%>">\
- <p class="sort-options">\
- Sort by:\
- <a href="#" class="sort-option byrating">best rated</a>\
- <a href="#" class="sort-option byascage">newest</a>\
- <a href="#" class="sort-option byage">oldest</a>\
- </p>\
- <div class="comment-header">Comments</div>\
- <div class="comment-loading" id="cn<%id%>">\
- loading comments... <img src="<%loadingImage%>" alt="" /></div>\
- <ul id="cl<%id%>" class="comment-ul"></ul>\
- <div id="ca<%id%>">\
- <p class="add-a-comment">Add a comment\
- (<a href="#" class="comment-markup" id="ab<%id%>">markup</a>):</p>\
- <div class="comment-markup-box" id="mb<%id%>">\
- reStructured text markup: <i>*emph*</i>, <b>**strong**</b>, \
- <tt>``code``</tt>, \
- code blocks: <tt>::</tt> and an indented block after blank line</div>\
- <form method="post" id="cf<%id%>" class="comment-form" action="">\
- <textarea name="comment" cols="80"></textarea>\
- <p class="propose-button">\
- <a href="#" id="pc<%id%>" class="show-propose-change">\
- Propose a change &#9657;\
- </a>\
- <a href="#" id="hc<%id%>" class="hide-propose-change">\
- Propose a change &#9663;\
- </a>\
- </p>\
- <textarea name="proposal" id="pt<%id%>" cols="80"\
- spellcheck="false"></textarea>\
- <input type="submit" value="Add comment" />\
- <input type="hidden" name="node" value="<%id%>" />\
- <input type="hidden" name="parent" value="" />\
- </form>\
- </div>\
- </div>';
-
- var commentTemplate = '\
- <div id="cd<%id%>" class="sphinx-comment<%css_class%>">\
- <div class="vote">\
- <div class="arrow">\
- <a href="#" id="uv<%id%>" class="vote" title="vote up">\
- <img src="<%upArrow%>" />\
- </a>\
- <a href="#" id="uu<%id%>" class="un vote" title="vote up">\
- <img src="<%upArrowPressed%>" />\
- </a>\
- </div>\
- <div class="arrow">\
- <a href="#" id="dv<%id%>" class="vote" title="vote down">\
- <img src="<%downArrow%>" id="da<%id%>" />\
- </a>\
- <a href="#" id="du<%id%>" class="un vote" title="vote down">\
- <img src="<%downArrowPressed%>" />\
- </a>\
- </div>\
- </div>\
- <div class="comment-content">\
- <p class="tagline comment">\
- <span class="user-id"><%username%></span>\
- <span class="rating"><%pretty_rating%></span>\
- <span class="delta"><%time.delta%></span>\
- </p>\
- <div class="comment-text comment"><#text#></div>\
- <p class="comment-opts comment">\
- <a href="#" class="reply hidden" id="rl<%id%>">reply &#9657;</a>\
- <a href="#" class="close-reply" id="cr<%id%>">reply &#9663;</a>\
- <a href="#" id="sp<%id%>" class="show-proposal">proposal &#9657;</a>\
- <a href="#" id="hp<%id%>" class="hide-proposal">proposal &#9663;</a>\
- <a href="#" id="dc<%id%>" class="delete-comment hidden">delete</a>\
- <span id="cm<%id%>" class="moderation hidden">\
- <a href="#" id="ac<%id%>" class="accept-comment">accept</a>\
- </span>\
- </p>\
- <pre class="proposal" id="pr<%id%>">\
-<#proposal_diff#>\
- </pre>\
- <ul class="comment-children" id="cl<%id%>"></ul>\
- </div>\
- <div class="clearleft"></div>\
- </div>\
- </div>';
-
- var replyTemplate = '\
- <li>\
- <div class="reply-div" id="rd<%id%>">\
- <form id="rf<%id%>">\
- <textarea name="comment" cols="80"></textarea>\
- <input type="submit" value="Add reply" />\
- <input type="button" value="Cancel" />\
- <input type="hidden" name="parent" value="<%id%>" />\
- <input type="hidden" name="node" value="" />\
- </form>\
- </div>\
- </li>';
-
- $(document).ready(function() {
- init();
- });
-})(jQuery);
-
-$(document).ready(function() {
- // add comment anchors for all paragraphs that are commentable
- $('.sphinx-has-comment').comment();
-
- // highlight search words in search results
- $("div.context").each(function() {
- var params = $.getQueryParameters();
- var terms = (params.q) ? params.q[0].split(/\s+/) : [];
- var result = $(this);
- $.each(terms, function() {
- result.highlightText(this.toLowerCase(), 'highlighted');
- });
- });
-
- // directly open comment window if requested
- var anchor = document.location.hash;
- if (anchor.substring(0, 9) == '#comment-') {
- $('#ao' + anchor.substring(9)).click();
- document.location.hash = '#s' + anchor.substring(9);
- }
-});
diff --git a/distro/common/html/ctl_chef_client.html b/distro/common/html/ctl_chef_client.html
deleted file mode 100644
index dbdca11487..0000000000
--- a/distro/common/html/ctl_chef_client.html
+++ /dev/null
@@ -1,270 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>chef-client &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="chef-client">
-<h1>chef-client<a class="headerlink" href="#chef-client" title="Permalink to this headline">¶</a></h1>
-<p>A chef-client is an agent that runs locally on every node that is under management by Chef. When a chef-client is run, it will perform all of the steps that are required to bring the node into the expected state, including:</p>
-<ul class="simple">
-<li>Registering and authenticating the node with the Chef server</li>
-<li>Building the node object</li>
-<li>Synchronizing cookbooks</li>
-<li>Compiling the resource collection by loading each of the required cookbooks, including recipes, attributes, and all other dependencies</li>
-<li>Taking the appropriate and required actions to configure the node</li>
-<li>Looking for exceptions and notifications, handling each as required</li>
-</ul>
-<p>The chef-client executable is run as a command-line tool.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p>A client.rb file is used to specify the configuration details for the chef-client.</p>
-<ul class="last simple">
-<li>This file is loaded every time this executable is run</li>
-<li>On UNIX- and Linux-based machines, the default location for this file is <tt class="docutils literal"><span class="pre">/etc/chef/client.rb</span></tt>; on Microsoft Windows machines, the default location for this file is <tt class="docutils literal"><span class="pre">C:\chef\client.rb</span></tt>; use the <tt class="docutils literal"><span class="pre">--config</span></tt> option from the command line to change this location</li>
-<li>This file is not created by default</li>
-<li>When a client.rb file is present in this directory, the settings contained within that file will override the default configuration settings</li>
-</ul>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<p>This command has the following syntax:</p>
-<div class="highlight-python"><div class="highlight"><pre>chef-client OPTION VALUE OPTION VALUE ...
-</pre></div>
-</div>
-<p>This command has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-A</span></tt>, <tt class="docutils literal"><span class="pre">--fatal-windows-admin-check</span></tt></dt>
-<dd>Use to cause a chef-client run to fail when the chef-client does not have administrator privileges in Microsoft Windows.</dd>
-<dt><tt class="docutils literal"><span class="pre">--chef-zero-port</span> <span class="pre">PORT</span></tt></dt>
-<dd>The port on which chef-zero will listen. If a port is not specified&#8212;individually, as range of ports, or from the <tt class="docutils literal"><span class="pre">chef_zero.port</span></tt> setting in the client.rb file&#8212;the chef-client will scan for ports between 8889-9999 and will pick the first port that is available.</dd>
-<dt><tt class="docutils literal"><span class="pre">-F</span> <span class="pre">FORMAT</span></tt>, <tt class="docutils literal"><span class="pre">--format</span> <span class="pre">FORMAT</span></tt></dt>
-<dd><p class="first">The output format: <tt class="docutils literal"><span class="pre">doc</span></tt> (default) or <tt class="docutils literal"><span class="pre">min</span></tt>.</p>
-<p>Use <tt class="docutils literal"><span class="pre">doc</span></tt> to print the progress of the chef-client run using full strings that display a summary of updates as they occur.</p>
-<p>Use <tt class="docutils literal"><span class="pre">min</span></tt> to print the progress of the chef-client run using single characters. A summary of updates is printed at the end of the chef-client run. A dot (<tt class="docutils literal"><span class="pre">.</span></tt>) is printed for events that do not have meaningful status information, such as loading a file or synchronizing a cookbook. For resources, a dot (<tt class="docutils literal"><span class="pre">.</span></tt>) is printed when the resource is up to date, an <tt class="docutils literal"><span class="pre">S</span></tt> is printed when the resource is skipped by <tt class="docutils literal"><span class="pre">not_if</span></tt> or <tt class="docutils literal"><span class="pre">only_if</span></tt>, and a <tt class="docutils literal"><span class="pre">U</span></tt> is printed when the resource is updated.</p>
-<p class="last">Other formatting options are available when those formatters are configured in the client.rb file using the <tt class="docutils literal"><span class="pre">add_formatter</span></tt> option.</p>
-</dd>
-<dt><tt class="docutils literal"><span class="pre">--force-formatter</span></tt></dt>
-<dd>Use to show formatter output instead of logger output.</dd>
-<dt><tt class="docutils literal"><span class="pre">--force-logger</span></tt></dt>
-<dd>Use to show logger output instead of formatter output.</dd>
-<dt><tt class="docutils literal"><span class="pre">-g</span> <span class="pre">GROUP</span></tt>, <tt class="docutils literal"><span class="pre">--group</span> <span class="pre">GROUP</span></tt></dt>
-<dd>The name of the group that owns a process. This is required when starting any executable as a daemon.</dd>
-<dt><tt class="docutils literal"><span class="pre">-h</span></tt>, <tt class="docutils literal"><span class="pre">--help</span></tt></dt>
-<dd>Shows help for the command.</dd>
-<dt><tt class="docutils literal"><span class="pre">-i</span> <span class="pre">SECONDS</span></tt>, <tt class="docutils literal"><span class="pre">--interval</span> <span class="pre">SECONDS</span></tt></dt>
-<dd>The frequency (in seconds) at which the chef-client runs. When the chef-client is run at intervals, <tt class="docutils literal"><span class="pre">--splay</span></tt> and <tt class="docutils literal"><span class="pre">--interval</span></tt> values are applied before the chef-client run. Default value: <tt class="docutils literal"><span class="pre">1800</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-j</span> <span class="pre">PATH</span></tt>, <tt class="docutils literal"><span class="pre">--json-attributes</span> <span class="pre">PATH</span></tt></dt>
-<dd><p class="first">The path to a file that contains JSON data.</p>
-<p>Use this option to define a <tt class="docutils literal"><span class="pre">run_list</span></tt> object. For example, a JSON file similar to:</p>
-<div class="highlight-javascript"><div class="highlight"><pre><span class="s2">&quot;run_list&quot;</span><span class="o">:</span> <span class="p">[</span>
- <span class="s2">&quot;recipe[base]&quot;</span><span class="p">,</span>
- <span class="s2">&quot;recipe[foo]&quot;</span><span class="p">,</span>
- <span class="s2">&quot;recipe[bar]&quot;</span><span class="p">,</span>
- <span class="s2">&quot;role[webserver]&quot;</span>
-<span class="p">],</span>
-</pre></div>
-</div>
-<p>may be used by running <tt class="docutils literal"><span class="pre">chef-client</span> <span class="pre">-j</span> <span class="pre">path/to/file.json</span></tt>.</p>
-<p>In certain situations this option may be used to update <tt class="docutils literal"><span class="pre">normal</span></tt> attributes.</p>
-<div class="last admonition warning">
-<p class="first admonition-title">Warning</p>
-<p>Any other attribute type that is contained in this JSON file will be treated as a <tt class="docutils literal"><span class="pre">normal</span></tt> attribute. For example, attempting to update <tt class="docutils literal"><span class="pre">override</span></tt> attributes using the <tt class="docutils literal"><span class="pre">-j</span></tt> option:</p>
-<div class="highlight-javascript"><div class="highlight"><pre><span class="p">{</span>
- <span class="s2">&quot;name&quot;</span><span class="o">:</span> <span class="s2">&quot;dev-99&quot;</span><span class="p">,</span>
- <span class="s2">&quot;description&quot;</span><span class="o">:</span> <span class="s2">&quot;Install some stuff&quot;</span><span class="p">,</span>
- <span class="s2">&quot;override_attributes&quot;</span><span class="o">:</span> <span class="p">{</span>
- <span class="s2">&quot;apptastic&quot;</span><span class="o">:</span> <span class="p">{</span>
- <span class="s2">&quot;enable_apptastic&quot;</span><span class="o">:</span> <span class="s2">&quot;false&quot;</span><span class="p">,</span>
- <span class="s2">&quot;apptastic_tier_name&quot;</span><span class="o">:</span> <span class="s2">&quot;dev-99.bomb.com&quot;</span>
- <span class="p">}</span>
- <span class="p">}</span>
-<span class="p">}</span>
-</pre></div>
-</div>
-<p>will result in a node object similar to:</p>
-<div class="last highlight-javascript"><div class="highlight"><pre><span class="p">{</span>
- <span class="s2">&quot;name&quot;</span><span class="o">:</span> <span class="s2">&quot;maybe-dev-99&quot;</span><span class="p">,</span>
- <span class="s2">&quot;normal&quot;</span><span class="o">:</span> <span class="p">{</span>
- <span class="s2">&quot;name&quot;</span><span class="o">:</span> <span class="s2">&quot;dev-99&quot;</span><span class="p">,</span>
- <span class="s2">&quot;description&quot;</span><span class="o">:</span> <span class="s2">&quot;Install some stuff&quot;</span><span class="p">,</span>
- <span class="s2">&quot;override_attributes&quot;</span><span class="o">:</span> <span class="p">{</span>
- <span class="s2">&quot;apptastic&quot;</span><span class="o">:</span> <span class="p">{</span>
- <span class="s2">&quot;enable_apptastic&quot;</span><span class="o">:</span> <span class="s2">&quot;false&quot;</span><span class="p">,</span>
- <span class="s2">&quot;apptastic_tier_name&quot;</span><span class="o">:</span> <span class="s2">&quot;dev-99.bomb.com&quot;</span>
- <span class="p">}</span>
- <span class="p">}</span>
- <span class="p">}</span>
-<span class="p">}</span>
-</pre></div>
-</div>
-</div>
-</dd>
-<dt><tt class="docutils literal"><span class="pre">-k</span> <span class="pre">KEY_FILE</span></tt>, <tt class="docutils literal"><span class="pre">--client_key</span> <span class="pre">KEY_FILE</span></tt></dt>
-<dd>The location of the file which contains the client key. Default value: <tt class="docutils literal"><span class="pre">/etc/chef/client.pem</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-K</span> <span class="pre">KEY_FILE</span></tt>, <tt class="docutils literal"><span class="pre">--validation_key</span> <span class="pre">KEY_FILE</span></tt></dt>
-<dd>The location of the file which contains the key used when a chef-client is registered with a Chef server. A validation key is signed using the <tt class="docutils literal"><span class="pre">validation_client_name</span></tt> for authentication. Default value: <tt class="docutils literal"><span class="pre">/etc/chef/validation.pem</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-l</span> <span class="pre">LEVEL</span></tt>, <tt class="docutils literal"><span class="pre">--log_level</span> <span class="pre">LEVEL</span></tt></dt>
-<dd>The level of logging that will be stored in a log file.</dd>
-<dt><tt class="docutils literal"><span class="pre">-L</span> <span class="pre">LOGLOCATION</span></tt>, <tt class="docutils literal"><span class="pre">--logfile</span> <span class="pre">c</span></tt></dt>
-<dd>The location in which log file output files will be saved. If this location is set to something other than <tt class="docutils literal"><span class="pre">STDOUT</span></tt>, standard output logging will still be performed (otherwise there would be no output other than to a file). This is recommended when starting any executable as a daemon. Default value: <tt class="docutils literal"><span class="pre">STDOUT</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]color</span></tt></dt>
-<dd>Use to view colored output. Default setting: <tt class="docutils literal"><span class="pre">--color</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-N</span> <span class="pre">NODE_NAME</span></tt>, <tt class="docutils literal"><span class="pre">--node-name</span> <span class="pre">NODE_NAME</span></tt></dt>
-<dd>The name of the node.</dd>
-<dt><tt class="docutils literal"><span class="pre">-o</span> <span class="pre">RUN_LIST_ITEM</span></tt>, <tt class="docutils literal"><span class="pre">--override-runlist</span> <span class="pre">RUN_LIST_ITEM</span></tt></dt>
-<dd>Replace the current run list with the specified items. This option will not clear the list of cookbooks (and related files) that is cached on the node.</dd>
-<dt><tt class="docutils literal"><span class="pre">--once</span></tt></dt>
-<dd>Use to run the chef-client only once and to cancel <tt class="docutils literal"><span class="pre">interval</span></tt> and <tt class="docutils literal"><span class="pre">splay</span></tt> options.</dd>
-<dt><tt class="docutils literal"><span class="pre">-P</span> <span class="pre">PID_FILE</span></tt>, <tt class="docutils literal"><span class="pre">--pid</span> <span class="pre">PID_FILE</span></tt></dt>
-<dd>The location in which a process identification number (pid) is saved. An executable, when started as a daemon, will write the pid to the specified file. Default value: <tt class="docutils literal"><span class="pre">/tmp/name-of-executable.pid</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-r</span> <span class="pre">RUN_LIST_ITEM</span></tt>, <tt class="docutils literal"><span class="pre">--runlist</span> <span class="pre">RUN_LIST_ITEM</span></tt></dt>
-<dd>Use to permanently replace the current run-list with the specified run-list items.</dd>
-<dt><tt class="docutils literal"><span class="pre">-R</span></tt>, <tt class="docutils literal"><span class="pre">--enable-reporting</span></tt></dt>
-<dd>Use to enable Chef reporting, which performs data collection during a chef-client run.</dd>
-<dt><tt class="docutils literal"><span class="pre">RECIPE_FILE</span></tt></dt>
-<dd>The path to a recipe. For example, if a recipe file is in the current directory, use <tt class="docutils literal"><span class="pre">recipe_file.rb</span></tt>. This is typically used with the <tt class="docutils literal"><span class="pre">--local-mode</span></tt> option.</dd>
-<dt><tt class="docutils literal"><span class="pre">--run-lock-timeout</span> <span class="pre">SECONDS</span></tt></dt>
-<dd>The amount of time (in seconds) to wait for a chef-client run to finish. Default value: not set (indefinite). Set to <tt class="docutils literal"><span class="pre">0</span></tt> to cause a second chef-client to exit immediately.</dd>
-<dt><tt class="docutils literal"><span class="pre">-s</span> <span class="pre">SECONDS</span></tt>, <tt class="docutils literal"><span class="pre">--splay</span> <span class="pre">SECONDS</span></tt></dt>
-<dd>A number (in seconds) to add to the <tt class="docutils literal"><span class="pre">interval</span></tt> that is used to determine the frequency of chef-client runs. This number can help prevent server load when there are many clients running at the same time. When the chef-client is run at intervals, <tt class="docutils literal"><span class="pre">--splay</span></tt> and <tt class="docutils literal"><span class="pre">--interval</span></tt> values are applied before the chef-client run.</dd>
-<dt><tt class="docutils literal"><span class="pre">-S</span> <span class="pre">CHEF_SERVER_URL</span></tt>, <tt class="docutils literal"><span class="pre">--server</span> <span class="pre">CHEF_SERVER_URL</span></tt></dt>
-<dd>The URL for the Chef server.</dd>
-<dt><tt class="docutils literal"><span class="pre">-u</span> <span class="pre">USER</span></tt>, <tt class="docutils literal"><span class="pre">--user</span> <span class="pre">USER</span></tt></dt>
-<dd>The user that owns a process. This is required when starting any executable as a daemon.</dd>
-<dt><tt class="docutils literal"><span class="pre">-v</span></tt>, <tt class="docutils literal"><span class="pre">--version</span></tt></dt>
-<dd>The version of the chef-client.</dd>
-<dt><tt class="docutils literal"><span class="pre">-W</span></tt>, <tt class="docutils literal"><span class="pre">--why-run</span></tt></dt>
-<dd>Use to run the executable in why-run mode, which is a type of chef-client run that does everything except modify the system. Use why-run mode to understand why the chef-client makes the decisions that it makes and to learn more about the current and proposed state of the system.</dd>
-<dt><tt class="docutils literal"><span class="pre">-z</span></tt>, <tt class="docutils literal"><span class="pre">--local-mode</span></tt></dt>
-<dd>Use to run the chef-client in local mode. This allows all commands that work against the Chef server to also work against the local chef-repo.</dd>
-</dl>
-</div>
-<div class="section" id="run-with-elevated-privileges">
-<h2>Run with Elevated Privileges<a class="headerlink" href="#run-with-elevated-privileges" title="Permalink to this headline">¶</a></h2>
-<p>The chef-client may need to be run with elevated privileges in order to get a recipe to converge correctly. On UNIX and UNIX-like operating systems this can be done by running the command as root. On Microsoft Windows this can be done by running the command prompt as an administrator.</p>
-<div class="section" id="linux">
-<h3>Linux<a class="headerlink" href="#linux" title="Permalink to this headline">¶</a></h3>
-<p>On Linux, the following error sometimes occurs when the permissions used to run the chef-client are incorrect:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-client
-<span class="o">[</span>Tue, 29 Nov 2011 19:46:17 -0800<span class="o">]</span> INFO: *** Chef 10.X.X ***
-<span class="o">[</span>Tue, 29 Nov 2011 19:46:18 -0800<span class="o">]</span> WARN: Failed to <span class="nb">read </span>the private key /etc/chef/client.pem: <span class="c">#&lt;Errno::EACCES: Permission denied - /etc/chef/client.pem&gt;</span>
-</pre></div>
-</div>
-<p>This can be resolved by running the command as root. There are a few ways this can be done:</p>
-<ul>
-<li><p class="first">Log in as root and then run the chef-client</p>
-</li>
-<li><p class="first">Use <tt class="docutils literal"><span class="pre">su</span></tt> to become the root user, and then run the chef-client. For example:</p>
-<blockquote>
-<div><div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>su
-</pre></div>
-</div>
-<p>and then:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-client
-</pre></div>
-</div>
-</div></blockquote>
-</li>
-<li><p class="first">Use the sudo utility</p>
-<blockquote>
-<div><div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo chef-client
-</pre></div>
-</div>
-</div></blockquote>
-</li>
-<li><p class="first">Give a user access to read <tt class="docutils literal"><span class="pre">/etc/chef</span></tt> and also the files accessed by the chef-client. This requires super user privileges and, as such, is not a recommended approach</p>
-</li>
-</ul>
-</div>
-<div class="section" id="windows">
-<h3>Windows<a class="headerlink" href="#windows" title="Permalink to this headline">¶</a></h3>
-<p>On Microsoft Windows, running without elevated privileges (when they are necessary) is an issue that fails silently. It will appear that the chef-client completed its run successfully, but the changes will not have been made. When this occurs, do one of the following to run the chef-client as the administrator:</p>
-<ul>
-<li><p class="first">Log in to the administrator account. (This is not the same as an account in the administrator&#8217;s security group.)</p>
-</li>
-<li><p class="first">Run the chef-client process from the administrator account while being logged into another account. Run the following command:</p>
-<blockquote>
-<div><div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>runas /user:Administrator <span class="s2">&quot;cmd /C chef-client&quot;</span>
-</pre></div>
-</div>
-<p>This will prompt for the administrator account password.</p>
-</div></blockquote>
-</li>
-<li><p class="first">Open a command prompt by right-clicking on the command prompt application, and then selecting <strong>Run as administrator</strong>. After the command window opens, the chef-client can be run as the administrator</p>
-</li>
-</ul>
-</div>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p><strong>Start a Chef run when the chef-client is running as a daemon</strong></p>
-<p>A chef-client that is running as a daemon can be woken up and started by sending the process a <tt class="docutils literal"><span class="pre">SIGUSR1</span></tt>. For example, to trigger a chef-client run on a machine running Linux:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo killall -USR1 chef-client
-</pre></div>
-</div>
-<p><strong>Start a Chef run manually</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>ps auxw|grep chef-client
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>root 66066 0.9 0.0 2488880 264 s001 S+ 10:26AM 0:03.05
-/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby /usr/bin/chef-client -i 3600 -s 20
-</pre></div>
-</div>
-<p>and then enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>sudo <span class="nb">kill</span> -USR1 66066
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/ctl_chef_server.html b/distro/common/html/ctl_chef_server.html
deleted file mode 100644
index c5bc97af08..0000000000
--- a/distro/common/html/ctl_chef_server.html
+++ /dev/null
@@ -1,728 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>chef-server-ctl (executable) &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="chef-server-ctl-executable">
-<h1>chef-server-ctl (executable)<a class="headerlink" href="#chef-server-ctl-executable" title="Permalink to this headline">¶</a></h1>
-<p>The Chef server includes a command-line utility named chef-server-ctl. This command-line tool is used to start and stop individual services, reconfigure the Chef server, run chef-pedant, and then tail Chef server log files.</p>
-<div class="section" id="backup-recover">
-<h2>backup-recover<a class="headerlink" href="#backup-recover" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">backup-recover</span></tt> subcommand is used to force the Chef server to attempt to become the backup server. This is the opposite of the <tt class="docutils literal"><span class="pre">master-recover</span></tt> subcommand.</p>
-<div class="admonition warning">
-<p class="first admonition-title">Warning</p>
-<p class="last">If this command is run on both back-end servers, it will put the back-end cluster into a state where no server holds the DRBD resource.</p>
-</div>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl backup-recover
-</pre></div>
-</div>
-</div>
-<div class="section" id="cleanse">
-<h2>cleanse<a class="headerlink" href="#cleanse" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">cleanse</span></tt> subcommand is used to re-set the Chef server to the state it was in before the first time the <tt class="docutils literal"><span class="pre">reconfigure</span></tt> subcommand is run to destroy all data, configuration files, and logs.</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl cleanse
-</pre></div>
-</div>
-</div>
-<div class="section" id="gather-logs">
-<h2>gather-logs<a class="headerlink" href="#gather-logs" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">gather-logs</span></tt> subcommand is used to gather the Chef server log files into a tarball that contains all of the important log files and system information.</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl gather-logs
-</pre></div>
-</div>
-</div>
-<div class="section" id="ha-status">
-<h2>ha-status<a class="headerlink" href="#ha-status" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">ha-status</span></tt> subcommand is used to check the status for services running in a High Availability topology. This command will verify the following:</p>
-<blockquote>
-<div><ul class="simple">
-<li>The Keepalived daemon is enabled in the config</li>
-<li>The DRBD process is enabled in the config</li>
-<li>The underlying block device or logical volume for DRBD has been created and configured</li>
-<li>The DRBD device exists</li>
-<li>The current state of the server is <tt class="docutils literal"><span class="pre">master</span></tt> or <tt class="docutils literal"><span class="pre">backup</span></tt>; any migration processes have completed</li>
-<li>The failover virtual IP address is correctly attached to only the <tt class="docutils literal"><span class="pre">master</span></tt> node</li>
-<li>The DRBD state is correct based on the state of the server being <tt class="docutils literal"><span class="pre">master</span></tt> or <tt class="docutils literal"><span class="pre">backup</span></tt></li>
-<li>The DRBD mount point is correctly mounted to only the <tt class="docutils literal"><span class="pre">master</span></tt> node</li>
-<li>The DRBD replication IP addresses are pingable</li>
-<li>The <tt class="docutils literal"><span class="pre">runit</span></tt> status of the services are correct (up or down) based on the <tt class="docutils literal"><span class="pre">master</span></tt> or <tt class="docutils literal"><span class="pre">backup</span></tt> state of the server</li>
-</ul>
-</div></blockquote>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl ha-status
-</pre></div>
-</div>
-<p>If this command runs successfully, it will return the following:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span><span class="o">[</span>OK<span class="o">]</span> all checks passed.
-</pre></div>
-</div>
-<p>Otherwise it will print out a list of errors, similar to the following:</p>
-<div class="highlight-bash"><div class="highlight"><pre>...
-<span class="o">[</span>OK<span class="o">]</span> nginx is running correctly, and I am master.
-<span class="o">[</span>ERROR<span class="o">]</span> redis_lb is not running.
-<span class="o">[</span>OK<span class="o">]</span> opscode-erchef is running correctly, and I am master.
-...
-<span class="o">[</span>ERROR<span class="o">]</span> ERRORS WERE DETECTED.
-</pre></div>
-</div>
-<p>For example:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="o">[</span>OK<span class="o">]</span> keepalived HA services enabled
-<span class="o">[</span>OK<span class="o">]</span> DRBD disk replication enabled
-<span class="o">[</span>OK<span class="o">]</span> DRBD partition /dev/opscode/drbd found
-<span class="o">[</span>OK<span class="o">]</span> DRBD device /dev/drbd0 found
-<span class="o">[</span>OK<span class="o">]</span> cluster <span class="nv">status</span> <span class="o">=</span> master
-<span class="o">[</span>OK<span class="o">]</span> found VIP IP address and I am master
-<span class="o">[</span>OK<span class="o">]</span> found VRRP communications interface eth1
-<span class="o">[</span>OK<span class="o">]</span> my DRBD status is Connected/Primary/UpToDate and I am master
-<span class="o">[</span>OK<span class="o">]</span> my DRBD partition is mounted and I am master
-<span class="o">[</span>OK<span class="o">]</span> DRBD primary IP address pings
-<span class="o">[</span>OK<span class="o">]</span> DRBD secondary IP address pings
-...
-<span class="o">[</span>OK<span class="o">]</span> all checks passed.
-</pre></div>
-</div>
-</div>
-<div class="section" id="help">
-<h2>help<a class="headerlink" href="#help" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">help</span></tt> subcommand is used to print a list of all available chef-server-ctl commands.</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl <span class="nb">help</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="install">
-<h2>install<a class="headerlink" href="#install" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">install</span></tt> subcommand is used to install premium features of the Chef server: Chef management console, Chef analytics, chef-client run reporting, high availability configurations, Chef push jobs, and Chef server replication.</p>
-<p><strong>Syntax</strong></p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl install name_of_premium_feature <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">name_of_premium_feature</span></tt> represents the command line value associated with the premium feature:</p>
-<p><strong>Options</strong></p>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">--path</span> <span class="pre">PATH</span></tt></dt>
-<dd>Use to specify the location of a package. This option is not required when packages are downloaded from <a class="reference external" href="https://packagecloud.io/">https://packagecloud.io/</a>.</dd>
-</dl>
-<div class="section" id="use-downloads">
-<h3>Use Downloads<a class="headerlink" href="#use-downloads" title="Permalink to this headline">¶</a></h3>
-<p>The <tt class="docutils literal"><span class="pre">install</span></tt> subcommand downloads packages from <a class="reference external" href="https://packagecloud.io/">https://packagecloud.io/</a> by default. For systems that are not behind a firewall (and have connectivity to <a class="reference external" href="https://packagecloud.io/">https://packagecloud.io/</a>), these packages can be installed as described below.</p>
-<table border="1" class="docutils">
-<colgroup>
-<col width="20%" />
-<col width="80%" />
-</colgroup>
-<thead valign="bottom">
-<tr class="row-odd"><th class="head">Feature</th>
-<th class="head">Command</th>
-</tr>
-</thead>
-<tbody valign="top">
-<tr class="row-even"><td>Chef Manage</td>
-<td><p class="first">Use Chef management console to manage data bags, attributes, run-lists, roles, environments, and cookbooks from a web user interface.</p>
-<p>(Front end machines only.) Run:</p>
-<div class="highlight-ruby"><div class="highlight"><pre>$ chef-server-ctl install opscode-manage
-</pre></div>
-</div>
-<p>then:</p>
-<div class="highlight-ruby"><div class="highlight"><pre>$ opscode-manage-ctl reconfigure
-</pre></div>
-</div>
-<p>and then:</p>
-<div class="last highlight-ruby"><div class="highlight"><pre>$ chef-server-ctl reconfigure
-</pre></div>
-</div>
-</td>
-</tr>
-<tr class="row-odd"><td>Chef Push Jobs</td>
-<td><p class="first">Use Chef push jobs to run jobs&#8212;an action or a command to be executed&#8212;against nodes independently of a chef-client run.</p>
-<p>Run:</p>
-<div class="highlight-ruby"><div class="highlight"><pre>$ chef-server-ctl install opscode-push-jobs-server
-</pre></div>
-</div>
-<p>then:</p>
-<div class="highlight-ruby"><div class="highlight"><pre>$ opscode-push-jobs-server-ctl reconfigure
-</pre></div>
-</div>
-<p>and then:</p>
-<div class="last highlight-ruby"><div class="highlight"><pre>$ chef-server-ctl reconfigure
-</pre></div>
-</div>
-</td>
-</tr>
-<tr class="row-even"><td>Chef Replication</td>
-<td><p class="first">Use Chef replication to asynchronously distribute cookbook, environment, role, and data bag data from a single, primary Chef server to one (or more) replicas of that Chef server.</p>
-<p>Run:</p>
-<div class="highlight-ruby"><div class="highlight"><pre>$ chef-server-ctl install chef-sync
-</pre></div>
-</div>
-<p>and then:</p>
-<div class="highlight-ruby"><div class="highlight"><pre>$ chef-sync-ctl reconfigure
-</pre></div>
-</div>
-<p>and then:</p>
-<div class="last highlight-ruby"><div class="highlight"><pre>$ chef-server-ctl reconfigure
-</pre></div>
-</div>
-</td>
-</tr>
-<tr class="row-odd"><td>Reporting</td>
-<td><p class="first">Use Chef reporting to keep track of what happens during every chef-client runs across all of the infrastructure being managed by Chef. Run Chef reporting with Chef management console to view reports from a web user interface.</p>
-<p>(Front end machines only.) Run:</p>
-<div class="highlight-ruby"><div class="highlight"><pre>$ chef-server-ctl install opscode-reporting
-</pre></div>
-</div>
-<p>then:</p>
-<div class="highlight-ruby"><div class="highlight"><pre>$ opscode-reporting-ctl reconfigure
-</pre></div>
-</div>
-<p>and then:</p>
-<div class="last highlight-ruby"><div class="highlight"><pre>$ chef-server-ctl reconfigure
-</pre></div>
-</div>
-</td>
-</tr>
-</tbody>
-</table>
-</div>
-<div class="section" id="use-local-packages">
-<h3>Use Local Packages<a class="headerlink" href="#use-local-packages" title="Permalink to this headline">¶</a></h3>
-<p>The <tt class="docutils literal"><span class="pre">install</span></tt> subcommand downloads packages from <a class="reference external" href="https://packagecloud.io/">https://packagecloud.io/</a> by default. For systems that are behind a firewall (and do not have connectivity to <a class="reference external" href="https://packagecloud.io/">https://packagecloud.io/</a>), these packages can be installed manually. First download the package that is appropriate for the platform and save it to a local path. Then run the <tt class="docutils literal"><span class="pre">install</span></tt> command using the <tt class="docutils literal"><span class="pre">--path</span></tt> option to specify the location for the package:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl install NAME_OF_PACKAGE --path /path/to/package
-</pre></div>
-</div>
-<p>For example:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl install opscode-manage-1.6.2-1.el6.x86_64 --path /home/vagrant
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="master-recover">
-<h2>master-recover<a class="headerlink" href="#master-recover" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">master-recover</span></tt> subcommand is used to force the Chef server to attempt to become the master server. This command is typically run in tandem with the <tt class="docutils literal"><span class="pre">backup-recover</span></tt> subcommand on the back-end peer, unless the back-end peer is no longer available.</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl master-recover
-</pre></div>
-</div>
-</div>
-<div class="section" id="org-create">
-<h2>org-create<a class="headerlink" href="#org-create" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">org-create</span></tt> subcommand is used to create an organization. (The validation key for the organization is returned to <tt class="docutils literal"><span class="pre">STDOUT</span></tt> when creating an organization using this command.)</p>
-<p><strong>Syntax</strong></p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-create ORG_NAME ORG_FULL_NAME <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-<p><strong>Options</strong></p>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span> <span class="pre">USER_NAME</span></tt>, <tt class="docutils literal"><span class="pre">--association_user</span> <span class="pre">USER_NAME</span></tt></dt>
-<dd>Use to associate a user with an organization and add them to the <tt class="docutils literal"><span class="pre">admins</span></tt> and <tt class="docutils literal"><span class="pre">billing_admins</span></tt> security groups.</dd>
-<dt><tt class="docutils literal"><span class="pre">-f</span> <span class="pre">FILE_NAME</span></tt>, <tt class="docutils literal"><span class="pre">--filename</span> <span class="pre">FILE_NAME</span></tt></dt>
-<dd>Use to write the private key to a file instead of <tt class="docutils literal"><span class="pre">STDOUT</span></tt>.</dd>
-</dl>
-<p><strong>Examples</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-create prod Production
-</pre></div>
-</div>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-create staging Staging -a chef-admin
-</pre></div>
-</div>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-create dev Development -f /tmp/id-dev.key
-</pre></div>
-</div>
-</div>
-<div class="section" id="org-delete">
-<h2>org-delete<a class="headerlink" href="#org-delete" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">org-delete</span></tt> subcommand is used to delete an organization.</p>
-<p><strong>Syntax</strong></p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-delete ORG_NAME
-</pre></div>
-</div>
-<p><strong>Examples</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-delete infra-testing-20140909
-</pre></div>
-</div>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-delete pedant-testing-org
-</pre></div>
-</div>
-</div>
-<div class="section" id="org-list">
-<h2>org-list<a class="headerlink" href="#org-list" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">org-list</span></tt> subcommand is used to list all of the organizations currently present on the Chef server.</p>
-<p><strong>Syntax</strong></p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-list <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-<p><strong>Options</strong></p>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span></tt>, <tt class="docutils literal"><span class="pre">--all-orgs</span></tt></dt>
-<dd>Use to show all organizations.</dd>
-<dt><tt class="docutils literal"><span class="pre">-w</span></tt>, <tt class="docutils literal"><span class="pre">--with-uri</span></tt></dt>
-<dd>Use to show the corresponding URIs.</dd>
-</dl>
-</div>
-<div class="section" id="org-show">
-<h2>org-show<a class="headerlink" href="#org-show" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">org-show</span></tt> subcommand is used to show the details for an organization.</p>
-<p><strong>Syntax</strong></p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-show ORG_NAME
-</pre></div>
-</div>
-</div>
-<div class="section" id="org-user-add">
-<h2>org-user-add<a class="headerlink" href="#org-user-add" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">org-user-add</span></tt> subcommand is used to add a user to an organization.</p>
-<p><strong>Syntax</strong></p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-user-add ORG_NAME USER_NAME <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-<p><strong>Options</strong></p>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">--admin</span></tt></dt>
-<dd>Use to add the user to the <tt class="docutils literal"><span class="pre">admins</span></tt> group.</dd>
-</dl>
-<p><strong>Examples</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-user-add prod john_smith
-</pre></div>
-</div>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-user-add preprod testmaster
-</pre></div>
-</div>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-user-add dev grantmc --admin
-</pre></div>
-</div>
-</div>
-<div class="section" id="org-user-remove">
-<h2>org-user-remove<a class="headerlink" href="#org-user-remove" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">org-user-remove</span></tt> subcommand is used to remove a user from an organization.</p>
-<p><strong>Syntax</strong></p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-user-remove ORG_NAME USER_NAME
-</pre></div>
-</div>
-<p><strong>Examples</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-user-remove prod john_smith
-</pre></div>
-</div>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl org-user-remove prod testmaster
-</pre></div>
-</div>
-</div>
-<div class="section" id="password">
-<h2>password<a class="headerlink" href="#password" title="Permalink to this headline">¶</a></h2>
-<div class="admonition warning">
-<p class="first admonition-title">Warning</p>
-<p class="last">This subcommand is currently disabled.</p>
-</div>
-</div>
-<div class="section" id="reconfigure">
-<h2>reconfigure<a class="headerlink" href="#reconfigure" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">reconfigure</span></tt> subcommand is used when changes are made to the private-chef.rb file to reconfigure the server. When changes are made to the private-chef.rb file, they will not be applied to the Chef server configuration until after this command is run. This subcommand will also restart any services for which the <tt class="docutils literal"><span class="pre">service_name['enabled']</span></tt> setting is set to <tt class="docutils literal"><span class="pre">true</span></tt>.</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl reconfigure
-</pre></div>
-</div>
-</div>
-<div class="section" id="show-config">
-<h2>show-config<a class="headerlink" href="#show-config" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">show-config</span></tt> subcommand is used to view the configuration that will be generated by the <tt class="docutils literal"><span class="pre">reconfigure</span></tt> subcommand. This command is most useful in the early stages of a deployment to ensure that everything is built properly prior to installation.</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl show-config
-</pre></div>
-</div>
-</div>
-<div class="section" id="uninstall">
-<h2>uninstall<a class="headerlink" href="#uninstall" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">uninstall</span></tt> subcommand is used to remove the Chef server application, but without removing any of the data. This subcommand will shut down all services (including the <tt class="docutils literal"><span class="pre">runit</span></tt> process supervisor).</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl uninstall
-</pre></div>
-</div>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">To revert the <tt class="docutils literal"><span class="pre">uninstall</span></tt> subcommand, run the <tt class="docutils literal"><span class="pre">reconfigure</span></tt> subcommand (because the <tt class="docutils literal"><span class="pre">start</span></tt> subcommand is disabled by the <tt class="docutils literal"><span class="pre">uninstall</span></tt> command).</p>
-</div>
-</div>
-<div class="section" id="upgrade">
-<h2>upgrade<a class="headerlink" href="#upgrade" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">upgrade</span></tt> subcommand is used to upgrade the Chef server.</p>
-<p><strong>Syntax</strong></p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl upgrade <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-<p><strong>Options</strong></p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Options for the <tt class="docutils literal"><span class="pre">upgrade</span></tt> subcommand may only be used when upgrading from Open Source Chef 11 to Chef server 12.</p>
-</div>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-d</span> <span class="pre">DIRECTORY</span></tt>, <tt class="docutils literal"><span class="pre">--chef11-data-dir</span> <span class="pre">DIRECTORY</span></tt></dt>
-<dd>The directory in which Open Source Chef 11 data is located. Default value: a temporary directory.</dd>
-<dt><tt class="docutils literal"><span class="pre">-e</span> <span class="pre">DIRECTORY</span></tt>, <tt class="docutils literal"><span class="pre">--chef12-data-dir</span> <span class="pre">DIRECTORY</span></tt></dt>
-<dd>The directory in which Chef server 12 data is located. Default value: a temporary directory.</dd>
-<dt><tt class="docutils literal"><span class="pre">-f</span> <span class="pre">FULL_NAME</span></tt>, <tt class="docutils literal"><span class="pre">--full-org-name</span> <span class="pre">FULL_NAME</span></tt></dt>
-<dd>The full name of the Chef server organization. A full organization name must begin with a non-white space character and must be between 1 and 1023 characters. For example: <tt class="docutils literal"><span class="pre">Chef</span> <span class="pre">Software,</span> <span class="pre">Inc.</span></tt>. If this option is not specified, the <tt class="docutils literal"><span class="pre">upgrade</span></tt> command will prompt for it.</dd>
-<dt><tt class="docutils literal"><span class="pre">-h</span></tt>, <tt class="docutils literal"><span class="pre">--help</span></tt></dt>
-<dd>Use to show help for the <tt class="docutils literal"><span class="pre">chef-server-ctl</span> <span class="pre">upgrade</span></tt> subcommand.</dd>
-<dt><tt class="docutils literal"><span class="pre">-k</span> <span class="pre">KEY</span></tt>, <tt class="docutils literal"><span class="pre">--key</span> <span class="pre">KEY</span></tt></dt>
-<dd>All users are assigned a public key. Use to write the public key to a file. Default value: <tt class="docutils literal"><span class="pre">/etc/chef-server/admin.pem</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-o</span> <span class="pre">ORG_NAME</span></tt>, <tt class="docutils literal"><span class="pre">--org-name</span> <span class="pre">ORG_NAME</span></tt></dt>
-<dd>The name of the Chef server organization. An organization name must begin with a lower-case letter or digit, may only contain lower-case letters, digits, hyphens, and underscores, and must be between 1 and 255 characters. For example: <tt class="docutils literal"><span class="pre">chef</span></tt>. If this option is not specified, the <tt class="docutils literal"><span class="pre">upgrade</span></tt> command will prompt for it.</dd>
-<dt><tt class="docutils literal"><span class="pre">-s</span> <span class="pre">URL</span></tt>, <tt class="docutils literal"><span class="pre">--chef11-server-url</span> <span class="pre">URL</span></tt></dt>
-<dd>The URL for the Open Source Chef or Enterprise Chef server, version 11. Default value: <tt class="docutils literal"><span class="pre">https://localhost</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-t</span> <span class="pre">NUMBER</span></tt>, <tt class="docutils literal"><span class="pre">--upload-threads</span> <span class="pre">NUMBER</span></tt></dt>
-<dd>The number of threads to use when migrating cookbooks. Default value: <tt class="docutils literal"><span class="pre">10</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-u</span> <span class="pre">USER</span></tt>, <tt class="docutils literal"><span class="pre">--user</span></tt></dt>
-<dd>Use to create a client as an admin client. This is required for any user to access Open Source Chef as an administrator.</dd>
-<dt><tt class="docutils literal"><span class="pre">-x</span> <span class="pre">URL</span></tt>, <tt class="docutils literal"><span class="pre">--chef12-server-url</span> <span class="pre">URL</span></tt></dt>
-<dd>The URL for the Chef server, version 12. Default value: <tt class="docutils literal"><span class="pre">https://localhost</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-y</span></tt>, <tt class="docutils literal"><span class="pre">--yes</span></tt></dt>
-<dd>Use to skip confirmation prompts during the upgrade process.</dd>
-</dl>
-</div>
-<div class="section" id="user-create">
-<h2>user-create<a class="headerlink" href="#user-create" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">user-create</span></tt> subcommand is used to create a user.</p>
-<p><strong>Syntax</strong></p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl user-create USER_NAME FIRST_NAME <span class="o">[</span>MIDDLE_NAME<span class="o">]</span> LAST_NAME EMAIL PASSWORD <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-<p><strong>Options</strong></p>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-f</span> <span class="pre">FILE_NAME</span></tt>, <tt class="docutils literal"><span class="pre">--filename</span> <span class="pre">FILE_NAME</span></tt></dt>
-<dd>Use to write the private key to a file instead of <tt class="docutils literal"><span class="pre">STDOUT</span></tt>.</dd>
-</dl>
-<p><strong>Examples</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl user-create john_smith John Smith john_smith@example.com insecure-passord
-</pre></div>
-</div>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl user-create jane_doe Jane Doe jane_doe@example.com PaSSword -f /tmp/jane_doe.key
-</pre></div>
-</div>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl user-create waldendude Henry David Thoreau waldendude@example.com excursions
-</pre></div>
-</div>
-</div>
-<div class="section" id="user-delete">
-<h2>user-delete<a class="headerlink" href="#user-delete" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">user-delete</span></tt> subcommand is used to delete a user.</p>
-<p><strong>Syntax</strong></p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl user-delete USER_NAME
-</pre></div>
-</div>
-<p><strong>Examples</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl user-delete john_smith
-</pre></div>
-</div>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl user-delete jane_doe
-</pre></div>
-</div>
-</div>
-<div class="section" id="user-edit">
-<h2>user-edit<a class="headerlink" href="#user-edit" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">user-edit</span></tt> subcommand is used to edit the details for a user. The data will be made available in the $EDITOR for editing.</p>
-<p><strong>Syntax</strong></p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl user-edit USER_NAME
-</pre></div>
-</div>
-<p><strong>Examples</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl user-edit john_smith
-</pre></div>
-</div>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl user-edit jane_doe
-</pre></div>
-</div>
-</div>
-<div class="section" id="user-list">
-<h2>user-list<a class="headerlink" href="#user-list" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">user-list</span></tt> subcommand is used to view a list of users.</p>
-<p><strong>Syntax</strong></p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl user-list <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-<p><strong>Options</strong></p>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-w</span></tt>, <tt class="docutils literal"><span class="pre">--with-uri</span></tt></dt>
-<dd>Use to show the corresponding URIs.</dd>
-</dl>
-</div>
-<div class="section" id="user-show">
-<h2>user-show<a class="headerlink" href="#user-show" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">user-show</span></tt> subcommand is used to show the details for a user.</p>
-<p><strong>Syntax</strong></p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl user-show USER_NAME <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-<p><strong>Options</strong></p>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-l</span></tt>, <tt class="docutils literal"><span class="pre">--with-orgs</span></tt></dt>
-<dd>Use to show the corresponding organizations.</dd>
-</dl>
-</div>
-<div class="section" id="service-subcommands">
-<h2>Service Subcommands<a class="headerlink" href="#service-subcommands" title="Permalink to this headline">¶</a></h2>
-<p>The Chef server has a built in process supervisor, which ensures that all of the required services are in the appropriate state at any given time. The supervisor starts two processes per service.</p>
-<div class="section" id="hup">
-<h3>hup<a class="headerlink" href="#hup" title="Permalink to this headline">¶</a></h3>
-<p>The <tt class="docutils literal"><span class="pre">hup</span></tt> subcommand is used to send a <tt class="docutils literal"><span class="pre">SIGHUP</span></tt> to all services. This command can also be run for an individual service by specifying the name of the service in the command.</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl hup name_of_service
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">name_of_service</span></tt> represents the name of any service that is listed after running the <tt class="docutils literal"><span class="pre">service-list</span></tt> subcommand.</p>
-</div>
-<div class="section" id="int">
-<h3>int<a class="headerlink" href="#int" title="Permalink to this headline">¶</a></h3>
-<p>The <tt class="docutils literal"><span class="pre">int</span></tt> subcommand is used to send a <tt class="docutils literal"><span class="pre">SIGINT</span></tt> to all services. This command can also be run for an individual service by specifying the name of the service in the command.</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl int name_of_service
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">name_of_service</span></tt> represents the name of any service that is listed after running the <tt class="docutils literal"><span class="pre">service-list</span></tt> subcommand.</p>
-</div>
-<div class="section" id="kill">
-<h3>kill<a class="headerlink" href="#kill" title="Permalink to this headline">¶</a></h3>
-<p>The <tt class="docutils literal"><span class="pre">kill</span></tt> subcommand is used to send a <tt class="docutils literal"><span class="pre">SIGKILL</span></tt> to all services. This command can also be run for an individual service by specifying the name of the service in the command.</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl <span class="nb">kill </span>name_of_service
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">name_of_service</span></tt> represents the name of any service that is listed after running the <tt class="docutils literal"><span class="pre">service-list</span></tt> subcommand.</p>
-</div>
-<div class="section" id="once">
-<h3>once<a class="headerlink" href="#once" title="Permalink to this headline">¶</a></h3>
-<p>The supervisor for the Chef server is configured to restart any service that fails, unless that service has been asked to change its state. The <tt class="docutils literal"><span class="pre">once</span></tt> subcommand is used to tell the supervisor to not attempt to restart any service that fails.</p>
-<p>This command is useful when troubleshooting configuration errors that prevent a service from starting. Run the <tt class="docutils literal"><span class="pre">once</span></tt> subcommand followed by the <tt class="docutils literal"><span class="pre">status</span></tt> subcommand to look for services in a down state and/or to identify which services are in trouble. This command can also be run for an individual service by specifying the name of the service in the command.</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl once name_of_service
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">name_of_service</span></tt> represents the name of any service that is listed after running the <tt class="docutils literal"><span class="pre">service-list</span></tt> subcommand.</p>
-</div>
-<div class="section" id="restart">
-<h3>restart<a class="headerlink" href="#restart" title="Permalink to this headline">¶</a></h3>
-<p>The <tt class="docutils literal"><span class="pre">restart</span></tt> subcommand is used to restart all services enabled on the Chef server or to restart an individual service by specifying the name of that service in the command.</p>
-<div class="admonition warning">
-<p class="first admonition-title">Warning</p>
-<p class="last">When running the Chef server in a high availability configuration, restarting all services may trigger failover.</p>
-</div>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl restart name_of_service
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">name_of_service</span></tt> represents the name of any service that is listed after running the <tt class="docutils literal"><span class="pre">service-list</span></tt> subcommand. When a service is successfully restarted the output should be similar to:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>ok: run: service_name: <span class="o">(</span>pid 12345<span class="o">)</span> 1s
-</pre></div>
-</div>
-</div>
-<div class="section" id="service-list">
-<h3>service-list<a class="headerlink" href="#service-list" title="Permalink to this headline">¶</a></h3>
-<p>The <tt class="docutils literal"><span class="pre">service-list</span></tt> subcommand is used to display a list of all available services. A service that is enabled is labeled with an asterisk (*).</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl service-list
-</pre></div>
-</div>
-</div>
-<div class="section" id="start">
-<h3>start<a class="headerlink" href="#start" title="Permalink to this headline">¶</a></h3>
-<p>The <tt class="docutils literal"><span class="pre">start</span></tt> subcommand is used to start all services that are enabled in the Chef server. This command can also be run for an individual service by specifying the name of the service in the command.</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl start name_of_service
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">name_of_service</span></tt> represents the name of any service that is listed after running the <tt class="docutils literal"><span class="pre">service-list</span></tt> subcommand. When a service is successfully started the output should be similar to:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>ok: run: service_name: <span class="o">(</span>pid 12345<span class="o">)</span> 1s
-</pre></div>
-</div>
-<p>The supervisor for the Chef server is configured to wait seven seconds for a service to respond to a command from the supervisor. If you see output that references a timeout, it means that a signal has been sent to the process, but that the process has yet to actually comply. In general, processes that have timed out are not a big concern, unless they are failing to respond to the signals at all. If a process is not responding, use a command like the <tt class="docutils literal"><span class="pre">kill</span></tt> subcommand to stop the process, investigate the cause (if required), and then use the <tt class="docutils literal"><span class="pre">start</span></tt> subcommand to re-enable it.</p>
-</div>
-<div class="section" id="status">
-<h3>status<a class="headerlink" href="#status" title="Permalink to this headline">¶</a></h3>
-<p>The <tt class="docutils literal"><span class="pre">status</span></tt> subcommand is used to show the status of all services available to the Chef server. The results will vary based on the configuration of a given server. This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl status
-</pre></div>
-</div>
-<p>and will return the status for all services. Status can be returned for individual services by specifying the name of the service as part of the command:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl status name_of_service
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">name_of_service</span></tt> represents the name of any service that is listed after running the <tt class="docutils literal"><span class="pre">service-list</span></tt> subcommand.</p>
-<p>When service status is requested, the output should be similar to:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>run: service_name: <span class="o">(</span>pid 12345<span class="o">)</span> 12345s; run: log: <span class="o">(</span>pid 1234<span class="o">)</span> 67890s
-</pre></div>
-</div>
-<p>where</p>
-<ul class="simple">
-<li><tt class="docutils literal"><span class="pre">run:</span></tt> is the state of the service (<tt class="docutils literal"><span class="pre">run:</span></tt> or <tt class="docutils literal"><span class="pre">down:</span></tt>)</li>
-<li><tt class="docutils literal"><span class="pre">service_name:</span></tt> is the name of the service for which status is returned</li>
-<li><tt class="docutils literal"><span class="pre">(pid</span> <span class="pre">12345)</span></tt> is the process identifier</li>
-<li><tt class="docutils literal"><span class="pre">12345s</span></tt> is the uptime of the service, in seconds</li>
-</ul>
-<p>For example:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>down: opscode-erchef: <span class="o">(</span>pid 35546<span class="o">)</span> 10s
-</pre></div>
-</div>
-<p>By default, runit will restart services automatically when the services fail. Therefore, runit may report the status of a service as <tt class="docutils literal"><span class="pre">run:</span></tt> even when there is an issue with that service. When investigating why a particular service is not running as it should be, look for the services with the shortest uptimes. For example, the list below indicates that the <strong>opscode-erchef</strong> should be investigated further:</p>
-<div class="highlight-bash"><div class="highlight"><pre>run: oc-id
-run: opscode-chef: <span class="o">(</span>pid 4327<span class="o">)</span> 13671s; run: log: <span class="o">(</span>pid 4326<span class="o">)</span> 13671s
-run: opscode-erchef: <span class="o">(</span>pid 5383<span class="o">)</span> 5s; run: log: <span class="o">(</span>pid 4382<span class="o">)</span> 13669s
-run: opscode-expander: <span class="o">(</span>pid 4078<span class="o">)</span> 13694s; run: log: <span class="o">(</span>pid 4077<span class="o">)</span> 13694s
-run: opscode-expander-reindexer: <span class="o">(</span>pid 4130<span class="o">)</span> 13692s; run: log: <span class="o">(</span>pid 4114<span class="o">)</span> 13692s
-</pre></div>
-</div>
-<div class="section" id="high-availability">
-<h4>High Availability<a class="headerlink" href="#high-availability" title="Permalink to this headline">¶</a></h4>
-<p>On back-end servers in a High Availability topology, Keepalived is used by the clustering service to determine whether a service should be running. If the <tt class="docutils literal"><span class="pre">status</span></tt> subcommand is run against any of these nodes, a few things change:</p>
-<ul class="simple">
-<li>On the back-end node that is currently the backup server, it is normal to see only one running process: Keepalived</li>
-<li>On the back-end node that is currently the master server, it is normal to see all services running. It is also normal to see some services in a down state if the server, on reboot, did not attempt to start the services because Keepalived determines which services are restarted based on the state of the cluster</li>
-</ul>
-<p>A sample status line for a service that is running on the master server in a High Availability topology:</p>
-<div class="highlight-bash"><div class="highlight"><pre>run: opscode-solr: <span class="o">(</span>pid 25341<span class="o">)</span> 239s, normally down; run: log: <span class="o">(</span>pid 5700<span class="o">)</span> 145308s
-</pre></div>
-</div>
-</div>
-<div class="section" id="log-files">
-<h4>Log Files<a class="headerlink" href="#log-files" title="Permalink to this headline">¶</a></h4>
-<p>A typical status line for a service that is running any of the Chef server front-end services is similar to the following:</p>
-<div class="highlight-bash"><div class="highlight"><pre>run: name_of_service: <span class="o">(</span>pid 1486<span class="o">)</span> 7819s; run: log: <span class="o">(</span>pid 1485<span class="o">)</span> 7819s
-</pre></div>
-</div>
-<p>where:</p>
-<ul class="simple">
-<li><tt class="docutils literal"><span class="pre">run</span></tt> describes the state in which the supervisor attempts to keep processes. This state is either <tt class="docutils literal"><span class="pre">run</span></tt> or <tt class="docutils literal"><span class="pre">down</span></tt>. If a service is in a <tt class="docutils literal"><span class="pre">down</span></tt> state, it should be stopped</li>
-<li><tt class="docutils literal"><span class="pre">name_of_service</span></tt> is the service name, for example: <tt class="docutils literal"><span class="pre">opscode-solr4</span></tt></li>
-<li><tt class="docutils literal"><span class="pre">(pid</span> <span class="pre">1486)</span> <span class="pre">7819s;</span></tt> is the process identifier followed by the amount of time (in seconds) the service has been running</li>
-<li><tt class="docutils literal"><span class="pre">run:</span> <span class="pre">log:</span> <span class="pre">(pid</span> <span class="pre">1485)</span> <span class="pre">7819s</span></tt> is the log process. It is typical for a log process to have a longer run time than a service; this is because the supervisor does not need to restart the log process in order to connect the supervised process</li>
-</ul>
-<p>If the service is down, the status line will appear similar to the following:</p>
-<div class="highlight-bash"><div class="highlight"><pre>down: opscode-solr4: 3s, normally up; run: log: <span class="o">(</span>pid 1485<span class="o">)</span> 8526s
-</pre></div>
-</div>
-<p>where</p>
-<ul class="simple">
-<li><tt class="docutils literal"><span class="pre">down</span></tt> indicates that the service is in a down state</li>
-<li><tt class="docutils literal"><span class="pre">3s,</span> <span class="pre">normally</span> <span class="pre">up;</span></tt> indicates that the service is normally in a run state and that the supervisor would attempt to restart this service after a reboot</li>
-</ul>
-</div>
-</div>
-<div class="section" id="stop">
-<h3>stop<a class="headerlink" href="#stop" title="Permalink to this headline">¶</a></h3>
-<p>The <tt class="docutils literal"><span class="pre">stop</span></tt> subcommand is used to stop all services enabled on the Chef server. This command can also be run for an individual service by specifying the name of the service in the command.</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl stop name_of_service
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">name_of_service</span></tt> represents the name of any service that is listed after running the <tt class="docutils literal"><span class="pre">service-list</span></tt> subcommand. When a service is successfully stopped the output should be similar to:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>ok: diwb: service_name: 0s, normally up
-</pre></div>
-</div>
-<p>For example:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl stop
-</pre></div>
-</div>
-<p>will return something similar to:</p>
-<div class="highlight-bash"><div class="highlight"><pre>ok: down: nginx: 393s, normally up
-ok: down: opscode-chef: 391s, normally up
-ok: down: opscode-erchef: 391s, normally up
-ok: down: opscode-expander: 390s, normally up
-ok: down: opscode-expander-reindexer: 389s, normally up
-ok: down: opscode-solr4: 389s, normally up
-ok: down: postgresql: 388s, normally up
-ok: down: rabbitmq: 388s, normally up
-ok: down: redis_lb: 387s, normally up
-</pre></div>
-</div>
-</div>
-<div class="section" id="tail">
-<h3>tail<a class="headerlink" href="#tail" title="Permalink to this headline">¶</a></h3>
-<p>The <tt class="docutils literal"><span class="pre">tail</span></tt> subcommand is used to follow all of the Chef server logs for all services. This command can also be run for an individual service by specifying the name of the service in the command.</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl tail name_of_service
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">name_of_service</span></tt> represents the name of any service that is listed after running the <tt class="docutils literal"><span class="pre">service-list</span></tt> subcommand.</p>
-</div>
-<div class="section" id="term">
-<h3>term<a class="headerlink" href="#term" title="Permalink to this headline">¶</a></h3>
-<p>The <tt class="docutils literal"><span class="pre">term</span></tt> subcommand is used to send a <tt class="docutils literal"><span class="pre">SIGTERM</span></tt> to all services. This command can also be run for an individual service by specifying the name of the service in the command.</p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-server-ctl term name_of_service
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">name_of_service</span></tt> represents the name of any service that is listed after running the <tt class="docutils literal"><span class="pre">service-list</span></tt> subcommand.</p>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/ctl_chef_shell.html b/distro/common/html/ctl_chef_shell.html
deleted file mode 100644
index e655ec9146..0000000000
--- a/distro/common/html/ctl_chef_shell.html
+++ /dev/null
@@ -1,163 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>chef-shell &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="chef-shell">
-<h1>chef-shell<a class="headerlink" href="#chef-shell" title="Permalink to this headline">¶</a></h1>
-<p>chef-shell is a recipe debugging tool that allows the use of breakpoints within recipes. chef-shell runs as an Interactive Ruby (IRb) session. chef-shell supports both recipe and attribute file syntax, as well as interactive debugging features.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">chef-shell is the new name for Shef as of Chef 11.x. chef-shell is backwards compatible and aside from the name change, has the same set of functionality as with previous releases.</p>
-</div>
-<p>The chef-shell executable is run as a command-line tool.</p>
-<div class="section" id="modes">
-<h2>Modes<a class="headerlink" href="#modes" title="Permalink to this headline">¶</a></h2>
-<p>chef-shell is tool that allows knife to be run using an Interactive Ruby (IRb) session. chef-shell currently supports recipe and attribute file syntax, as well as interactive debugging features. chef-shell has three run modes:</p>
-<table border="1" class="docutils">
-<colgroup>
-<col width="40%" />
-<col width="60%" />
-</colgroup>
-<thead valign="bottom">
-<tr class="row-odd"><th class="head">Mode</th>
-<th class="head">Description</th>
-</tr>
-</thead>
-<tbody valign="top">
-<tr class="row-even"><td>Standalone</td>
-<td>No cookbooks are loaded, and the run list is empty. This mode is the default.</td>
-</tr>
-<tr class="row-odd"><td>Solo</td>
-<td>chef-shell acts as a chef-solo client. It attempts to load the chef-solo configuration file and JSON attributes. If the JSON attributes set a run list, it will be honored. Cookbooks will be loaded in the same way that chef-solo loads them. chef-solo mode is activated with the <tt class="docutils literal"><span class="pre">-s</span></tt> or <tt class="docutils literal"><span class="pre">--solo</span></tt> command line option, and JSON attributes are specified in the same way as for chef-solo, with <tt class="docutils literal"><span class="pre">-j</span> <span class="pre">/path/to/chef-solo.json</span></tt>.</td>
-</tr>
-<tr class="row-even"><td>Client</td>
-<td>chef-shell acts as a chef-client. During startup, it reads the chef-client configuration file and contacts the Chef server to get attributes and cookbooks. The run list will be set in the same way as normal chef-client runs. chef-client mode is activated with the <tt class="docutils literal"><span class="pre">-z</span></tt> or <tt class="docutils literal"><span class="pre">--client</span></tt> options. You can also specify the configuration file with <tt class="docutils literal"><span class="pre">-c</span> <span class="pre">CONFIG</span></tt> and the server URL with <tt class="docutils literal"><span class="pre">-S</span> <span class="pre">SERVER_URL</span></tt>.</td>
-</tr>
-</tbody>
-</table>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<p>This command has the following syntax:</p>
-<div class="highlight-python"><div class="highlight"><pre>chef-shell OPTION VALUE OPTION VALUE ...
-</pre></div>
-</div>
-<p>This command has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span></tt>, <tt class="docutils literal"><span class="pre">--standalone</span></tt></dt>
-<dd>Use to run chef-shell in standalone mode.</dd>
-<dt><tt class="docutils literal"><span class="pre">-c</span> <span class="pre">CONFIG</span></tt>, <tt class="docutils literal"><span class="pre">--config</span> <span class="pre">CONFIG</span></tt></dt>
-<dd>The configuration file to use.</dd>
-<dt><tt class="docutils literal"><span class="pre">-h</span></tt>, <tt class="docutils literal"><span class="pre">--help</span></tt></dt>
-<dd>Shows help for the command.</dd>
-<dt><tt class="docutils literal"><span class="pre">-j</span> <span class="pre">PATH</span></tt>, <tt class="docutils literal"><span class="pre">--json-attributes</span> <span class="pre">PATH</span></tt></dt>
-<dd><p class="first">The path to a file that contains JSON data.</p>
-<p>Use this option to define a <tt class="docutils literal"><span class="pre">run_list</span></tt> object. For example, a JSON file similar to:</p>
-<div class="highlight-javascript"><div class="highlight"><pre><span class="s2">&quot;run_list&quot;</span><span class="o">:</span> <span class="p">[</span>
- <span class="s2">&quot;recipe[base]&quot;</span><span class="p">,</span>
- <span class="s2">&quot;recipe[foo]&quot;</span><span class="p">,</span>
- <span class="s2">&quot;recipe[bar]&quot;</span><span class="p">,</span>
- <span class="s2">&quot;role[webserver]&quot;</span>
-<span class="p">],</span>
-</pre></div>
-</div>
-<p>may be used by running <tt class="docutils literal"><span class="pre">chef-client</span> <span class="pre">-j</span> <span class="pre">path/to/file.json</span></tt>.</p>
-<p>In certain situations this option may be used to update <tt class="docutils literal"><span class="pre">normal</span></tt> attributes.</p>
-<div class="last admonition warning">
-<p class="first admonition-title">Warning</p>
-<p>Any other attribute type that is contained in this JSON file will be treated as a <tt class="docutils literal"><span class="pre">normal</span></tt> attribute. For example, attempting to update <tt class="docutils literal"><span class="pre">override</span></tt> attributes using the <tt class="docutils literal"><span class="pre">-j</span></tt> option:</p>
-<div class="highlight-javascript"><div class="highlight"><pre><span class="p">{</span>
- <span class="s2">&quot;name&quot;</span><span class="o">:</span> <span class="s2">&quot;dev-99&quot;</span><span class="p">,</span>
- <span class="s2">&quot;description&quot;</span><span class="o">:</span> <span class="s2">&quot;Install some stuff&quot;</span><span class="p">,</span>
- <span class="s2">&quot;override_attributes&quot;</span><span class="o">:</span> <span class="p">{</span>
- <span class="s2">&quot;apptastic&quot;</span><span class="o">:</span> <span class="p">{</span>
- <span class="s2">&quot;enable_apptastic&quot;</span><span class="o">:</span> <span class="s2">&quot;false&quot;</span><span class="p">,</span>
- <span class="s2">&quot;apptastic_tier_name&quot;</span><span class="o">:</span> <span class="s2">&quot;dev-99.bomb.com&quot;</span>
- <span class="p">}</span>
- <span class="p">}</span>
-<span class="p">}</span>
-</pre></div>
-</div>
-<p>will result in a node object similar to:</p>
-<div class="last highlight-javascript"><div class="highlight"><pre><span class="p">{</span>
- <span class="s2">&quot;name&quot;</span><span class="o">:</span> <span class="s2">&quot;maybe-dev-99&quot;</span><span class="p">,</span>
- <span class="s2">&quot;normal&quot;</span><span class="o">:</span> <span class="p">{</span>
- <span class="s2">&quot;name&quot;</span><span class="o">:</span> <span class="s2">&quot;dev-99&quot;</span><span class="p">,</span>
- <span class="s2">&quot;description&quot;</span><span class="o">:</span> <span class="s2">&quot;Install some stuff&quot;</span><span class="p">,</span>
- <span class="s2">&quot;override_attributes&quot;</span><span class="o">:</span> <span class="p">{</span>
- <span class="s2">&quot;apptastic&quot;</span><span class="o">:</span> <span class="p">{</span>
- <span class="s2">&quot;enable_apptastic&quot;</span><span class="o">:</span> <span class="s2">&quot;false&quot;</span><span class="p">,</span>
- <span class="s2">&quot;apptastic_tier_name&quot;</span><span class="o">:</span> <span class="s2">&quot;dev-99.bomb.com&quot;</span>
- <span class="p">}</span>
- <span class="p">}</span>
- <span class="p">}</span>
-<span class="p">}</span>
-</pre></div>
-</div>
-</div>
-</dd>
-<dt><tt class="docutils literal"><span class="pre">-l</span> <span class="pre">LEVEL</span></tt>, <tt class="docutils literal"><span class="pre">--log-level</span> <span class="pre">LEVEL</span></tt></dt>
-<dd>The level of logging that will be stored in a log file.</dd>
-<dt><tt class="docutils literal"><span class="pre">-s</span></tt>, <tt class="docutils literal"><span class="pre">--solo</span></tt></dt>
-<dd>Use to run chef-shell in chef-solo mode.</dd>
-<dt><tt class="docutils literal"><span class="pre">-S</span> <span class="pre">CHEF_SERVER_URL</span></tt>, <tt class="docutils literal"><span class="pre">--server</span> <span class="pre">CHEF_SERVER_URL</span></tt></dt>
-<dd>The URL for the Chef server.</dd>
-<dt><tt class="docutils literal"><span class="pre">-v</span></tt>, <tt class="docutils literal"><span class="pre">--version</span></tt></dt>
-<dd>The version of the chef-client.</dd>
-<dt><tt class="docutils literal"><span class="pre">-z</span></tt>, <tt class="docutils literal"><span class="pre">--client</span></tt></dt>
-<dd>Use to run chef-shell in chef-client mode.</dd>
-</dl>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/ctl_chef_solo.html b/distro/common/html/ctl_chef_solo.html
deleted file mode 100644
index 2382fed80a..0000000000
--- a/distro/common/html/ctl_chef_solo.html
+++ /dev/null
@@ -1,194 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>chef-solo &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="chef-solo">
-<h1>chef-solo<a class="headerlink" href="#chef-solo" title="Permalink to this headline">¶</a></h1>
-<p>chef-solo is an open source version of the chef-client that allows using cookbooks with nodes without requiring access to a Chef server. chef-solo runs locally and requires that a cookbook (and any of its dependencies) be on the same physical disk as the node. chef-solo is a limited-functionality version of the chef-client and <strong>does not support</strong> the following:</p>
-<ul class="simple">
-<li>Node data storage</li>
-<li>Search indexes</li>
-<li>Centralized distribution of cookbooks</li>
-<li>A centralized API that interacts with and integrates infrastructure components</li>
-<li>Authentication or authorization</li>
-<li>Persistent attributes</li>
-</ul>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">chef-solo can be run as a daemon.</p>
-</div>
-<p>The chef-solo executable is run as a command-line tool.</p>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<p>This command has the following syntax:</p>
-<div class="highlight-python"><div class="highlight"><pre>chef-solo OPTION VALUE OPTION VALUE ...
-</pre></div>
-</div>
-<p>This command has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-c</span> <span class="pre">CONFIG</span></tt>, <tt class="docutils literal"><span class="pre">--config</span> <span class="pre">CONFIG</span></tt></dt>
-<dd>The configuration file to use.</dd>
-<dt><tt class="docutils literal"><span class="pre">-d</span></tt>, <tt class="docutils literal"><span class="pre">--daemonize</span></tt></dt>
-<dd><p class="first">Use to run the executable as a daemon. This option may not be used in the same command with the <tt class="docutils literal"><span class="pre">--[no-]fork</span></tt> option.</p>
-<p class="last">This option is only available on machines that run in UNIX or Linux environments. For machines that are running Microsoft Windows that require similar functionality, use the <tt class="docutils literal"><span class="pre">chef-client::service</span></tt> recipe in the <tt class="docutils literal"><span class="pre">chef-client</span></tt> cookbook: <a class="reference external" href="http://community.opscode.com/cookbooks/chef-client">http://community.opscode.com/cookbooks/chef-client</a>. This will install a chef-client service under Microsoft Windows using the Windows Service Wrapper.</p>
-</dd>
-<dt><tt class="docutils literal"><span class="pre">-E</span> <span class="pre">ENVIRONMENT_NAME</span></tt>, <tt class="docutils literal"><span class="pre">--environment</span> <span class="pre">ENVIRONMENT_NAME</span></tt></dt>
-<dd>The name of the environment.</dd>
-<dt><tt class="docutils literal"><span class="pre">-f</span></tt>, <tt class="docutils literal"><span class="pre">--[no-]fork</span></tt></dt>
-<dd>Use to contain the chef-client run in a secondary process with dedicated RAM. When the chef-client run is complete the RAM will be returned to the master process. This option helps ensure that a chef-client will use a steady amount of RAM over time because the master process will not run recipes. This option will also help prevent memory leaks (such as those that can be introduced by the code contained within a poorly designed cookbook). Use <tt class="docutils literal"><span class="pre">--no-fork</span></tt> to disable running the chef-client in fork node. Default value: <tt class="docutils literal"><span class="pre">--fork</span></tt>. This option may not be used in the same command with the <tt class="docutils literal"><span class="pre">--daemonize</span></tt> and <tt class="docutils literal"><span class="pre">--interval</span></tt> options.</dd>
-<dt><tt class="docutils literal"><span class="pre">-F</span> <span class="pre">FORMAT</span></tt>, <tt class="docutils literal"><span class="pre">--format</span> <span class="pre">FORMAT</span></tt></dt>
-<dd><p class="first">The output format: <tt class="docutils literal"><span class="pre">doc</span></tt> (default) or <tt class="docutils literal"><span class="pre">min</span></tt>.</p>
-<p>Use <tt class="docutils literal"><span class="pre">doc</span></tt> to print the progress of the chef-client run using full strings that display a summary of updates as they occur.</p>
-<p>Use <tt class="docutils literal"><span class="pre">min</span></tt> to print the progress of the chef-client run using single characters. A summary of updates is printed at the end of the chef-client run. A dot (<tt class="docutils literal"><span class="pre">.</span></tt>) is printed for events that do not have meaningful status information, such as loading a file or synchronizing a cookbook. For resources, a dot (<tt class="docutils literal"><span class="pre">.</span></tt>) is printed when the resource is up to date, an <tt class="docutils literal"><span class="pre">S</span></tt> is printed when the resource is skipped by <tt class="docutils literal"><span class="pre">not_if</span></tt> or <tt class="docutils literal"><span class="pre">only_if</span></tt>, and a <tt class="docutils literal"><span class="pre">U</span></tt> is printed when the resource is updated.</p>
-<p class="last">Other formatting options are available when those formatters are configured in the client.rb file using the <tt class="docutils literal"><span class="pre">add_formatter</span></tt> option.</p>
-</dd>
-<dt><tt class="docutils literal"><span class="pre">--force-formatter</span></tt></dt>
-<dd>Use to show formatter output instead of logger output.</dd>
-<dt><tt class="docutils literal"><span class="pre">--force-logger</span></tt></dt>
-<dd>Use to show logger output instead of formatter output.</dd>
-<dt><tt class="docutils literal"><span class="pre">-g</span> <span class="pre">GROUP</span></tt>, <tt class="docutils literal"><span class="pre">--group</span> <span class="pre">GROUP</span></tt></dt>
-<dd>The name of the group that owns a process. This is required when starting any executable as a daemon.</dd>
-<dt><tt class="docutils literal"><span class="pre">-h</span></tt>, <tt class="docutils literal"><span class="pre">--help</span></tt></dt>
-<dd>Shows help for the command.</dd>
-<dt><tt class="docutils literal"><span class="pre">-i</span> <span class="pre">SECONDS</span></tt>, <tt class="docutils literal"><span class="pre">--interval</span> <span class="pre">SECONDS</span></tt></dt>
-<dd>The frequency (in seconds) at which the chef-client runs. When the chef-client is run at intervals, <tt class="docutils literal"><span class="pre">--splay</span></tt> and <tt class="docutils literal"><span class="pre">--interval</span></tt> values are applied before the chef-client run. This option may not be used in the same command with the <tt class="docutils literal"><span class="pre">--[no-]fork</span></tt> option.</dd>
-<dt><tt class="docutils literal"><span class="pre">-j</span> <span class="pre">PATH</span></tt>, <tt class="docutils literal"><span class="pre">--json-attributes</span> <span class="pre">PATH</span></tt></dt>
-<dd><p class="first">The path to a file that contains JSON data.</p>
-<p>Use this option to define a <tt class="docutils literal"><span class="pre">run_list</span></tt> object. For example, a JSON file similar to:</p>
-<div class="highlight-javascript"><div class="highlight"><pre><span class="s2">&quot;run_list&quot;</span><span class="o">:</span> <span class="p">[</span>
- <span class="s2">&quot;recipe[base]&quot;</span><span class="p">,</span>
- <span class="s2">&quot;recipe[foo]&quot;</span><span class="p">,</span>
- <span class="s2">&quot;recipe[bar]&quot;</span><span class="p">,</span>
- <span class="s2">&quot;role[webserver]&quot;</span>
-<span class="p">],</span>
-</pre></div>
-</div>
-<p>may be used by running <tt class="docutils literal"><span class="pre">chef-client</span> <span class="pre">-j</span> <span class="pre">path/to/file.json</span></tt>.</p>
-<p>In certain situations this option may be used to update <tt class="docutils literal"><span class="pre">normal</span></tt> attributes.</p>
-<div class="last admonition warning">
-<p class="first admonition-title">Warning</p>
-<p>Any other attribute type that is contained in this JSON file will be treated as a <tt class="docutils literal"><span class="pre">normal</span></tt> attribute. For example, attempting to update <tt class="docutils literal"><span class="pre">override</span></tt> attributes using the <tt class="docutils literal"><span class="pre">-j</span></tt> option:</p>
-<div class="highlight-javascript"><div class="highlight"><pre><span class="p">{</span>
- <span class="s2">&quot;name&quot;</span><span class="o">:</span> <span class="s2">&quot;dev-99&quot;</span><span class="p">,</span>
- <span class="s2">&quot;description&quot;</span><span class="o">:</span> <span class="s2">&quot;Install some stuff&quot;</span><span class="p">,</span>
- <span class="s2">&quot;override_attributes&quot;</span><span class="o">:</span> <span class="p">{</span>
- <span class="s2">&quot;apptastic&quot;</span><span class="o">:</span> <span class="p">{</span>
- <span class="s2">&quot;enable_apptastic&quot;</span><span class="o">:</span> <span class="s2">&quot;false&quot;</span><span class="p">,</span>
- <span class="s2">&quot;apptastic_tier_name&quot;</span><span class="o">:</span> <span class="s2">&quot;dev-99.bomb.com&quot;</span>
- <span class="p">}</span>
- <span class="p">}</span>
-<span class="p">}</span>
-</pre></div>
-</div>
-<p>will result in a node object similar to:</p>
-<div class="last highlight-javascript"><div class="highlight"><pre><span class="p">{</span>
- <span class="s2">&quot;name&quot;</span><span class="o">:</span> <span class="s2">&quot;maybe-dev-99&quot;</span><span class="p">,</span>
- <span class="s2">&quot;normal&quot;</span><span class="o">:</span> <span class="p">{</span>
- <span class="s2">&quot;name&quot;</span><span class="o">:</span> <span class="s2">&quot;dev-99&quot;</span><span class="p">,</span>
- <span class="s2">&quot;description&quot;</span><span class="o">:</span> <span class="s2">&quot;Install some stuff&quot;</span><span class="p">,</span>
- <span class="s2">&quot;override_attributes&quot;</span><span class="o">:</span> <span class="p">{</span>
- <span class="s2">&quot;apptastic&quot;</span><span class="o">:</span> <span class="p">{</span>
- <span class="s2">&quot;enable_apptastic&quot;</span><span class="o">:</span> <span class="s2">&quot;false&quot;</span><span class="p">,</span>
- <span class="s2">&quot;apptastic_tier_name&quot;</span><span class="o">:</span> <span class="s2">&quot;dev-99.bomb.com&quot;</span>
- <span class="p">}</span>
- <span class="p">}</span>
- <span class="p">}</span>
-<span class="p">}</span>
-</pre></div>
-</div>
-</div>
-</dd>
-<dt><tt class="docutils literal"><span class="pre">-l</span> <span class="pre">LEVEL</span></tt>, <tt class="docutils literal"><span class="pre">--log_level</span> <span class="pre">LEVEL</span></tt></dt>
-<dd>The level of logging that will be stored in a log file.</dd>
-<dt><tt class="docutils literal"><span class="pre">-L</span> <span class="pre">LOGLOCATION</span></tt>, <tt class="docutils literal"><span class="pre">--logfile</span> <span class="pre">c</span></tt></dt>
-<dd>The location in which log file output files will be saved. If this location is set to something other than <tt class="docutils literal"><span class="pre">STDOUT</span></tt>, standard output logging will still be performed (otherwise there would be no output other than to a file). This is recommended when starting any executable as a daemon.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]color</span></tt></dt>
-<dd>Use to view colored output. Default setting: <tt class="docutils literal"><span class="pre">--color</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-N</span> <span class="pre">NODE_NAME</span></tt>, <tt class="docutils literal"><span class="pre">--node-name</span> <span class="pre">NODE_NAME</span></tt></dt>
-<dd>The name of the node.</dd>
-<dt><tt class="docutils literal"><span class="pre">-o</span> <span class="pre">RUN_LIST_ITEM</span></tt>, <tt class="docutils literal"><span class="pre">--override-runlist</span> <span class="pre">RUN_LIST_ITEM</span></tt></dt>
-<dd>Replace the current run list with the specified items.</dd>
-<dt><tt class="docutils literal"><span class="pre">-r</span> <span class="pre">RECIPE_URL</span></tt>, <tt class="docutils literal"><span class="pre">--recipe-url</span> <span class="pre">RECIPE_URL</span></tt></dt>
-<dd>The URL location from which a remote cookbook tar.gz will be downloaded.</dd>
-<dt><tt class="docutils literal"><span class="pre">--run-lock-timeout</span> <span class="pre">SECONDS</span></tt></dt>
-<dd>The amount of time (in seconds) to wait for a chef-client run to finish. Default value: not set (indefinite). Set to <tt class="docutils literal"><span class="pre">0</span></tt> to cause a second chef-client to exit immediately.</dd>
-<dt><tt class="docutils literal"><span class="pre">-s</span> <span class="pre">SECONDS</span></tt>, <tt class="docutils literal"><span class="pre">--splay</span> <span class="pre">SECONDS</span></tt></dt>
-<dd>A number (in seconds) to add to the <tt class="docutils literal"><span class="pre">interval</span></tt> that is used to determine the frequency of chef-client runs. This number can help prevent server load when there are many clients running at the same time. When the chef-client is run at intervals, <tt class="docutils literal"><span class="pre">--splay</span></tt> and <tt class="docutils literal"><span class="pre">--interval</span></tt> values are applied before the chef-client run.</dd>
-<dt><tt class="docutils literal"><span class="pre">-u</span> <span class="pre">USER</span></tt>, <tt class="docutils literal"><span class="pre">--user</span> <span class="pre">USER</span></tt></dt>
-<dd>The user that owns a process. This is required when starting any executable as a daemon.</dd>
-<dt><tt class="docutils literal"><span class="pre">-v</span></tt>, <tt class="docutils literal"><span class="pre">--version</span></tt></dt>
-<dd>The version of the chef-client.</dd>
-<dt><tt class="docutils literal"><span class="pre">-W</span></tt>, <tt class="docutils literal"><span class="pre">--why-run</span></tt></dt>
-<dd>Use to run the executable in why-run mode, which is a type of chef-client run that does everything except modify the system. Use why-run mode to understand why the chef-client makes the decisions that it makes and to learn more about the current and proposed state of the system.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p><strong>Use a URL</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-solo -c ~/solo.rb -j ~/node.json -r http://www.example.com/chef-solo.tar.gz
-</pre></div>
-</div>
-<p>The tar.gz archived into the <tt class="docutils literal"><span class="pre">file_cache_path</span></tt>, and then extracted to <tt class="docutils literal"><span class="pre">cookbooks_path</span></tt>.</p>
-<p><strong>Use a directory</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-solo -c ~/solo.rb -j ~/node.json
-</pre></div>
-</div>
-<p>chef-solo will look in the solo.rb file to determine the directory in which cookbooks are located.</p>
-<p><strong>Use a URL for cookbook and JSON data</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>chef-solo -c ~/solo.rb -j http://www.example.com/node.json -r http://www.example.com/chef-solo.tar.gz
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/index.html b/distro/common/html/index.html
deleted file mode 100644
index 2742b07e03..0000000000
--- a/distro/common/html/index.html
+++ /dev/null
@@ -1,202 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>chef-client Man Pages &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="chef-client-man-pages">
-<h1>chef-client Man Pages<a class="headerlink" href="#chef-client-man-pages" title="Permalink to this headline">¶</a></h1>
-<p>The following command line interfaces are available in the chef-client:</p>
-<ul class="simple">
-<li><a class="reference internal" href="ctl_chef_client.html"><em>chef-client</em></a></li>
-<li><a class="reference internal" href="ctl_chef_server.html"><em>chef-server-ctl (executable)</em></a></li>
-<li><a class="reference internal" href="ctl_chef_shell.html"><em>chef-shell</em></a></li>
-<li><a class="reference internal" href="ctl_chef_solo.html"><em>chef-solo</em></a></li>
-<li><a class="reference internal" href="knife.html"><em>knife</em></a></li>
-</ul>
-<div class="section" id="knife">
-<h2>knife<a class="headerlink" href="#knife" title="Permalink to this headline">¶</a></h2>
-<p>knife is a command-line tool that provides an interface between a local chef-repo and the Chef server. knife helps users to manage:</p>
-<ul class="simple">
-<li>Nodes</li>
-<li>Cookbooks and recipes</li>
-<li>Roles</li>
-<li>Stores of JSON data (data bags), including encrypted data</li>
-<li>Environments</li>
-<li>Cloud resources, including provisioning</li>
-<li>The installation of the chef-client on management workstations</li>
-<li>Searching of indexed data on the Chef server</li>
-</ul>
-<p>The following sections describe functionality common to all knife subcommands:</p>
-<ul class="simple">
-<li><a class="reference internal" href="knife_using.html"><em>Working with Knife</em></a></li>
-<li><a class="reference internal" href="knife_common_options.html"><em>Common Options</em></a></li>
-</ul>
-<p>knife includes the following sub-commands:</p>
-<table border="1" class="docutils">
-<colgroup>
-<col width="25%" />
-<col width="75%" />
-</colgroup>
-<thead valign="bottom">
-<tr class="row-odd"><th class="head">Sub-command</th>
-<th class="head">Description</th>
-</tr>
-</thead>
-<tbody valign="top">
-<tr class="row-even"><td><a class="reference internal" href="knife_bootstrap.html"><em>knife bootstrap</em></a></td>
-<td>The <strong>knife bootstrap</strong> subcommand is used to run a bootstrap operation that installs the chef-client on the target system. The bootstrap operation must specify the IP address or FQDN of the target system.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_client.html"><em>knife client</em></a></td>
-<td>The <strong>knife client</strong> subcommand is used to manage an API client list and their associated RSA public key-pairs. This allows authentication requests to be made to the Chef server by any entity that uses the Chef server API, such as the chef-client and knife.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_configure.html"><em>knife configure</em></a></td>
-<td>The <strong>knife configure</strong> subcommand is used to create the knife.rb and client.rb files so that they can be distributed to workstations and nodes.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_cookbook.html"><em>knife cookbook</em></a></td>
-<td>The <strong>knife cookbook</strong> subcommand is used to interact with cookbooks that are located on the Chef server or the local chef-repo.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_cookbook_site.html"><em>knife cookbook site</em></a></td>
-<td>The <strong>knife cookbook site</strong> subcommand is used to interact with cookbooks that are located at <a class="reference external" href="https://supermarket.getchef.com/cookbooks">https://supermarket.getchef.com/cookbooks</a>. A user account is required for any community actions that write data to this site. The following arguments do not require a user account: <tt class="docutils literal"><span class="pre">download</span></tt>, <tt class="docutils literal"><span class="pre">search</span></tt>, <tt class="docutils literal"><span class="pre">install</span></tt>, and <tt class="docutils literal"><span class="pre">list</span></tt>.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_data_bag.html"><em>knife data bag</em></a></td>
-<td>The <strong>knife data bag</strong> subcommand is used to manage arbitrary stores of globally available JSON data.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_delete.html"><em>knife delete</em></a></td>
-<td>The <strong>knife delete</strong> subcommand is used to delete an object from a Chef server. This subcommand works similar to <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">cookbook</span> <span class="pre">delete</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">data</span> <span class="pre">bag</span> <span class="pre">delete</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">environment</span> <span class="pre">delete</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">node</span> <span class="pre">delete</span></tt>, and <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">role</span> <span class="pre">delete</span></tt>, but with a single verb (and a single action).</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_deps.html"><em>knife deps</em></a></td>
-<td>The <strong>knife deps</strong> subcommand is used to identify dependencies for a node, role, or cookbook.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_diff.html"><em>knife diff</em></a></td>
-<td>The <strong>knife diff</strong> subcommand is used to compare the differences between files and directories on the Chef server and in the chef-repo. For example, to compare files on the Chef server prior to an uploading or downloading files using the <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">download</span></tt> and <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">upload</span></tt> subcommands, or to ensure that certain files in multiple production environments are the same. This subcommand is similar to the <tt class="docutils literal"><span class="pre">git</span> <span class="pre">diff</span></tt> command that can be used to diff what is in the chef-repo with what is synced to a git repository.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_download.html"><em>knife download</em></a></td>
-<td>The <strong>knife download</strong> subcommand is used to download roles, cookbooks, environments, nodes, and data bags from the Chef server to the current working directory. It can be used to back up data on the Chef server, inspect the state of one or more files, or to extract out-of-process changes users may have made to files on the Chef server, such as if a user made a change that bypassed version source control. This subcommand is often used in conjunction with <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">diff</span></tt>, which can be used to see exactly what changes will be downloaded, and then <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">upload</span></tt>, which does the opposite of <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">download</span></tt>.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_edit.html"><em>knife edit</em></a></td>
-<td>The <strong>knife edit</strong> subcommand is used to edit objects on the Chef server. This subcommand works similar to <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">cookbook</span> <span class="pre">edit</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">data</span> <span class="pre">bag</span> <span class="pre">edit</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">environment</span> <span class="pre">edit</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">node</span> <span class="pre">edit</span></tt>, and <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">role</span> <span class="pre">edit</span></tt>, but with a single verb (and a single action).</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_environment.html"><em>knife environment</em></a></td>
-<td>The <strong>knife environment</strong> subcommand is used to manage environments within a single organization on the Chef server.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_exec.html"><em>knife exec</em></a></td>
-<td>The <strong>knife exec</strong> subcommand uses the knife configuration file to execute Ruby scripts in the context of a fully configured chef-client. This subcommand is most often used to run scripts that will only access Chef server one time (or otherwise very infrequently). Use this subcommand any time that an operation does not warrant full usage of the knife subcommand library.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_index_rebuild.html"><em>knife index rebuild</em></a></td>
-<td>The <strong>knife index rebuild</strong> subcommand is used to rebuild the search indexes for the open source Chef server. This operation is destructive and may take some time.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_list.html"><em>knife list</em></a></td>
-<td>The <strong>knife list</strong> subcommand is used to view a list of objects on the Chef server. This subcommand works similar to <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">cookbook</span> <span class="pre">list</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">data</span> <span class="pre">bag</span> <span class="pre">list</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">environment</span> <span class="pre">list</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">node</span> <span class="pre">list</span></tt>, and <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">role</span> <span class="pre">list</span></tt>, but with a single verb (and a single action).</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_node.html"><em>knife node</em></a></td>
-<td>The <strong>knife node</strong> subcommand is used to manage the nodes that exist on a Chef server.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_raw.html"><em>knife raw</em></a></td>
-<td>The <strong>knife raw</strong> subcommand is used to send a REST request to an endpoint in the Chef server API.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_recipe_list.html"><em>knife recipe list</em></a></td>
-<td>The <strong>knife recipe list</strong> subcommand is used to view all of the recipes that are on a Chef server. A regular expression can be used to limit the results to recipes that match a specific pattern. The regular expression must be within quotes and not be surrounded by forward slashes (/).</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_role.html"><em>knife role</em></a></td>
-<td>The <strong>knife role</strong> subcommand is used to manage the roles that are associated with one or more nodes on a Chef server.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_search.html"><em>knife search</em></a></td>
-<td>The <strong>knife search</strong> subcommand is used run a search query for information that is indexed on a Chef server.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_serve.html"><em>knife serve</em></a></td>
-<td>The <strong>knife serve</strong> subcommand is used to run a persistent chef-zero against the local chef-repo. (chef-zero is a lightweight Chef server that runs in-memory on the local machine.) This is the same as running the chef-client executable with the <tt class="docutils literal"><span class="pre">--local-mode</span></tt> option. The <tt class="docutils literal"><span class="pre">chef_repo_path</span></tt> is located automatically and the Chef server will bind to the first available port between <tt class="docutils literal"><span class="pre">8889</span></tt> and <tt class="docutils literal"><span class="pre">9999</span></tt>. <strong>knife serve</strong> will print the URL for the local Chef server, so that it may be added to the knife.rb file.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_show.html"><em>knife show</em></a></td>
-<td>The <strong>knife show</strong> subcommand is used to view the details of one (or more) objects on the Chef server. This subcommand works similar to <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">cookbook</span> <span class="pre">show</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">data</span> <span class="pre">bag</span> <span class="pre">show</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">environment</span> <span class="pre">show</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">node</span> <span class="pre">show</span></tt>, and <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">role</span> <span class="pre">show</span></tt>, but with a single verb (and a single action).</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_ssh.html"><em>knife ssh</em></a></td>
-<td>The <strong>knife ssh</strong> subcommand is used to invoke SSH commands (in parallel) on a subset of nodes within an organization, based on the results of a <a class="reference external" href="http://docs.opscode.com/essentials_search.html">search query</a> made to the Chef server.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_ssl_check.html"><em>knife ssl check</em></a></td>
-<td><p class="first">The <strong>knife ssl check</strong> subcommand is used to verify the SSL configuration for the Enterprise Chef and/or Open Source Chef servers, or at another location specified by a URL or URI.</p>
-<div class="last admonition warning">
-<p class="first admonition-title">Warning</p>
-<p class="last">When verification of a remote server&#8217;s SSL certificate is disabled, the chef-client will issue a warning similar to &#8220;SSL validation of HTTPS requests is disabled. HTTPS connections are still encrypted, but the chef-client is not able to detect forged replies or man-in-the-middle attacks.&#8221; To configure SSL for the chef-client, set <tt class="docutils literal"><span class="pre">ssl_verify_mode</span></tt> to <tt class="docutils literal"><span class="pre">:verify_peer</span></tt> (recommended) <strong>or</strong> <tt class="docutils literal"><span class="pre">verify_api_cert</span></tt> to <tt class="docutils literal"><span class="pre">true</span></tt> in the client.rb file.</p>
-</div>
-</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_ssl_fetch.html"><em>knife ssl fetch</em></a></td>
-<td><p class="first">The <strong>knife ssl fetch</strong> subcommand is used to copy SSL certificates from an HTTPS server to the <tt class="docutils literal"><span class="pre">trusted_certs_dir</span></tt> directory that is used by knife and the chef-client to store trusted SSL certificates. When these certificates match the hostname of the remote server, running <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">ssl</span> <span class="pre">fetch</span></tt> is the only step required to verify a remote server that is accessed by either knife or the chef-client.</p>
-<div class="last admonition warning">
-<p class="first admonition-title">Warning</p>
-<p class="last">It is the user&#8217;s responsibility to verify the authenticity of every SSL certificate before downloading it to the <tt class="docutils literal"><span class="pre">trusted_certs_dir</span></tt> directory. knife will use any certificate in that directory as if it is a 100% trusted and authentic SSL certificate. knife will not be able to determine if any certificate in this directory has been tampered with, is forged, malicious, or otherwise harmful. Therefore it is essential that users take the proper steps before downloading certificates into this directory.</p>
-</div>
-</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_status.html"><em>knife status</em></a></td>
-<td>The <strong>knife status</strong> subcommand is used to display a brief summary of the nodes on a Chef server, including the time of the most recent successful chef-client run.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_tag.html"><em>knife tag</em></a></td>
-<td>The <strong>knife tag</strong> subcommand is used to apply tags to nodes on a Chef server.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_upload.html"><em>knife upload</em></a></td>
-<td>The <strong>knife upload</strong> subcommand is used to upload roles, cookbooks, environments, and data bags to the Chef server from the current working directory in the chef-repo. This subcommand is often used in conjunction with <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">diff</span></tt>, which can be used to see exactly what changes will be uploaded, and then <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">download</span></tt>, which does the opposite of <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">upload</span></tt>.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_user.html"><em>knife user</em></a></td>
-<td>The <strong>knife user</strong> subcommand is used to manage the list of users and their associated RSA public key-pairs.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_xargs.html"><em>knife xargs</em></a></td>
-<td>The <strong>knife xargs</strong> subcommand is used to take patterns from standard input, download as JSON, run a command against the downloaded JSON, and then upload any changes.</td>
-</tr>
-</tbody>
-</table>
-<div class="toctree-wrapper compound">
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife.html b/distro/common/html/knife.html
deleted file mode 100644
index 5a081702fc..0000000000
--- a/distro/common/html/knife.html
+++ /dev/null
@@ -1,170 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife">
-<h1>knife<a class="headerlink" href="#knife" title="Permalink to this headline">¶</a></h1>
-<p>knife is a command-line tool that provides an interface between a local chef-repo and the Chef server. knife helps users to manage:</p>
-<ul class="simple">
-<li>Nodes</li>
-<li>Cookbooks and recipes</li>
-<li>Roles</li>
-<li>Stores of JSON data (data bags), including encrypted data</li>
-<li>Environments</li>
-<li>Cloud resources, including provisioning</li>
-<li>The installation of the chef-client on management workstations</li>
-<li>Searching of indexed data on the Chef server</li>
-</ul>
-<p>The following sections describe functionality common to all knife subcommands:</p>
-<ul class="simple">
-<li><a class="reference internal" href="knife_using.html"><em>Working with Knife</em></a></li>
-<li><a class="reference internal" href="knife_common_options.html"><em>Common Options</em></a></li>
-</ul>
-<p>knife includes the following sub-commands:</p>
-<table border="1" class="docutils">
-<colgroup>
-<col width="25%" />
-<col width="75%" />
-</colgroup>
-<thead valign="bottom">
-<tr class="row-odd"><th class="head">Sub-command</th>
-<th class="head">Description</th>
-</tr>
-</thead>
-<tbody valign="top">
-<tr class="row-even"><td><a class="reference internal" href="knife_bootstrap.html"><em>knife bootstrap</em></a></td>
-<td>The <strong>knife bootstrap</strong> subcommand is used to run a bootstrap operation that installs the chef-client on the target system. The bootstrap operation must specify the IP address or FQDN of the target system.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_client.html"><em>knife client</em></a></td>
-<td>The <strong>knife client</strong> subcommand is used to manage an API client list and their associated RSA public key-pairs. This allows authentication requests to be made to the Chef server by any entity that uses the Chef server API, such as the chef-client and knife.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_configure.html"><em>knife configure</em></a></td>
-<td>The <strong>knife configure</strong> subcommand is used to create the knife.rb and client.rb files so that they can be distributed to workstations and nodes.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_cookbook.html"><em>knife cookbook</em></a></td>
-<td>The <strong>knife cookbook</strong> subcommand is used to interact with cookbooks that are located on the Chef server or the local chef-repo.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_cookbook_site.html"><em>knife cookbook site</em></a></td>
-<td>The <strong>knife cookbook site</strong> subcommand is used to interact with cookbooks that are located at <a class="reference external" href="https://supermarket.getchef.com/cookbooks">https://supermarket.getchef.com/cookbooks</a>. A user account is required for any community actions that write data to this site. The following arguments do not require a user account: <tt class="docutils literal"><span class="pre">download</span></tt>, <tt class="docutils literal"><span class="pre">search</span></tt>, <tt class="docutils literal"><span class="pre">install</span></tt>, and <tt class="docutils literal"><span class="pre">list</span></tt>.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_data_bag.html"><em>knife data bag</em></a></td>
-<td>The <strong>knife data bag</strong> subcommand is used to manage arbitrary stores of globally available JSON data.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_delete.html"><em>knife delete</em></a></td>
-<td>The <strong>knife delete</strong> subcommand is used to delete an object from a Chef server. This subcommand works similar to <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">cookbook</span> <span class="pre">delete</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">data</span> <span class="pre">bag</span> <span class="pre">delete</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">environment</span> <span class="pre">delete</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">node</span> <span class="pre">delete</span></tt>, and <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">role</span> <span class="pre">delete</span></tt>, but with a single verb (and a single action).</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_deps.html"><em>knife deps</em></a></td>
-<td>The <strong>knife deps</strong> subcommand is used to identify dependencies for a node, role, or cookbook.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_diff.html"><em>knife diff</em></a></td>
-<td>The <strong>knife diff</strong> subcommand is used to compare the differences between files and directories on the Chef server and in the chef-repo. For example, to compare files on the Chef server prior to an uploading or downloading files using the <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">download</span></tt> and <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">upload</span></tt> subcommands, or to ensure that certain files in multiple production environments are the same. This subcommand is similar to the <tt class="docutils literal"><span class="pre">git</span> <span class="pre">diff</span></tt> command that can be used to diff what is in the chef-repo with what is synced to a git repository.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_download.html"><em>knife download</em></a></td>
-<td>The <strong>knife download</strong> subcommand is used to download roles, cookbooks, environments, nodes, and data bags from the Chef server to the current working directory. It can be used to back up data on the Chef server, inspect the state of one or more files, or to extract out-of-process changes users may have made to files on the Chef server, such as if a user made a change that bypassed version source control. This subcommand is often used in conjunction with <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">diff</span></tt>, which can be used to see exactly what changes will be downloaded, and then <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">upload</span></tt>, which does the opposite of <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">download</span></tt>.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_edit.html"><em>knife edit</em></a></td>
-<td>The <strong>knife edit</strong> subcommand is used to edit objects on the Chef server. This subcommand works similar to <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">cookbook</span> <span class="pre">edit</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">data</span> <span class="pre">bag</span> <span class="pre">edit</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">environment</span> <span class="pre">edit</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">node</span> <span class="pre">edit</span></tt>, and <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">role</span> <span class="pre">edit</span></tt>, but with a single verb (and a single action).</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_environment.html"><em>knife environment</em></a></td>
-<td>The <strong>knife environment</strong> subcommand is used to manage environments within a single organization on the Chef server.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_exec.html"><em>knife exec</em></a></td>
-<td>The <strong>knife exec</strong> subcommand uses the knife configuration file to execute Ruby scripts in the context of a fully configured chef-client. This subcommand is most often used to run scripts that will only access Chef server one time (or otherwise very infrequently). Use this subcommand any time that an operation does not warrant full usage of the knife subcommand library.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_index_rebuild.html"><em>knife index rebuild</em></a></td>
-<td>The <strong>knife index rebuild</strong> subcommand is used to rebuild the search indexes for the open source Chef server. This operation is destructive and may take some time.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_list.html"><em>knife list</em></a></td>
-<td>The <strong>knife list</strong> subcommand is used to view a list of objects on the Chef server. This subcommand works similar to <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">cookbook</span> <span class="pre">list</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">data</span> <span class="pre">bag</span> <span class="pre">list</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">environment</span> <span class="pre">list</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">node</span> <span class="pre">list</span></tt>, and <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">role</span> <span class="pre">list</span></tt>, but with a single verb (and a single action).</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_node.html"><em>knife node</em></a></td>
-<td>The <strong>knife node</strong> subcommand is used to manage the nodes that exist on a Chef server.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_raw.html"><em>knife raw</em></a></td>
-<td>The <strong>knife raw</strong> subcommand is used to send a REST request to an endpoint in the Chef server API.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_recipe_list.html"><em>knife recipe list</em></a></td>
-<td>The <strong>knife recipe list</strong> subcommand is used to view all of the recipes that are on a Chef server. A regular expression can be used to limit the results to recipes that match a specific pattern. The regular expression must be within quotes and not be surrounded by forward slashes (/).</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_role.html"><em>knife role</em></a></td>
-<td>The <strong>knife role</strong> subcommand is used to manage the roles that are associated with one or more nodes on a Chef server.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_search.html"><em>knife search</em></a></td>
-<td>The <strong>knife search</strong> subcommand is used run a search query for information that is indexed on a Chef server.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_show.html"><em>knife show</em></a></td>
-<td>The <strong>knife show</strong> subcommand is used to view the details of one (or more) objects on the Chef server. This subcommand works similar to <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">cookbook</span> <span class="pre">show</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">data</span> <span class="pre">bag</span> <span class="pre">show</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">environment</span> <span class="pre">show</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">node</span> <span class="pre">show</span></tt>, and <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">role</span> <span class="pre">show</span></tt>, but with a single verb (and a single action).</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_ssh.html"><em>knife ssh</em></a></td>
-<td>The <strong>knife ssh</strong> subcommand is used to invoke SSH commands (in parallel) on a subset of nodes within an organization, based on the results of a <a class="reference external" href="http://docs.opscode.com/essentials_search.html">search query</a> made to the Chef server.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_status.html"><em>knife status</em></a></td>
-<td>The <strong>knife status</strong> subcommand is used to display a brief summary of the nodes on a Chef server, including the time of the most recent successful chef-client run.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_tag.html"><em>knife tag</em></a></td>
-<td>The <strong>knife tag</strong> subcommand is used to apply tags to nodes on a Chef server.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_upload.html"><em>knife upload</em></a></td>
-<td>The <strong>knife upload</strong> subcommand is used to upload roles, cookbooks, environments, and data bags to the Chef server from the current working directory in the chef-repo. This subcommand is often used in conjunction with <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">diff</span></tt>, which can be used to see exactly what changes will be uploaded, and then <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">download</span></tt>, which does the opposite of <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">upload</span></tt>.</td>
-</tr>
-<tr class="row-odd"><td><a class="reference internal" href="knife_user.html"><em>knife user</em></a></td>
-<td>The <strong>knife user</strong> subcommand is used to manage the list of users and their associated RSA public key-pairs.</td>
-</tr>
-<tr class="row-even"><td><a class="reference internal" href="knife_xargs.html"><em>knife xargs</em></a></td>
-<td>The <strong>knife xargs</strong> subcommand is used to take patterns from standard input, download as JSON, run a command against the downloaded JSON, and then upload any changes.</td>
-</tr>
-</tbody>
-</table>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_bootstrap.html b/distro/common/html/knife_bootstrap.html
deleted file mode 100644
index 5e3d70404f..0000000000
--- a/distro/common/html/knife_bootstrap.html
+++ /dev/null
@@ -1,285 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife bootstrap &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-bootstrap">
-<h1>knife bootstrap<a class="headerlink" href="#knife-bootstrap" title="Permalink to this headline">¶</a></h1>
-<p>A bootstrap is a process that installs the chef-client on a target system so that it can run as a chef-client and communicate with a Chef server.</p>
-<p>The <strong>knife bootstrap</strong> subcommand is used to run a bootstrap operation that installs the chef-client on the target system. The bootstrap operation must specify the IP address or FQDN of the target system.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">To bootstrap the chef-client on Microsoft Windows machines, the <a class="reference external" href="http://docs.opscode.com/plugin_knife_windows.html">knife-windows</a> plugins is required, which includes the necessary bootstrap scripts that are used to do the actual installation.</p>
-</div>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife bootstrap FQDN_or_IP_ADDRESS <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-A</span></tt>, <tt class="docutils literal"><span class="pre">--forward-agent</span></tt></dt>
-<dd>Use to enable SSH agent forwarding.</dd>
-<dt><tt class="docutils literal"><span class="pre">--bootstrap-curl-options</span> <span class="pre">OPTIONS</span></tt></dt>
-<dd>Use to specify arbitrary options to be added to the bootstrap command when using cURL. This option may not be used in the same command with <tt class="docutils literal"><span class="pre">--bootstrap-install-command</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--bootstrap-install-command</span> <span class="pre">COMMAND</span></tt></dt>
-<dd>Use to execute a custom installation command sequence for the chef-client. This option may not be used in the same command with <tt class="docutils literal"><span class="pre">--bootstrap-curl-options</span></tt>, <tt class="docutils literal"><span class="pre">--bootstrap-install-sh</span></tt>, or <tt class="docutils literal"><span class="pre">--bootstrap-wget-options</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--bootstrap-install-sh</span> <span class="pre">URL</span></tt></dt>
-<dd>Use to fetch and execute an installation script at the specified URL. This option may not be used in the same command with <tt class="docutils literal"><span class="pre">--bootstrap-install-command</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--bootstrap-no-proxy</span> <span class="pre">NO_PROXY_URL_or_IP</span></tt></dt>
-<dd><p class="first">A URL or IP address that specifies a location that should not be proxied.</p>
-<div class="last admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">This option is used internally by Chef to help verify bootstrap operations during testing and should never be used during an actual bootstrap operation.</p>
-</div>
-</dd>
-<dt><tt class="docutils literal"><span class="pre">--bootstrap-proxy</span> <span class="pre">PROXY_URL</span></tt></dt>
-<dd>The proxy server for the node that is the target of a bootstrap operation.</dd>
-<dt><tt class="docutils literal"><span class="pre">--bootstrap-version</span> <span class="pre">VERSION</span></tt></dt>
-<dd>The version of the chef-client to install.</dd>
-<dt><tt class="docutils literal"><span class="pre">--bootstrap-wget-options</span> <span class="pre">OPTIONS</span></tt></dt>
-<dd>Use to specify arbitrary options to be added to the bootstrap command when using GNU Wget. This option may not be used in the same command with <tt class="docutils literal"><span class="pre">--bootstrap-install-command</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-E</span> <span class="pre">ENVIRONMENT</span></tt>, <tt class="docutils literal"><span class="pre">--environment</span> <span class="pre">ENVIRONMENT</span></tt></dt>
-<dd>The name of the environment. When this option is added to a command, the command will run only against the named environment.</dd>
-<dt><tt class="docutils literal"><span class="pre">-G</span> <span class="pre">GATEWAY</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-gateway</span> <span class="pre">GATEWAY</span></tt></dt>
-<dd>The SSH tunnel or gateway that is used to run a bootstrap action on a machine that is not accessible from the workstation.</dd>
-<dt><tt class="docutils literal"><span class="pre">--hint</span> <span class="pre">HINT_NAME[=HINT_FILE]</span></tt></dt>
-<dd><p class="first">Use to specify an Ohai hint to be set on the target node.</p>
-<p>Ohai hints are used to tell Ohai something about the system that it is running on that it would not be able to discover itself. An Ohai hint exists if a JSON file exists in the hint directory with the same name as the hint. For example, calling <tt class="docutils literal"><span class="pre">hint?('antartica')</span></tt> in an Ohai plugin would return an empty hash if the file <tt class="docutils literal"><span class="pre">antartica.json</span></tt> existed in the hints directory, and return nil if the file does not exist.</p>
-<p>If the hint file contains JSON content, it will be returned as a hash from the call to <tt class="docutils literal"><span class="pre">hint?</span></tt>.</p>
-<div class="highlight-javascript"><div class="highlight"><pre><span class="p">{</span>
- <span class="s2">&quot;snow&quot;</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
- <span class="s2">&quot;penguins&quot;</span><span class="o">:</span> <span class="s2">&quot;many&quot;</span>
-<span class="p">}</span>
-</pre></div>
-</div>
-<div class="highlight-ruby"><div class="highlight"><pre><span class="n">arctic_hint</span> <span class="o">=</span> <span class="n">hint?</span><span class="p">(</span><span class="s1">&#39;antartica&#39;</span><span class="p">)</span>
-<span class="k">if</span> <span class="n">arctic_hint</span><span class="o">[</span><span class="s1">&#39;snow&#39;</span><span class="o">]</span>
- <span class="s2">&quot;There are </span><span class="si">#{</span><span class="n">arctic_hint</span><span class="o">[</span><span class="s1">&#39;penguins&#39;</span><span class="o">]</span><span class="si">}</span><span class="s2"> penguins here.&quot;</span>
-<span class="k">else</span>
- <span class="s2">&quot;There is no snow here, and penguins like snow.&quot;</span>
-<span class="k">end</span>
-</pre></div>
-</div>
-<p>The default directory in which hint files are located is <tt class="docutils literal"><span class="pre">/etc/chef/ohai/hints/</span></tt>. Use the <tt class="docutils literal"><span class="pre">Ohai::Config[:hints_path]</span></tt> setting in the client.rb file to customize this location.</p>
-<p class="last"><tt class="docutils literal"><span class="pre">HINT_FILE</span></tt> is the name of the JSON file. <tt class="docutils literal"><span class="pre">HINT_NAME</span></tt> is the name of a hint in a JSON file. Use multiple <tt class="docutils literal"><span class="pre">--hint</span></tt> options to specify multiple hints.</p>
-</dd>
-<dt><tt class="docutils literal"><span class="pre">-i</span> <span class="pre">IDENTITY_FILE</span></tt>, <tt class="docutils literal"><span class="pre">--identity-file</span> <span class="pre">IDENTITY_FILE</span></tt></dt>
-<dd>The SSH identity file used for authentication. Key-based authentication is recommended.</dd>
-<dt><tt class="docutils literal"><span class="pre">-j</span> <span class="pre">JSON_ATTRIBS</span></tt>, <tt class="docutils literal"><span class="pre">--json-attributes</span> <span class="pre">JSON_ATTRIBS</span></tt></dt>
-<dd>A JSON string that is added to the first run of a chef-client.</dd>
-<dt><tt class="docutils literal"><span class="pre">-N</span> <span class="pre">NAME</span></tt>, <tt class="docutils literal"><span class="pre">--node-name</span> <span class="pre">NAME</span></tt></dt>
-<dd>The name of the node.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]host-key-verify</span></tt></dt>
-<dd>Use <tt class="docutils literal"><span class="pre">--no-host-key-verify</span></tt> to disable host key verification. Default setting: <tt class="docutils literal"><span class="pre">--host-key-verify</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]node-verify-api-cert</span></tt></dt>
-<dd>Use <tt class="docutils literal"><span class="pre">verify_api_cert</span></tt> to only do SSL validation of the Chef server connection; may be needed if the chef-client needs to talk to other services that have broken SSL certificates. If this option is not specified, the setting for <tt class="docutils literal"><span class="pre">verify_api_cert</span></tt> in the configuration file is applied.</dd>
-<dt><tt class="docutils literal"><span class="pre">--node-ssl-verify-mode</span> <span class="pre">PEER_OR_NONE</span></tt></dt>
-<dd><p class="first">The verify mode for HTTPS requests.</p>
-<p>Use <tt class="docutils literal"><span class="pre">:verify_none</span></tt> to do no validation of SSL certificates.</p>
-<p>Use <tt class="docutils literal"><span class="pre">:verify_peer</span></tt> to do validation of all SSL certificates, including the Chef server connections, S3 connections, and any HTTPS <strong>remote_file</strong> resource URLs used in the chef-client run. This is the recommended setting.</p>
-<p class="last">If this option is not specified, the setting for <tt class="docutils literal"><span class="pre">ssl_verify_mode</span></tt> in the configuration file is applied.</p>
-</dd>
-<dt><tt class="docutils literal"><span class="pre">-p</span> <span class="pre">PORT</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-port</span> <span class="pre">PORT</span></tt></dt>
-<dd>The SSH port.</dd>
-<dt><tt class="docutils literal"><span class="pre">-P</span> <span class="pre">PASSWORD</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-password</span> <span class="pre">PASSWORD</span></tt></dt>
-<dd>The SSH password. This can be used to pass the password directly on the command line. If this option is not specified (and a password is required) knife will prompt for the password.</dd>
-<dt><tt class="docutils literal"><span class="pre">--prerelease</span></tt></dt>
-<dd>Use to install pre-release gems.</dd>
-<dt><tt class="docutils literal"><span class="pre">-r</span> <span class="pre">RUN_LIST</span></tt>, <tt class="docutils literal"><span class="pre">--run-list</span> <span class="pre">RUN_LIST</span></tt></dt>
-<dd>A comma-separated list of roles and/or recipes to be applied.</dd>
-<dt><tt class="docutils literal"><span class="pre">--secret</span> <span class="pre">SECRET</span></tt></dt>
-<dd>The encryption key that is used for values contained within a data bag item.</dd>
-<dt><tt class="docutils literal"><span class="pre">--secret-file</span> <span class="pre">FILE</span></tt></dt>
-<dd>The path to the file that contains the encryption key.</dd>
-<dt><tt class="docutils literal"><span class="pre">--sudo</span></tt></dt>
-<dd>Use to execute a bootstrap operation with sudo.</dd>
-<dt><tt class="docutils literal"><span class="pre">-t</span> <span class="pre">TEMPLATE</span></tt>, <tt class="docutils literal"><span class="pre">--bootstrap-template</span> <span class="pre">TEMPLATE</span></tt></dt>
-<dd>Use to specify the bootstrap template to use. This may specify the name of a bootstrap template&#8212;<tt class="docutils literal"><span class="pre">chef-full</span></tt>, for example&#8212;or it may specify the full path to an Embedded Ruby (ERB) template that defines a custom bootstrap. Default value: <tt class="docutils literal"><span class="pre">chef-full</span></tt>, which installs the chef-client using the omnibus installer on all supported platforms.</dd>
-<dt><tt class="docutils literal"><span class="pre">--use-sudo-password</span></tt></dt>
-<dd>Use to perform a bootstrap operation with sudo; specify the password with the <tt class="docutils literal"><span class="pre">-P</span></tt> (or <tt class="docutils literal"><span class="pre">--ssh-password</span></tt>) option.</dd>
-<dt><tt class="docutils literal"><span class="pre">-V</span> <span class="pre">-V</span></tt></dt>
-<dd>Use to run the initial chef-client run at the <tt class="docutils literal"><span class="pre">debug</span></tt> log-level (e.g. <tt class="docutils literal"><span class="pre">chef-client</span> <span class="pre">-l</span> <span class="pre">debug</span></tt>).</dd>
-<dt><tt class="docutils literal"><span class="pre">-x</span> <span class="pre">USERNAME</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-user</span> <span class="pre">USERNAME</span></tt></dt>
-<dd>The SSH user name.</dd>
-</dl>
-</div>
-<div class="section" id="custom-templates">
-<h2>Custom Templates<a class="headerlink" href="#custom-templates" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">chef-full</span></tt> distribution uses the omnibus installer. For most bootstrap operations, regardless of the platform on which the target node is running, using the <tt class="docutils literal"><span class="pre">chef-full</span></tt> distribution is the best approach for installing the chef-client on a target node. In some situations, using another supported distribution is necessary. And in some situations, a custom template may be required. For example, the default bootstrap operation relies on an Internet connection to get the distribution to the target node. If a target node cannot access the Internet, then a custom template can be used to define a specific location for the distribution so that the target node may access it during the bootstrap operation.</p>
-<p>A custom bootstrap template file (<tt class="docutils literal"><span class="pre">template_filename.erb</span></tt>) must be located in a <tt class="docutils literal"><span class="pre">bootstrap/</span></tt> directory. Use the <tt class="docutils literal"><span class="pre">--distro</span></tt> option with the <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">bootstrap</span></tt> subcommand to specify the bootstrap template file. For example, a bootstrap template file named &#8220;british_sea_power.erb&#8221;:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife bootstrap 123.456.7.8 -x username -P password --sudo --distro <span class="s2">&quot;british_sea_power.erb&quot;</span>
-</pre></div>
-</div>
-<p>The following examples show how a bootstrap template file can be customized for various platforms.</p>
-<div class="section" id="ubuntu-12-04">
-<h3>Ubuntu 12.04<a class="headerlink" href="#ubuntu-12-04" title="Permalink to this headline">¶</a></h3>
-<p>The following example shows how to modify the default script for Ubuntu 12.04. First, copy the bootstrap template from the default location. If the chef-client is installed from a RubyGems, the full path can be found in the gem contents:</p>
-<div class="highlight-bash"><div class="highlight"><pre>% gem contents chef | grep ubuntu12.04-gems
-/Users/jtimberman/.rvm/gems/ruby-1.9.2-p180/gems/chef-0.10.2/lib/chef/knife/bootstrap/ubuntu12.04-gems.erb
-</pre></div>
-</div>
-<p>Copy the template to the chef-repo in the <tt class="docutils literal"><span class="pre">.chef/bootstrap</span></tt> directory:</p>
-<div class="highlight-bash"><div class="highlight"><pre>% cp /Users/jtimberman/.rvm/gems/ruby-1.9.2-p180/gems/chef-0.10.2/
- lib/chef/knife/bootstrap/ubuntu12.04-gems.erb ~/chef-repo/.chef/
- bootstrap/ubuntu12.04-gems-mine.erb
-</pre></div>
-</div>
-<p>Modify the template with any editor, then use it with the <tt class="docutils literal"><span class="pre">-d</span></tt> or <tt class="docutils literal"><span class="pre">--distro</span></tt> option in the <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">bootstrap</span></tt> operation, or use any of the knife plug-ins that support cloud computing.</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife bootstrap 192.168.1.100 -r <span class="s1">&#39;role[webserver]&#39;</span> -d ubuntu12.04-gems-mine
-</pre></div>
-</div>
-<p>Alternatively, an example bootstrap template can be found in the git source for the chef-repo: <a class="reference external" href="https://github.com/opscode/chef/tree/master/lib/chef/knife/bootstrap">https://github.com/opscode/chef/tree/master/lib/chef/knife/bootstrap</a>. Copy the template to <tt class="docutils literal"><span class="pre">~/.chef-repo/.chef/bootstrap/ubuntu12.04-apt.erb</span></tt> and modify the template appropriately.</p>
-</div>
-<div class="section" id="debian-and-apt">
-<h3>Debian and Apt<a class="headerlink" href="#debian-and-apt" title="Permalink to this headline">¶</a></h3>
-<p>The following example shows how to use the <strong>knife bootstrap</strong> sub-command to create a client configuration file (/etc/chef/client.rb) that uses Hosted Chef as the Chef server. The configuration file will look something like:</p>
-<div class="highlight-ruby"><div class="highlight"><pre><span class="n">log_level</span> <span class="ss">:info</span>
-<span class="n">log_location</span> <span class="no">STDOUT</span>
-<span class="n">chef_server_url</span> <span class="s1">&#39;https://api.opscode.com/organizations/ORGNAME&#39;</span>
-<span class="n">validation_client_name</span> <span class="s1">&#39;ORGNAME-validator&#39;</span>
-</pre></div>
-</div>
-<p>The <strong>knife bootstrap</strong> sub-command will look in three locations for the template that is used during the bootstrap operation. The locations are:</p>
-<ol class="arabic simple">
-<li>A bootstrap directory in the installed knife library; the actual location may vary, depending how the chef-client is installed</li>
-<li>A bootstrap directory in the <tt class="docutils literal"><span class="pre">$PWD/.chef</span></tt>, e.g. in <tt class="docutils literal"><span class="pre">~/chef-repo/.chef</span></tt></li>
-<li>A bootstrap directory in the users <tt class="docutils literal"><span class="pre">$HOME/.chef</span></tt></li>
-</ol>
-<p>If, in the example above, the second location was used, then create the <tt class="docutils literal"><span class="pre">.chef/bootstrap/</span></tt> directory in the chef-repo, and then create the Embedded Ruby (ERB) template file by running commands similar to the following:</p>
-<div class="highlight-bash"><div class="highlight"><pre>mkdir ~/.chef/bootstrap
-vi ~/.chef/bootstrap/debian5.0-apt.erb
-</pre></div>
-</div>
-<p>When finished creating the directory and the Embedded Ruby (ERB) template file, edit the template to run the SSH commands. Then set up the validation certificate and the client configuration file.</p>
-<p>Finally, run the chef-client on the node using a <strong>knife bootstrap</strong> command that specifies a run-list (the <tt class="docutils literal"><span class="pre">-r</span></tt> option). The bootstrap template can be called using a command similar to the following:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife bootstrap mynode.example.com -r <span class="s1">&#39;role[webserver]&#39;</span>,<span class="s1">&#39;role[production]&#39;</span> --distro debian5.0-apt
-</pre></div>
-</div>
-</div>
-<div class="section" id="microsoft-windows">
-<h3>Microsoft Windows<a class="headerlink" href="#microsoft-windows" title="Permalink to this headline">¶</a></h3>
-<p>The following example shows how to modify the default script for Microsoft Windows and Windows PowerShell:</p>
-<div class="highlight-bash"><div class="highlight"><pre>@setlocal
-
-&lt;%<span class="o">=</span> <span class="s2">&quot;SETX HTTP_PROXY \&quot;#{knife_config[:bootstrap_proxy]}\&quot;&quot;</span> <span class="k">if </span>knife_config<span class="o">[</span>:bootstrap_proxy<span class="o">]</span> %&gt;
-@mkdir &lt;%<span class="o">=</span> bootstrap_directory %&gt;
-
-&gt; &lt;%<span class="o">=</span> bootstrap_directory %&gt;<span class="se">\w</span>get.ps1 <span class="o">(</span>
- &lt;%<span class="o">=</span> win_wget_ps %&gt;
-<span class="o">)</span>
-
-:install
-@rem Install Chef using chef-client MSI installer
-
-&lt;% <span class="nv">url</span><span class="o">=</span><span class="s2">&quot;http://reposerver.example.com/chef-client-11.6.0.rc.1-1.windows.msi&quot;</span> -%&gt;
-@set <span class="s2">&quot;REMOTE_SOURCE_MSI_URL=&lt;%= url %&gt;&quot;</span>
-@set <span class="s2">&quot;LOCAL_DESTINATION_MSI_PATH=&lt;%= local_download_path %&gt;&quot;</span>
-
-@powershell -ExecutionPolicy Unrestricted -NoProfile -NonInteractive <span class="s2">&quot;&amp; &#39;&lt;%= bootstrap_directory %&gt;\wget.ps1&#39; &#39;%REMOTE_SOURCE_MSI_URL%&#39; &#39;%LOCAL_DESTINATION_MSI_PATH%&#39;&quot;</span>
-
-@REM Replace install_chef from knife-windows Gem with one that has extra flags to turn on Chef service feature -- only available in Chef &gt;<span class="o">=</span> 11.6.x
-@REM &lt;%<span class="o">=</span> install_chef %&gt;
-@echo Installing Chef Client 11.6.0.rc1 with msiexec
-@msiexec /q /i <span class="s2">&quot;%LOCAL_DESTINATION_MSI_PATH%&quot;</span> <span class="nv">ADDLOCAL</span><span class="o">=</span><span class="s2">&quot;ChefClientFeature,ChefServiceFeature&quot;</span>
-@endlocal
-
-@echo Writing validation key...
-
-&gt; &lt;%<span class="o">=</span> bootstrap_directory %&gt;<span class="se">\v</span>alidation.pem <span class="o">(</span>
- &lt;%<span class="o">=</span> validation_key %&gt;
-<span class="o">)</span>
-
-@echo Validation key written.
-
-&lt;% <span class="k">if</span> @config<span class="o">[</span>:encrypted_data_bag_secret<span class="o">]</span> -%&gt;
-&gt; &lt;%<span class="o">=</span> bootstrap_directory %&gt;<span class="se">\e</span>ncrypted_data_bag_secret <span class="o">(</span>
- &lt;%<span class="o">=</span> encrypted_data_bag_secret %&gt;
-<span class="o">)</span>
-&lt;% end -%&gt;
-
-&gt; &lt;%<span class="o">=</span> bootstrap_directory %&gt;<span class="se">\c</span>lient.rb <span class="o">(</span>
- &lt;%<span class="o">=</span> config_content %&gt;
-<span class="o">)</span>
-
-&gt; &lt;%<span class="o">=</span> bootstrap_directory %&gt;<span class="se">\f</span>irst-boot.json <span class="o">(</span>
- &lt;%<span class="o">=</span> run_list %&gt;
-<span class="o">)</span>
-
-&lt;%<span class="o">=</span> start_chef %&gt;
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Use an SSH password</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife bootstrap 192.168.1.1 -x username -P PASSWORD --sudo
-</pre></div>
-</div>
-<p><strong>Use a file that contains a private key</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife bootstrap 192.168.1.1 -x username -i ~/.ssh/id_rsa --sudo
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_client.html b/distro/common/html/knife_client.html
deleted file mode 100644
index d8c28f708a..0000000000
--- a/distro/common/html/knife_client.html
+++ /dev/null
@@ -1,285 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife client &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-client">
-<h1>knife client<a class="headerlink" href="#knife-client" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife client</strong> subcommand is used to manage an API client list and their associated RSA public key-pairs. This allows authentication requests to be made to the Chef server by any entity that uses the Chef server API, such as the chef-client and knife.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<div class="section" id="bulk-delete">
-<h2>bulk delete<a class="headerlink" href="#bulk-delete" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">bulk</span> <span class="pre">delete</span></tt> argument is used to delete any API client that matches a pattern defined by a regular expression. The regular expression must be within quotes and not be surrounded by forward slashes (<tt class="docutils literal"><span class="pre">/</span></tt>).</p>
-<div class="section" id="syntax">
-<h3>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife client bulk delete REGEX
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h3>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="examples">
-<h3>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h3>
-<p>None.</p>
-</div>
-</div>
-<div class="section" id="create">
-<h2>create<a class="headerlink" href="#create" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">create</span></tt> argument is used to create a new API client. This process will generate an RSA key pair for the named API client. The public key will be stored on the Chef server and the private key will be displayed on <tt class="docutils literal"><span class="pre">STDOUT</span></tt> or written to a named file.</p>
-<ul class="simple">
-<li>For the chef-client, the private key should be copied to the system as <tt class="docutils literal"><span class="pre">/etc/chef/client.pem</span></tt>.</li>
-<li>For knife, the private key is typically copied to <tt class="docutils literal"><span class="pre">~/.chef/client_name.pem</span></tt> and referenced in the knife.rb configuration file.</li>
-</ul>
-<div class="section" id="id1">
-<h3>Syntax<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife client create CLIENT_NAME <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id2">
-<h3>Options<a class="headerlink" href="#id2" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span></tt>, <tt class="docutils literal"><span class="pre">--admin</span></tt></dt>
-<dd>Use to create a client as an admin client. This is required for any user to access Open Source Chef as an administrator. This option only works when used with the open source Chef server and will have no effect when used with Enterprise Chef.</dd>
-<dt><tt class="docutils literal"><span class="pre">-f</span> <span class="pre">FILE</span></tt>, <tt class="docutils literal"><span class="pre">--file</span> <span class="pre">FILE</span></tt></dt>
-<dd>Use to save a private key to the specified file name.</dd>
-<dt><tt class="docutils literal"><span class="pre">--validator</span></tt></dt>
-<dd>Use to create the client as the chef-validator. Default value: <tt class="docutils literal"><span class="pre">true</span></tt>.</dd>
-</dl>
-</div>
-<div class="section" id="id3">
-<h3>Examples<a class="headerlink" href="#id3" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Create an admin client</strong></p>
-<p>To create a chef-client that can access the Chef server API as an administrator&#8212;sometimes referred to as an &#8220;API chef-client&#8221;&#8212;with the name &#8220;exampleorg&#8221; and save its private key to a file, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife client create exampleorg -a -f <span class="s2">&quot;/etc/chef/client.pem&quot;</span>
-</pre></div>
-</div>
-<p><strong>Create an admin client for Enterprise Chef</strong></p>
-<p>When running the <tt class="docutils literal"><span class="pre">create</span></tt> argument on Enterprise Chef, be sure to omit the <tt class="docutils literal"><span class="pre">-a</span></tt> option:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife client create exampleorg -f <span class="s2">&quot;/etc/chef/client.pem&quot;</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="delete">
-<h2>delete<a class="headerlink" href="#delete" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">delete</span></tt> argument is used to delete a registered API client.</p>
-<div class="section" id="id4">
-<h3>Syntax<a class="headerlink" href="#id4" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife client delete CLIENT_NAME
-</pre></div>
-</div>
-</div>
-<div class="section" id="id5">
-<h3>Options<a class="headerlink" href="#id5" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id6">
-<h3>Examples<a class="headerlink" href="#id6" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Delete a client</strong></p>
-<p>To delete a client with the name &#8220;client_foo&#8221;, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife client delete client_foo
-</pre></div>
-</div>
-<p>Type <tt class="docutils literal"><span class="pre">Y</span></tt> to confirm a deletion.</p>
-</div>
-</div>
-<div class="section" id="edit">
-<h2>edit<a class="headerlink" href="#edit" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">edit</span></tt> argument is used to edit the details of a registered API client. When this argument is run, knife will open $EDITOR to enable editing of the <tt class="docutils literal"><span class="pre">admin</span></tt> attribute. (None of the other attributes should be changed using this argument.) When finished, knife will update the Chef server with those changes.</p>
-<div class="section" id="id7">
-<h3>Syntax<a class="headerlink" href="#id7" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife client edit CLIENT_NAME
-</pre></div>
-</div>
-</div>
-<div class="section" id="id8">
-<h3>Options<a class="headerlink" href="#id8" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id9">
-<h3>Examples<a class="headerlink" href="#id9" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Edit a client</strong></p>
-<p>To edit a client with the name &#8220;exampleorg&#8221;, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife client edit exampleorg
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="list">
-<h2>list<a class="headerlink" href="#list" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">list</span></tt> argument is used to view a list of registered API client.</p>
-<div class="section" id="id10">
-<h3>Syntax<a class="headerlink" href="#id10" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife client list <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id11">
-<h3>Options<a class="headerlink" href="#id11" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-w</span></tt>, <tt class="docutils literal"><span class="pre">--with-uri</span></tt></dt>
-<dd>Use to show the corresponding URIs.</dd>
-</dl>
-</div>
-<div class="section" id="id12">
-<h3>Examples<a class="headerlink" href="#id12" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>View a list of clients</strong></p>
-<p>To verify the API client list for the Chef server, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife client list
-</pre></div>
-</div>
-<p>to return something similar to:</p>
-<div class="highlight-bash"><div class="highlight"><pre>exampleorg
-i-12345678
-rs-123456
-</pre></div>
-</div>
-<p>To verify that an API client can authenticate to the
-Chef server correctly, try getting a list of clients using <tt class="docutils literal"><span class="pre">-u</span></tt> and <tt class="docutils literal"><span class="pre">-k</span></tt> options to specify its name and private key:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife client list -u ORGNAME -k .chef/ORGNAME.pem
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="reregister">
-<h2>reregister<a class="headerlink" href="#reregister" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">reregister</span></tt> argument is used to regenerate an RSA key pair for an API client. The public key will be stored on the Chef server and the private key will be displayed on <tt class="docutils literal"><span class="pre">STDOUT</span></tt> or written to a named file.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Running this argument will invalidate the previous RSA key pair, making it unusable during authentication to the Chef server.</p>
-</div>
-<div class="section" id="id13">
-<h3>Syntax<a class="headerlink" href="#id13" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife client reregister CLIENT_NAME <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id14">
-<h3>Options<a class="headerlink" href="#id14" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-f</span> <span class="pre">FILE_NAME</span></tt>, <tt class="docutils literal"><span class="pre">--file</span> <span class="pre">FILE_NAME</span></tt></dt>
-<dd>Use to save a private key to the specified file name.</dd>
-</dl>
-</div>
-<div class="section" id="id15">
-<h3>Examples<a class="headerlink" href="#id15" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Reregister clients</strong></p>
-<p>To regenerate the RSA key pair for a client named &#8220;testclient&#8221; and save it to a file named &#8220;rsa_key&#8221;, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife client regenerate testclient -f rsa_key
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="show">
-<h2>show<a class="headerlink" href="#show" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">show</span></tt> argument is used to show the details of an API client.</p>
-<div class="section" id="id16">
-<h3>Syntax<a class="headerlink" href="#id16" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife client show CLIENT_NAME <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id17">
-<h3>Options<a class="headerlink" href="#id17" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span> <span class="pre">ATTR</span></tt>, <tt class="docutils literal"><span class="pre">--attribute</span> <span class="pre">ATTR</span></tt></dt>
-<dd>The attribute (or attributes) to show.</dd>
-</dl>
-</div>
-<div class="section" id="id18">
-<h3>Examples<a class="headerlink" href="#id18" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Show clients</strong></p>
-<p>To view a client named &#8220;testclient&#8221;, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife client show testclient
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>admin: <span class="nb">false</span>
-chef_type: client
-json_class: Chef::ApiClient
-name: testclient
-public_key:
-</pre></div>
-</div>
-<p>To view information in JSON format, use the <tt class="docutils literal"><span class="pre">-F</span></tt> common option as part of the command like this:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role show devops -F json
-</pre></div>
-</div>
-<p>Other formats available include <tt class="docutils literal"><span class="pre">text</span></tt>, <tt class="docutils literal"><span class="pre">yaml</span></tt>, and <tt class="docutils literal"><span class="pre">pp</span></tt>.</p>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_common_options.html b/distro/common/html/knife_common_options.html
deleted file mode 100644
index 5006ccf27f..0000000000
--- a/distro/common/html/knife_common_options.html
+++ /dev/null
@@ -1,96 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>Common Options &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="common-options">
-<h1>Common Options<a class="headerlink" href="#common-options" title="Permalink to this headline">¶</a></h1>
-<p>The following options can be run with all knife sub-commands and plug-ins:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-c</span> <span class="pre">CONFIG_FILE</span></tt>, <tt class="docutils literal"><span class="pre">--config</span> <span class="pre">CONFIG_FILE</span></tt></dt>
-<dd>The configuration file to use. For example, when knife is run from a node that is configured to be managed by the Chef server, this option is used to allow knife to use the same credentials as the chef-client when communicating with the Chef server.</dd>
-<dt><tt class="docutils literal"><span class="pre">--chef-zero-port</span> <span class="pre">PORT</span></tt></dt>
-<dd>The port on which chef-zero will listen.</dd>
-<dt><tt class="docutils literal"><span class="pre">-d</span></tt>, <tt class="docutils literal"><span class="pre">--disable-editing</span></tt></dt>
-<dd>Use to prevent the $EDITOR from being opened and to accept data as-is.</dd>
-<dt><tt class="docutils literal"><span class="pre">--defaults</span></tt></dt>
-<dd>Use to have knife use the default value instead of asking a user to provide one.</dd>
-<dt><tt class="docutils literal"><span class="pre">-e</span> <span class="pre">EDITOR</span></tt>, <tt class="docutils literal"><span class="pre">--editor</span> <span class="pre">EDITOR</span></tt></dt>
-<dd>The $EDITOR that is used for all interactive commands.</dd>
-<dt><tt class="docutils literal"><span class="pre">-E</span> <span class="pre">ENVIRONMENT</span></tt>, <tt class="docutils literal"><span class="pre">--environment</span> <span class="pre">ENVIRONMENT</span></tt></dt>
-<dd>The name of the environment. When this option is added to a command, the command will run only against the named environment. This option is ignored during search queries made using the <strong>knife search</strong> subcommand.</dd>
-<dt><tt class="docutils literal"><span class="pre">-F</span> <span class="pre">FORMAT</span></tt>, <tt class="docutils literal"><span class="pre">--format</span> <span class="pre">FORMAT</span></tt></dt>
-<dd>The output format: <tt class="docutils literal"><span class="pre">summary</span></tt> (default), <tt class="docutils literal"><span class="pre">text</span></tt>, <tt class="docutils literal"><span class="pre">json</span></tt>, <tt class="docutils literal"><span class="pre">yaml</span></tt>, and <tt class="docutils literal"><span class="pre">pp</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-h</span></tt>, <tt class="docutils literal"><span class="pre">--help</span></tt></dt>
-<dd>Shows help for the command.</dd>
-<dt><tt class="docutils literal"><span class="pre">-k</span> <span class="pre">KEY</span></tt>, <tt class="docutils literal"><span class="pre">--key</span> <span class="pre">KEY</span></tt></dt>
-<dd>The private key that knife will use to sign requests made by the API client to the Chef server.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]color</span></tt></dt>
-<dd>Use to view colored output.</dd>
-<dt><tt class="docutils literal"><span class="pre">--print-after</span></tt></dt>
-<dd>Use to show data after a destructive operation.</dd>
-<dt><tt class="docutils literal"><span class="pre">-s</span> <span class="pre">URL</span></tt>, <tt class="docutils literal"><span class="pre">--server-url</span> <span class="pre">URL</span></tt></dt>
-<dd>The URL for the Chef server.</dd>
-<dt><tt class="docutils literal"><span class="pre">-u</span> <span class="pre">USER</span></tt>, <tt class="docutils literal"><span class="pre">--user</span> <span class="pre">USER</span></tt></dt>
-<dd>The user name used by knife to sign requests made by the API client to the Chef server. Authentication will fail if the user name does not match the private key.</dd>
-<dt><tt class="docutils literal"><span class="pre">-v</span></tt>, <tt class="docutils literal"><span class="pre">--version</span></tt></dt>
-<dd>The version of the chef-client.</dd>
-<dt><tt class="docutils literal"><span class="pre">-V</span></tt>, <tt class="docutils literal"><span class="pre">--verbose</span></tt></dt>
-<dd>Set for more verbose outputs. Use <tt class="docutils literal"><span class="pre">-VV</span></tt> for maximum verbosity.</dd>
-<dt><tt class="docutils literal"><span class="pre">-y</span></tt>, <tt class="docutils literal"><span class="pre">--yes</span></tt></dt>
-<dd>Use to respond to all confirmation prompts with &#8220;Yes&#8221;. knife will not ask for confirmation.</dd>
-<dt><tt class="docutils literal"><span class="pre">-z</span></tt>, <tt class="docutils literal"><span class="pre">--local-mode</span></tt></dt>
-<dd>Use to run the chef-client in local mode. This allows all commands that work against the Chef server to also work against the local chef-repo.</dd>
-</dl>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_configure.html b/distro/common/html/knife_configure.html
deleted file mode 100644
index ef4d91287b..0000000000
--- a/distro/common/html/knife_configure.html
+++ /dev/null
@@ -1,105 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife configure &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-configure">
-<h1>knife configure<a class="headerlink" href="#knife-configure" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife configure</strong> subcommand is used to create the knife.rb and client.rb files so that they can be distributed to workstations and nodes.</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax when creating a knife.rb file:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife configure <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-<p>and the following syntax when creating a client.rb file:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife configure client DIRECTORY
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This subcommand has the following options for use when configuring a knife.rb file:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">--admin-client-name</span> <span class="pre">NAME</span></tt></dt>
-<dd>The name of the client, typically the name of the admin client.</dd>
-<dt><tt class="docutils literal"><span class="pre">--admin-client-key</span> <span class="pre">PATH</span></tt></dt>
-<dd>The path to the private key used by the client, typically a file named <tt class="docutils literal"><span class="pre">admin.pem</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-i</span></tt>, <tt class="docutils literal"><span class="pre">--initial</span></tt></dt>
-<dd>Use to create a API client, typically an administrator client on a freshly-installed Chef server.</dd>
-<dt><tt class="docutils literal"><span class="pre">-r</span> <span class="pre">REPO</span></tt>, <tt class="docutils literal"><span class="pre">--repository</span> <span class="pre">REPO</span></tt></dt>
-<dd>The path to the chef-repo.</dd>
-<dt><tt class="docutils literal"><span class="pre">--validation-client-name</span> <span class="pre">NAME</span></tt></dt>
-<dd>The name of the validation client, typically a client named chef-validator.</dd>
-<dt><tt class="docutils literal"><span class="pre">--validation-key</span> <span class="pre">PATH</span></tt></dt>
-<dd>The path to the validation key used by the client, typically a file named chef-validator.pem.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Configure knife.rb</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife configure
-</pre></div>
-</div>
-<p><strong>Configure client.rb</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife configure client <span class="s1">&#39;/directory&#39;</span>
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_cookbook.html b/distro/common/html/knife_cookbook.html
deleted file mode 100644
index b5a0f99203..0000000000
--- a/distro/common/html/knife_cookbook.html
+++ /dev/null
@@ -1,487 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife cookbook &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-cookbook">
-<h1>knife cookbook<a class="headerlink" href="#knife-cookbook" title="Permalink to this headline">¶</a></h1>
-<p>A cookbook is the fundamental unit of configuration and policy distribution. Each cookbook defines a scenario, such as everything needed to install and configure MySQL, and then it contains all of the components that are required to support that scenario, including:</p>
-<ul class="simple">
-<li>Attribute values that are set on nodes</li>
-<li>Definitions that allow the creation of reusable collections of resources</li>
-<li>File distributions</li>
-<li>Libraries that extend the chef-client and/or provide helpers to Ruby code</li>
-<li>Recipes that specify which resources to manage and the order in which those resources will be applied</li>
-<li>Custom resources and providers</li>
-<li>Templates</li>
-<li>Versions</li>
-<li>Metadata about recipes (including dependencies), version constraints, supported platforms, and so on</li>
-</ul>
-<p>The <strong>knife cookbook</strong> subcommand is used to interact with cookbooks that are located on the Chef server or the local chef-repo.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<div class="section" id="bulk-delete">
-<h2>bulk delete<a class="headerlink" href="#bulk-delete" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">bulk</span> <span class="pre">delete</span></tt> argument is used to delete cookbook files that match a pattern defined by a regular expression. The regular expression must be within quotes and not be surrounded by forward slashes (/).</p>
-<div class="section" id="syntax">
-<h3>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook bulk delete REGEX <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h3>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-p</span></tt>, <tt class="docutils literal"><span class="pre">--purge</span></tt></dt>
-<dd>Use to entirely remove a cookbook (or cookbook version) from the Chef server. This action should be used carefully because only one copy of any single file is stored on the Chef server. Consequently, purging a cookbook will disable any other cookbook that references one or more files from a cookbook that has been purged.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h3>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Bulk delete many cookbooks</strong></p>
-<p>Use a regular expression to define the pattern used to bulk delete cookbooks:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook bulk delete <span class="s2">&quot;^[0-9]{3}$&quot;</span> -p
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="create">
-<h2>create<a class="headerlink" href="#create" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">create</span></tt> argument is used to create a new cookbook directory on the local machine, including the following directories and files:</p>
-<blockquote>
-<div><ul class="simple">
-<li>cookbook/attributes</li>
-<li>cookbook/CHANGELOG.md</li>
-<li>cookbook/definitions</li>
-<li>cookbook/files/default</li>
-<li>cookbook/libraries</li>
-<li>cookbook/metadata.rb</li>
-<li>cookbook/providers</li>
-<li>cookbook/README.md (or .rdoc)</li>
-<li>cookbook/recipes/default.rb</li>
-<li>cookbook/resources</li>
-<li>cookbook/templates/default</li>
-</ul>
-</div></blockquote>
-<p>After the cookbook is created, it can be uploaded to the Chef server using the <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">upload</span></tt> argument.</p>
-<div class="section" id="id1">
-<h3>Syntax<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook create COOKBOOK_NAME <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id2">
-<h3>Options<a class="headerlink" href="#id2" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-C</span> <span class="pre">COPYRIGHT_HOLDER</span></tt>, <tt class="docutils literal"><span class="pre">--copyright</span> <span class="pre">COPYRIGHT_HOLDER</span></tt></dt>
-<dd>The name of the copyright holder. This option will place a copyright notice that contains the name of the copyright holder in each of the pre-created files. If this option is not specified, a copyright name of &#8220;your_company_name&#8221; will be used instead; it can be easily modified later.</dd>
-<dt><tt class="docutils literal"><span class="pre">-I</span> <span class="pre">LICENSE</span></tt>, <tt class="docutils literal"><span class="pre">--license</span> <span class="pre">LICENSE</span></tt></dt>
-<dd>The type of license under which a cookbook is distributed: <tt class="docutils literal"><span class="pre">apachev2</span></tt>, <tt class="docutils literal"><span class="pre">gplv2</span></tt>, <tt class="docutils literal"><span class="pre">gplv3</span></tt>, <tt class="docutils literal"><span class="pre">mit</span></tt>, or <tt class="docutils literal"><span class="pre">none</span></tt> (default). This option will place the appropriate license notice in the pre-created files: <tt class="docutils literal"><span class="pre">Apache</span> <span class="pre">v2.0</span></tt> (for <tt class="docutils literal"><span class="pre">apachev2</span></tt>), <tt class="docutils literal"><span class="pre">GPL</span> <span class="pre">v2</span></tt> (for <tt class="docutils literal"><span class="pre">gplv2</span></tt>), <tt class="docutils literal"><span class="pre">GPL</span> <span class="pre">v3</span></tt> (for <tt class="docutils literal"><span class="pre">gplv3</span></tt>), <tt class="docutils literal"><span class="pre">MIT</span></tt> (for <tt class="docutils literal"><span class="pre">mit</span></tt>), or <tt class="docutils literal"><span class="pre">license</span> <span class="pre">'Proprietary</span> <span class="pre">-</span> <span class="pre">All</span> <span class="pre">Rights</span> <span class="pre">Reserved</span></tt> (for <tt class="docutils literal"><span class="pre">none</span></tt>). Be aware of the licenses for files inside of a cookbook and be sure to follow any restrictions they describe.</dd>
-<dt><tt class="docutils literal"><span class="pre">-m</span> <span class="pre">EMAIL</span></tt>, <tt class="docutils literal"><span class="pre">--email</span> <span class="pre">EMAIL</span></tt></dt>
-<dd>The email address for the individual who maintains the cookbook. This option will place an email address in each of the pre-created files. If this option is not specified, an email name of &#8220;your_email&#8221; will be used instead; it can be easily modified later.</dd>
-<dt><tt class="docutils literal"><span class="pre">-o</span> <span class="pre">PATH</span></tt>, <tt class="docutils literal"><span class="pre">--cookbook-path</span> <span class="pre">PATH</span></tt></dt>
-<dd>The directory in which cookbooks are created. This can be a colon-separated path.</dd>
-<dt><tt class="docutils literal"><span class="pre">-r</span> <span class="pre">FORMAT</span></tt>, <tt class="docutils literal"><span class="pre">--readme-format</span> <span class="pre">FORMAT</span></tt></dt>
-<dd>The document format of the readme file: <tt class="docutils literal"><span class="pre">md</span></tt> (markdown) and <tt class="docutils literal"><span class="pre">rdoc</span></tt> (Ruby docs).</dd>
-</dl>
-</div>
-<div class="section" id="id3">
-<h3>Examples<a class="headerlink" href="#id3" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Create a cookbook</strong></p>
-<p>To create a cookbook named &#8220;my_cookbook&#8221; with copyright, email, license, and readme format options specified, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook create my_cookbook -C <span class="s2">&quot;My Name&quot;</span> -m <span class="s2">&quot;my@email.com&quot;</span> -I apachev2 -r md
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>** Creating cookbook my_cookbook
-** Creating README <span class="k">for </span>cookbook: my_cookbook
-** Creating metadata <span class="k">for </span>cookbook: my_cookbook
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="delete">
-<h2>delete<a class="headerlink" href="#delete" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">delete</span></tt> argument is used to delete a specified cookbook or cookbook version on the Chef server (and not locally).</p>
-<div class="section" id="id4">
-<h3>Syntax<a class="headerlink" href="#id4" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook delete COOKBOOK_NAME <span class="o">[</span>COOKBOOK_VERSION<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id5">
-<h3>Options<a class="headerlink" href="#id5" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span></tt>, <tt class="docutils literal"><span class="pre">--all</span></tt></dt>
-<dd>Use to delete all cookbooks (and cookbook versions).</dd>
-<dt><tt class="docutils literal"><span class="pre">COOKBOOK_VERSION</span></tt></dt>
-<dd>The version of a cookbook to be deleted. If a cookbook has only one version, this option does not need to be specified. If a cookbook has more than one version and this option is not specified, knife will prompt for a version.</dd>
-<dt><tt class="docutils literal"><span class="pre">-p</span></tt>, <tt class="docutils literal"><span class="pre">--purge</span></tt></dt>
-<dd>Use to entirely remove a cookbook (or cookbook version) from the Chef server. This action should be used carefully because only one copy of any single file is stored on the Chef server. Consequently, purging a cookbook will disable any other cookbook that references one or more files from a cookbook that has been purged.</dd>
-</dl>
-</div>
-<div class="section" id="id6">
-<h3>Examples<a class="headerlink" href="#id6" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Delete a cookbook</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook delete cookbook_name version
-</pre></div>
-</div>
-<p>For example:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook delete smartmon 0.8
-</pre></div>
-</div>
-<p>Type <tt class="docutils literal"><span class="pre">Y</span></tt> to confirm a deletion.</p>
-</div>
-</div>
-<div class="section" id="download">
-<h2>download<a class="headerlink" href="#download" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">download</span></tt> argument is used to download a cookbook from the Chef server to the current working directory.</p>
-<div class="section" id="id7">
-<h3>Syntax<a class="headerlink" href="#id7" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook download COOKBOOK_NAME <span class="o">[</span>COOKBOOK_VERSION<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id8">
-<h3>Options<a class="headerlink" href="#id8" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-d</span> <span class="pre">DOWNLOAD_DIRECTORY</span></tt>, <tt class="docutils literal"><span class="pre">--dir</span> <span class="pre">DOWNLOAD_DIRECTORY</span></tt></dt>
-<dd>The directory in which cookbooks are located.</dd>
-<dt><tt class="docutils literal"><span class="pre">-f</span></tt>, <tt class="docutils literal"><span class="pre">--force</span></tt></dt>
-<dd>Use to overwrite an existing directory.</dd>
-<dt><tt class="docutils literal"><span class="pre">-N</span></tt>, <tt class="docutils literal"><span class="pre">--latest</span></tt></dt>
-<dd>Use to download the most recent version of a cookbook.</dd>
-</dl>
-</div>
-<div class="section" id="id9">
-<h3>Examples<a class="headerlink" href="#id9" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Download a cookbook</strong></p>
-<p>To download a cookbook named &#8220;smartmon&#8221;, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook download smartmon
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="list">
-<h2>list<a class="headerlink" href="#list" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">list</span></tt> argument is used to view a list of cookbooks that are currently available on the Chef server. The list will contain only the most recent version for each cookbook by default.</p>
-<div class="section" id="id10">
-<h3>Syntax<a class="headerlink" href="#id10" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook list <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id11">
-<h3>Options<a class="headerlink" href="#id11" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span></tt>, <tt class="docutils literal"><span class="pre">--all</span></tt></dt>
-<dd>Use to return all available versions for every cookbook.</dd>
-<dt><tt class="docutils literal"><span class="pre">-w</span></tt>, <tt class="docutils literal"><span class="pre">--with-uri</span></tt></dt>
-<dd>Use to show the corresponding URIs.</dd>
-</dl>
-</div>
-<div class="section" id="id12">
-<h3>Examples<a class="headerlink" href="#id12" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>View a list of cookbooks</strong></p>
-<p>To view a list of cookbooks:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook list
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="metadata">
-<h2>metadata<a class="headerlink" href="#metadata" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">metadata</span></tt> argument is used to generate the metadata for one or more cookbooks.</p>
-<div class="section" id="id13">
-<h3>Syntax<a class="headerlink" href="#id13" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook metadata <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id14">
-<h3>Options<a class="headerlink" href="#id14" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span></tt>, <tt class="docutils literal"><span class="pre">--all</span></tt></dt>
-<dd>Use to generate metadata for all cookbooks.</dd>
-<dt><tt class="docutils literal"><span class="pre">-o</span> <span class="pre">PATH:PATH</span></tt>, <tt class="docutils literal"><span class="pre">--cookbook-path</span> <span class="pre">PATH:PATH</span></tt></dt>
-<dd>The directory in which cookbooks are created. This can be a colon-separated path.</dd>
-</dl>
-</div>
-<div class="section" id="id15">
-<h3>Examples<a class="headerlink" href="#id15" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Generate metadata</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook metadata -a
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="metadata-from-file">
-<h2>metadata from file<a class="headerlink" href="#metadata-from-file" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">metadata</span> <span class="pre">from</span> <span class="pre">file</span></tt> argument is used to load the metadata for a cookbook from a file.</p>
-<div class="section" id="id16">
-<h3>Syntax<a class="headerlink" href="#id16" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook metadata from file FILE
-</pre></div>
-</div>
-</div>
-<div class="section" id="id17">
-<h3>Options<a class="headerlink" href="#id17" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id18">
-<h3>Examples<a class="headerlink" href="#id18" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>View metadata</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook metadata from file /path/to/file
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="show">
-<h2>show<a class="headerlink" href="#show" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">show</span></tt> argument is used to view information about a cookbook, parts of a cookbook (attributes, definitions, files, libraries, providers, recipes, resources, and templates), or a file that is associated with a cookbook (including attributes such as checksum or specificity).</p>
-<div class="section" id="id19">
-<h3>Syntax<a class="headerlink" href="#id19" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook show COOKBOOK_NAME <span class="o">[</span>COOKBOOK_VERSION<span class="o">]</span> <span class="o">[</span>PART...<span class="o">]</span> <span class="o">[</span>FILE_NAME<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id20">
-<h3>Options<a class="headerlink" href="#id20" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">COOKBOOK_VERSION</span></tt></dt>
-<dd>The version of a cookbook to be shown. If a cookbook has only one version, this option does not need to be specified. If a cookbook has more than one version and this option is not specified, a list of cookbook versions will be returned.</dd>
-<dt><tt class="docutils literal"><span class="pre">-f</span> <span class="pre">FQDN</span></tt>, <tt class="docutils literal"><span class="pre">--fqdn</span> <span class="pre">FQDN</span></tt></dt>
-<dd>The FQDN of the host.</dd>
-<dt><tt class="docutils literal"><span class="pre">FILE_NAME</span></tt></dt>
-<dd>The name of a file that is associated with a cookbook.</dd>
-<dt><tt class="docutils literal"><span class="pre">-p</span> <span class="pre">PLATFORM</span></tt>, <tt class="docutils literal"><span class="pre">--platform</span> <span class="pre">PLATFORM</span></tt></dt>
-<dd>The platform for which a cookbook is designed.</dd>
-<dt><tt class="docutils literal"><span class="pre">PART</span></tt></dt>
-<dd>The part of the cookbook to show: <tt class="docutils literal"><span class="pre">attributes</span></tt>, <tt class="docutils literal"><span class="pre">definitions</span></tt>, <tt class="docutils literal"><span class="pre">files</span></tt>, <tt class="docutils literal"><span class="pre">libraries</span></tt>, <tt class="docutils literal"><span class="pre">providers</span></tt>, <tt class="docutils literal"><span class="pre">recipes</span></tt>, <tt class="docutils literal"><span class="pre">resources</span></tt>, or <tt class="docutils literal"><span class="pre">templates</span></tt>. More than one part can be specified.</dd>
-<dt><tt class="docutils literal"><span class="pre">-V</span> <span class="pre">PLATFORM_VERSION</span></tt>, <tt class="docutils literal"><span class="pre">--platform-version</span> <span class="pre">PLATFORM_VERSION</span></tt></dt>
-<dd>The version of the platform.</dd>
-<dt><tt class="docutils literal"><span class="pre">-w</span></tt>, <tt class="docutils literal"><span class="pre">--with-uri</span></tt></dt>
-<dd>Use to show the corresponding URIs.</dd>
-</dl>
-</div>
-<div class="section" id="id21">
-<h3>Examples<a class="headerlink" href="#id21" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Show cookbook data</strong></p>
-<p>To get the list of available versions of a cookbook named &#8220;getting-started&#8221;, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook show getting-started
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>getting-started 0.3.0 0.2.0
-</pre></div>
-</div>
-<p><strong>Show cookbook versions</strong></p>
-<p>To show a list of data about a cookbook using the name of the cookbook and the version, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook show getting-started 0.3.0
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>attributes:
- checksum: fa0fc4abf3f6787aeb5c3c5c35de667c
- name: default.rb
- path: attributes/default.rb
- specificity: default
- url: https://somelongurlhere.com
-chef_type: cookbook_version
-cookbook_name: getting-started
-definitions: <span class="o">[]</span>
-files: <span class="o">[]</span>
-frozen?: <span class="nb">false</span>
-json_class: Chef::CookbookVersion
-libraries: <span class="o">[]</span>
-</pre></div>
-</div>
-<p><strong>Show a cookbook version</strong></p>
-<p>To only view data about &#8220;templates&#8221;, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook show getting-started 0.3.0 templates
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>checksum: a29d6f254577b830091f140c3a78b1fe
-name: chef-getting-started.txt.erb
-path: templates/default/chef-getting-started.txt.erb
-specificity: default
-url: https://someurlhere.com
-</pre></div>
-</div>
-<p><strong>Show cookbook data as JSON</strong></p>
-<p>To view information in JSON format, use the <tt class="docutils literal"><span class="pre">-F</span></tt> common option as part of the command like this:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role show devops -F json
-</pre></div>
-</div>
-<p>Other formats available include <tt class="docutils literal"><span class="pre">text</span></tt>, <tt class="docutils literal"><span class="pre">yaml</span></tt>, and <tt class="docutils literal"><span class="pre">pp</span></tt>.</p>
-</div>
-</div>
-<div class="section" id="test">
-<h2>test<a class="headerlink" href="#test" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">test</span></tt> argument is used to test a cookbook for syntax errors. This argument uses Ruby syntax checking to verify every file in a cookbook that ends in .rb and Embedded Ruby (ERB). This argument will respect .chefignore files when determining which cookbooks to test for syntax errors.</p>
-<div class="section" id="id22">
-<h3>Syntax<a class="headerlink" href="#id22" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook <span class="nb">test </span>COOKBOOK_NAME <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id23">
-<h3>Options<a class="headerlink" href="#id23" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span></tt>, <tt class="docutils literal"><span class="pre">--all</span></tt></dt>
-<dd>Use to test all cookbooks.</dd>
-<dt><tt class="docutils literal"><span class="pre">-o</span> <span class="pre">PATH:PATH</span></tt>, <tt class="docutils literal"><span class="pre">--cookbook-path</span> <span class="pre">PATH:PATH</span></tt></dt>
-<dd>The directory in which cookbooks are created. This can be a colon-separated path.</dd>
-</dl>
-</div>
-<div class="section" id="id24">
-<h3>Examples<a class="headerlink" href="#id24" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Test a cookbook</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook <span class="nb">test </span>cookbook_name
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="upload">
-<h2>upload<a class="headerlink" href="#upload" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">upload</span></tt> argument is used to upload one or more cookbooks (and any files that are associated with those cookbooks) from a local repository to the Chef server. Only files that do not already exist on the Chef server will be uploaded.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Use a .chefignore file to prevent the upload of specific files and file types, such as temporary files or files placed in folders by version control systems. The .chefignore file must be located in the root of the cookbook repository and must use rules similar to filename globbing (as defined by the Ruby <tt class="docutils literal"><span class="pre">File.fnmatch</span></tt> syntax).</p>
-</div>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Empty directories are not uploaded to the Chef server. To upload an empty directory, create a &#8220;dot&#8221; file&#8212;e.g. <tt class="docutils literal"><span class="pre">.keep</span></tt>&#8212;in that directory to ensure that the directory itself is not empty.</p>
-</div>
-<div class="section" id="id25">
-<h3>Syntax<a class="headerlink" href="#id25" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook upload <span class="o">[</span>COOKBOOK_NAME...<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id26">
-<h3>Options<a class="headerlink" href="#id26" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span></tt>, <tt class="docutils literal"><span class="pre">--all</span></tt></dt>
-<dd>Use to upload all cookbooks.</dd>
-<dt><tt class="docutils literal"><span class="pre">--concurrency</span></tt></dt>
-<dd>The number of allowed concurrent connections. Default: <tt class="docutils literal"><span class="pre">10</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-d</span></tt>, <tt class="docutils literal"><span class="pre">--include-dependencies</span></tt></dt>
-<dd>Use to ensure that when a cookbook has a dependency on one (or more) cookbooks, those cookbooks will also be uploaded.</dd>
-<dt><tt class="docutils literal"><span class="pre">-E</span> <span class="pre">ENVIRONMENT</span></tt>, <tt class="docutils literal"><span class="pre">--environment</span> <span class="pre">ENVIRONMENT</span></tt></dt>
-<dd>Use to set the environment version dependency to the cookbook version being uploaded.</dd>
-<dt><tt class="docutils literal"><span class="pre">--force</span></tt></dt>
-<dd>Use to update a cookbook even if the <tt class="docutils literal"><span class="pre">--freeze</span></tt> flag has been set.</dd>
-<dt><tt class="docutils literal"><span class="pre">--freeze</span></tt></dt>
-<dd>Use to require changes to a cookbook be included as a new version. Only the <tt class="docutils literal"><span class="pre">--force</span></tt> option can override this setting.</dd>
-<dt><tt class="docutils literal"><span class="pre">-o</span> <span class="pre">PATH:PATH</span></tt>, <tt class="docutils literal"><span class="pre">--cookbook-path</span> <span class="pre">PATH:PATH</span></tt></dt>
-<dd>The directory in which cookbooks are created. This can be a colon-separated path.</dd>
-</dl>
-</div>
-<div class="section" id="id27">
-<h3>Examples<a class="headerlink" href="#id27" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Upload a cookbook</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook upload cookbook_name
-</pre></div>
-</div>
-<p><strong>Freeze a cookbook</strong></p>
-<p>To upload a cookbook, and then prevent other users from being able to make changes to it, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook upload redis --freeze
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>Uploading redis...
-Upload completed
-</pre></div>
-</div>
-<p>If a cookbook is frozen and the <tt class="docutils literal"><span class="pre">--force</span></tt> option is not specified, knife will return an error message similar to the following:</p>
-<div class="highlight-bash"><div class="highlight"><pre>Uploading redis...
-ERROR: Version 0.1.6 of cookbook redis is frozen. Use --force to override.
-</pre></div>
-</div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_cookbook_site.html b/distro/common/html/knife_cookbook_site.html
deleted file mode 100644
index 60efafa7e1..0000000000
--- a/distro/common/html/knife_cookbook_site.html
+++ /dev/null
@@ -1,375 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife cookbook site &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-cookbook-site">
-<h1>knife cookbook site<a class="headerlink" href="#knife-cookbook-site" title="Permalink to this headline">¶</a></h1>
-<p>The Cookbooks Site API is used to provide access to the cookbooks community hosted at <a class="reference external" href="https://supermarket.getchef.com/cookbooks">https://supermarket.getchef.com/cookbooks</a>. All of the cookbooks in the community are accessible through a RESTful API located at <a class="reference external" href="https://supermarket.getchef.com/api/v1/cookbooks">https://supermarket.getchef.com/api/v1/cookbooks</a> by using any of the supported endpoints. In most cases, using knife and the <strong>knife cookbook site</strong> sub-command (and any of its arguments) is the recommended method of interacting with these cookbooks, but in some cases, using the Cookbooks Site API directly may make sense.</p>
-<p>The <strong>knife cookbook site</strong> subcommand is used to interact with cookbooks that are located at <a class="reference external" href="https://supermarket.getchef.com/cookbooks">https://supermarket.getchef.com/cookbooks</a>. A user account is required for any community actions that write data to this site. The following arguments do not require a user account: <tt class="docutils literal"><span class="pre">download</span></tt>, <tt class="docutils literal"><span class="pre">search</span></tt>, <tt class="docutils literal"><span class="pre">install</span></tt>, and <tt class="docutils literal"><span class="pre">list</span></tt>.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<div class="section" id="download">
-<h2>download<a class="headerlink" href="#download" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">download</span></tt> argument is used to download a cookbook from the community website. A cookbook will be downloaded as a tar.gz archive and placed in the current working directory. If a cookbook (or cookbook version) has been deprecated and the <tt class="docutils literal"><span class="pre">--force</span></tt> option is not used, knife will alert the user that the cookbook is deprecated and then will provide the name of the most recent non-deprecated version of that cookbook.</p>
-<div class="section" id="syntax">
-<h3>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook site download COOKBOOK_NAME <span class="o">[</span>COOKBOOK_VERSION<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h3>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">COOKBOOK_VERSION</span></tt></dt>
-<dd>The version of a cookbook to be downloaded. If a cookbook has only one version, this option does not need to be specified. If a cookbook has more than one version and this option is not specified, the most recent version of the cookbook will be downloaded.</dd>
-<dt><tt class="docutils literal"><span class="pre">-f</span> <span class="pre">FILE</span></tt>, <tt class="docutils literal"><span class="pre">--file</span> <span class="pre">FILE</span></tt></dt>
-<dd>The file to which a cookbook download is written.</dd>
-<dt><tt class="docutils literal"><span class="pre">--force</span></tt></dt>
-<dd>Use to overwrite an existing directory.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h3>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Download a cookbook</strong></p>
-<p>To download the cookbook <tt class="docutils literal"><span class="pre">getting-started</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook site download getting-started
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>Downloading getting-started from the cookbooks site at version 0.3.0 to
- /Users/sdanna/opscodesupport/getting-started-0.3.0.tar.gz
-Cookbook saved: /Users/sdanna/opscodesupport/getting-started-0.3.0.tar.gz
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="install">
-<h2>install<a class="headerlink" href="#install" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">install</span></tt> argument is used to install a cookbook that has been downloaded from the community site to a local git repository . This action uses the git version control system in conjunction with the <a class="reference external" href="https://supermarket.getchef.com/cookbooks">https://supermarket.getchef.com/cookbooks</a> site to install community-contributed cookbooks to the local chef-repo. Using this argument does the following:</p>
-<blockquote>
-<div><ol class="arabic simple">
-<li>A new &#8220;pristine copy&#8221; branch is created in git for tracking the upstream.</li>
-<li>All existing versions of a cookbook are removed from the branch.</li>
-<li>The cookbook is downloaded from <a class="reference external" href="https://supermarket.getchef.com/cookbooks">https://supermarket.getchef.com/cookbooks</a> in the tar.gz format.</li>
-<li>The downloaded cookbook is untarred and its contents are committed to git and a tag is created.</li>
-<li>The &#8220;pristine copy&#8221; branch is merged into the master branch.</li>
-</ol>
-</div></blockquote>
-<p>This process allows the upstream cookbook in the master branch to be modified while letting git maintain changes as a separate patch. When an updated upstream version becomes available, those changes can be merged while maintaining any local modifications.</p>
-<div class="section" id="id1">
-<h3>Syntax<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook site install COOKBOOK_NAME <span class="o">[</span>COOKBOOK_VERSION<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id2">
-<h3>Options<a class="headerlink" href="#id2" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-b</span></tt>, <tt class="docutils literal"><span class="pre">--use-current-branch</span></tt></dt>
-<dd>Use to ensure that the current branch is used.</dd>
-<dt><tt class="docutils literal"><span class="pre">-B</span> <span class="pre">BRANCH</span></tt>, <tt class="docutils literal"><span class="pre">--branch</span> <span class="pre">BRANCH</span></tt></dt>
-<dd>The name of the default branch. This will default to the master branch.</dd>
-<dt><tt class="docutils literal"><span class="pre">COOKBOOK_VERSION</span></tt></dt>
-<dd>The version of the cookbook to be installed. If a version is not specified, the most recent version of the cookbook will be installed.</dd>
-<dt><tt class="docutils literal"><span class="pre">-D</span></tt>, <tt class="docutils literal"><span class="pre">--skip-dependencies</span></tt></dt>
-<dd>Use to ensure that all cookbooks to which the installed cookbook has a dependency will not be installed.</dd>
-<dt><tt class="docutils literal"><span class="pre">-o</span> <span class="pre">PATH:PATH</span></tt>, <tt class="docutils literal"><span class="pre">--cookbook-path</span> <span class="pre">PATH:PATH</span></tt></dt>
-<dd>The directory in which cookbooks are created. This can be a colon-separated path.</dd>
-</dl>
-</div>
-<div class="section" id="id3">
-<h3>Examples<a class="headerlink" href="#id3" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Install a cookbook</strong></p>
-<p>To install the cookbook <tt class="docutils literal"><span class="pre">getting-started</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook site install getting-started
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>Installing getting-started to /Users/sdanna/opscodesupport/.chef/../cookbooks
-Checking out the master branch.
-Creating pristine copy branch chef-vendor-getting-started
-Downloading getting-started from the cookbooks site at version 0.3.0 to
- /Users/sdanna/opscodesupport/.chef/../cookbooks/getting-started.tar.gz
-Cookbook saved: /Users/sdanna/opscodesupport/.chef/../cookbooks/getting-started.tar.gz
-Removing pre-existing version.
-Uncompressing getting-started version /Users/sdanna/opscodesupport/.chef/../cookbooks.
-removing downloaded tarball
-1 files updated, committing changes
-Creating tag cookbook-site-imported-getting-started-0.3.0
-Checking out the master branch.
-Updating 4d44b5b..b4c32f2
-Fast-forward
- cookbooks/getting-started/README.rdoc | 4 +++
- cookbooks/getting-started/attributes/default.rb | 1 +
- cookbooks/getting-started/metadata.json | 29 ++++++++++++++++++++
- cookbooks/getting-started/metadata.rb | 6 ++++
- cookbooks/getting-started/recipes/default.rb | 23 +++++++++++++++
- .../templates/default/chef-getting-started.txt.erb | 5 +++
- 6 files changed, 68 insertions<span class="o">(</span>+<span class="o">)</span>, 0 deletions<span class="o">(</span>-<span class="o">)</span>
- create mode 100644 cookbooks/getting-started/README.rdoc
- create mode 100644 cookbooks/getting-started/attributes/default.rb
- create mode 100644 cookbooks/getting-started/metadata.json
- create mode 100644 cookbooks/getting-started/metadata.rb
- create mode 100644 cookbooks/getting-started/recipes/default.rb
- create mode 100644 cookbooks/getting-started/templates/default/chef-getting-started.txt.erb
-Cookbook getting-started version 0.3.0 successfully installed
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="list">
-<h2>list<a class="headerlink" href="#list" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">list</span></tt> argument is used to view a list of cookbooks that are currently available at <a class="reference external" href="https://supermarket.getchef.com/cookbooks">https://supermarket.getchef.com/cookbooks</a>.</p>
-<div class="section" id="id4">
-<h3>Syntax<a class="headerlink" href="#id4" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook site list
-</pre></div>
-</div>
-</div>
-<div class="section" id="id5">
-<h3>Options<a class="headerlink" href="#id5" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-w</span></tt>, <tt class="docutils literal"><span class="pre">--with-uri</span></tt></dt>
-<dd>Use to show the corresponding URIs.</dd>
-</dl>
-</div>
-<div class="section" id="id6">
-<h3>Examples<a class="headerlink" href="#id6" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>View a list of cookbooks</strong></p>
-<p>To view a list of cookbooks at <a class="reference external" href="https://supermarket.getchef.com/cookbooks">https://supermarket.getchef.com/cookbooks</a> server, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook site list
-</pre></div>
-</div>
-<p>to return:</p>
-<div class="highlight-python"><div class="highlight"><pre>1password homesick rabbitmq
-7-zip hostname rabbitmq-management
-AmazonEC2Tag hosts rabbitmq_chef
-R hosts-awareness rackspaceknife
-accounts htop radiant
-ack-grep hudson rails
-activemq icinga rails_enterprise
-ad id3lib redis-package
-ad-likewise iftop redis2
-ant iis redmine
-[...truncated...]
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="search">
-<h2>search<a class="headerlink" href="#search" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">search</span></tt> argument is used to search for a cookbook at <a class="reference external" href="https://supermarket.getchef.com/cookbooks">https://supermarket.getchef.com/cookbooks</a>. A search query is used to return a list of cookbooks at <a class="reference external" href="https://supermarket.getchef.com/cookbooks">https://supermarket.getchef.com/cookbooks</a> and uses the same syntax as the <strong>knife search</strong> sub-command.</p>
-<div class="section" id="id7">
-<h3>Syntax<a class="headerlink" href="#id7" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook site search SEARCH_QUERY <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id8">
-<h3>Options<a class="headerlink" href="#id8" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id9">
-<h3>Examples<a class="headerlink" href="#id9" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Search for cookbooks</strong></p>
-<p>To search for all of the cookbooks that can be used with Apache, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook site search apache*
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>apache2:
- cookbook: https://supermarket.chef.io/api/v1/cookbooks/apache2
- cookbook_description: Installs and configures apache2 using Debian symlinks with helper definitions
- cookbook_maintainer: opscode
- cookbook_name: apache2
-instiki:
- cookbook: https://supermarket.chef.io/api/v1/cookbooks/instiki
- cookbook_description: Installs instiki, a Ruby on Rails wiki server under passenger+Apache2.
- cookbook_maintainer: jtimberman
- cookbook_name: instiki
-kickstart:
- cookbook: https://supermarket.chef.io/api/v1/cookbooks/kickstart
- cookbook_description: Creates apache2 vhost and serves a kickstart file.
- cookbook_maintainer: opscode
- cookbook_name: kickstart
-<span class="o">[</span>...truncated...<span class="o">]</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="share">
-<h2>share<a class="headerlink" href="#share" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">share</span></tt> argument is used to add a cookbook to <a class="reference external" href="https://supermarket.getchef.com/cookbooks">https://supermarket.getchef.com/cookbooks</a>. This action will require a user account and a certificate for <a class="reference external" href="https://supermarket.getchef.com">https://supermarket.getchef.com</a>. By default, knife will use the user name and API key that is identified in the configuration file used during the upload; otherwise these values must be specified on the command line or in an alternate configuration file. If a cookbook already exists on <a class="reference external" href="https://supermarket.getchef.com/cookbooks">https://supermarket.getchef.com/cookbooks</a>, then only an owner or maintainer of that cookbook can make updates.</p>
-<div class="section" id="id10">
-<h3>Syntax<a class="headerlink" href="#id10" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook site share COOKBOOK_NAME CATEGORY <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id11">
-<h3>Options<a class="headerlink" href="#id11" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">CATEGORY</span></tt></dt>
-<dd>The cookbook category: <tt class="docutils literal"><span class="pre">&quot;Databases&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;Web</span> <span class="pre">Servers&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;Process</span> <span class="pre">Management&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;Monitoring</span> <span class="pre">&amp;</span> <span class="pre">Trending&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;Programming</span> <span class="pre">Languages&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;Package</span> <span class="pre">Management&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;Applications&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;Networking&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;Operating</span> <span class="pre">Systems</span> <span class="pre">&amp;</span> <span class="pre">Virtualization&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;Utilities&quot;</span></tt>, or <tt class="docutils literal"><span class="pre">&quot;Other&quot;</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-n</span></tt>, <tt class="docutils literal"><span class="pre">--dry-run</span></tt></dt>
-<dd>Use to take no action and only print out results. Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-o</span> <span class="pre">PATH:PATH</span></tt>, <tt class="docutils literal"><span class="pre">--cookbook-path</span> <span class="pre">PATH:PATH</span></tt></dt>
-<dd>The directory in which cookbooks are created. This can be a colon-separated path.</dd>
-</dl>
-</div>
-<div class="section" id="id12">
-<h3>Examples<a class="headerlink" href="#id12" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Share a cookbook</strong></p>
-<p>To share a cookbook named <tt class="docutils literal"><span class="pre">apache2</span></tt>:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook site share <span class="s2">&quot;apache2&quot;</span> <span class="s2">&quot;Web Servers&quot;</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="show">
-<h2>show<a class="headerlink" href="#show" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">show</span></tt> argument is used to view information about a cookbook on <a class="reference external" href="https://supermarket.getchef.com/cookbooks">https://supermarket.getchef.com/cookbooks</a>.</p>
-<div class="section" id="id13">
-<h3>Syntax<a class="headerlink" href="#id13" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook site show COOKBOOK_NAME <span class="o">[</span>COOKBOOK_VERSION<span class="o">]</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id14">
-<h3>Options<a class="headerlink" href="#id14" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">COOKBOOK_VERSION</span></tt></dt>
-<dd>The version of a cookbook to be shown. If a cookbook has only one version, this option does not need to be specified. If a cookbook has more than one version and this option is not specified, a list of cookbook versions will be returned.</dd>
-</dl>
-</div>
-<div class="section" id="id15">
-<h3>Examples<a class="headerlink" href="#id15" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Show cookbook data</strong></p>
-<p>To show the details for a cookbook named <tt class="docutils literal"><span class="pre">haproxy</span></tt>:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook site show haproxy
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>average_rating:
-category: Networking
-created_at: 2009-10-25T23:51:07Z
-description: Installs and configures haproxy
-external_url:
-latest_version: https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_3
-maintainer: opscode
-name: haproxy
-updated_at: 2011-06-30T21:53:25Z
-versions:
- https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_3
- https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_2
- https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_1
- https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_0
- https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/0_8_1
- https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/0_8_0
- https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/0_7_0
-</pre></div>
-</div>
-<p><strong>Show cookbook data as JSON</strong></p>
-<p>To view information in JSON format, use the <tt class="docutils literal"><span class="pre">-F</span></tt> common option as part of the command like this:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role show devops -F json
-</pre></div>
-</div>
-<p>Other formats available include <tt class="docutils literal"><span class="pre">text</span></tt>, <tt class="docutils literal"><span class="pre">yaml</span></tt>, and <tt class="docutils literal"><span class="pre">pp</span></tt>.</p>
-</div>
-</div>
-<div class="section" id="unshare">
-<h2>unshare<a class="headerlink" href="#unshare" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">unshare</span></tt> argument is used to stop the sharing of a cookbook at <a class="reference external" href="https://supermarket.getchef.com/cookbooks">https://supermarket.getchef.com/cookbooks</a>. Only the maintainer of a cookbook may perform this action.</p>
-<div class="section" id="id16">
-<h3>Syntax<a class="headerlink" href="#id16" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook site unshare COOKBOOK_NAME
-</pre></div>
-</div>
-</div>
-<div class="section" id="id17">
-<h3>Options<a class="headerlink" href="#id17" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id18">
-<h3>Examples<a class="headerlink" href="#id18" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Unshare a cookbook</strong></p>
-<p>To unshare a cookbook named <tt class="docutils literal"><span class="pre">getting-started</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife cookbook site unshare getting-started
-</pre></div>
-</div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html>
diff --git a/distro/common/html/knife_data_bag.html b/distro/common/html/knife_data_bag.html
deleted file mode 100644
index a7a297be95..0000000000
--- a/distro/common/html/knife_data_bag.html
+++ /dev/null
@@ -1,374 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife data bag &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-data-bag">
-<h1>knife data bag<a class="headerlink" href="#knife-data-bag" title="Permalink to this headline">¶</a></h1>
-<p>A data bag is a global variable that is stored as JSON data and is accessible from a Chef server. A data bag is indexed for searching and can be loaded by a recipe or accessed during a search.</p>
-<p>A data bag item may be encrypted using <a class="reference external" href="https://en.wikipedia.org/wiki/Symmetric-key_algorithm">shared secret encryption</a>. This allows each data bag item to store confidential information (such as a database password) or to be managed in a source control system (without plain-text data appearing in revision history). Each data bag item may be encrypted individually; if a data bag contains multiple encrypted data bag items, these data bag items are not required to share the same encryption keys.</p>
-<p>The <strong>knife data bag</strong> subcommand is used to manage arbitrary stores of globally available JSON data.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<div class="section" id="create">
-<h2>create<a class="headerlink" href="#create" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">create</span></tt> argument is used to add a data bag to the Chef server.</p>
-<div class="section" id="syntax">
-<h3>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag create DATA_BAG_NAME <span class="o">[</span>DATA_BAG_ITEM<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h3>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">DATA_BAG_ITEM</span></tt></dt>
-<dd>The name of a specific item within a data bag.</dd>
-<dt><tt class="docutils literal"><span class="pre">--secret</span> <span class="pre">SECRET</span></tt></dt>
-<dd>The encryption key that is used for values contained within a data bag item. If <tt class="docutils literal"><span class="pre">secret</span></tt> is not specified, the chef-client will look for a secret at the path specified by the <tt class="docutils literal"><span class="pre">encrypted_data_bag_secret</span></tt> setting in the client.rb file.</dd>
-<dt><tt class="docutils literal"><span class="pre">--secret-file</span> <span class="pre">FILE</span></tt></dt>
-<dd>The path to the file that contains the encryption key.</dd>
-</dl>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">For encrypted data bag items, use <em>either</em> <tt class="docutils literal"><span class="pre">--secret</span></tt> or <tt class="docutils literal"><span class="pre">--secret-file</span></tt>, not both.</p>
-</div>
-</div>
-<div class="section" id="examples">
-<h3>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Create a data bag</strong></p>
-<p>To create a data bag named &#8220;admins&#8221;, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag create admins
-</pre></div>
-</div>
-<p>to return:</p>
-<div class="highlight-bash"><div class="highlight"><pre>Created data_bag<span class="o">[</span>admins<span class="o">]</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="delete">
-<h2>delete<a class="headerlink" href="#delete" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">delete</span></tt> argument is used to delete a data bag or a data bag item from a Chef server.</p>
-<div class="section" id="id1">
-<h3>Syntax<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag delete DATA_BAG_NAME <span class="o">[</span>DATA_BAG_ITEM<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id2">
-<h3>Options<a class="headerlink" href="#id2" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">DATA_BAG_ITEM</span></tt></dt>
-<dd>The name of a specific item within a data bag.</dd>
-</dl>
-</div>
-<div class="section" id="id3">
-<h3>Examples<a class="headerlink" href="#id3" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Delete a data bag</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag delete data_bag_name
-</pre></div>
-</div>
-<p><strong>Delete a data bag item</strong></p>
-<p>To delete an item named &#8220;charlie&#8221;, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag delete admins charlie
-</pre></div>
-</div>
-<p>Type <tt class="docutils literal"><span class="pre">Y</span></tt> to confirm a deletion.</p>
-</div>
-</div>
-<div class="section" id="edit">
-<h2>edit<a class="headerlink" href="#edit" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">edit</span></tt> argument is used to edit the data contained in a data bag. If encryption is being used, the data bag will be decrypted, the data will be made available in the $EDITOR, and then encrypted again before saving it to the Chef server.</p>
-<div class="section" id="id4">
-<h3>Syntax<a class="headerlink" href="#id4" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag edit DATA_BAG_NAME <span class="o">[</span>DATA_BAG_ITEM<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id5">
-<h3>Options<a class="headerlink" href="#id5" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">DATA_BAG_ITEM</span></tt></dt>
-<dd>The name of a specific item within a data bag.</dd>
-<dt><tt class="docutils literal"><span class="pre">--secret</span> <span class="pre">SECRET</span></tt></dt>
-<dd>The encryption key that is used for values contained within a data bag item. If <tt class="docutils literal"><span class="pre">secret</span></tt> is not specified, the chef-client will look for a secret at the path specified by the <tt class="docutils literal"><span class="pre">encrypted_data_bag_secret</span></tt> setting in the client.rb file.</dd>
-<dt><tt class="docutils literal"><span class="pre">--secret-file</span> <span class="pre">FILE</span></tt></dt>
-<dd>The path to the file that contains the encryption key.</dd>
-</dl>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">For encrypted data bag items, use <em>either</em> <tt class="docutils literal"><span class="pre">--secret</span></tt> or <tt class="docutils literal"><span class="pre">--secret-file</span></tt>, not both.</p>
-</div>
-</div>
-<div class="section" id="id6">
-<h3>Examples<a class="headerlink" href="#id6" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Edit a data bag</strong></p>
-<p>To edit the contents of a data bag, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag edit dogs tibetanspaniel
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">dogs</span></tt> is the name of the data bag and <tt class="docutils literal"><span class="pre">tibetanspaniel</span></tt> is the name of the data bag item. This will return something similar to the following in the knife editor:</p>
-<div class="highlight-javascript"><div class="highlight"><pre><span class="p">{</span>
- <span class="s2">&quot;name&quot;</span><span class="o">:</span><span class="s2">&quot;data_bag_item_dogs_tibetanspaniel&quot;</span><span class="p">,</span>
- <span class="s2">&quot;json_class&quot;</span><span class="o">:</span><span class="s2">&quot;Chef::DataBagItem&quot;</span><span class="p">,</span>
- <span class="s2">&quot;chef_type&quot;</span><span class="o">:</span><span class="s2">&quot;data_bag_item&quot;</span><span class="p">,</span>
- <span class="s2">&quot;data_bag&quot;</span><span class="o">:</span><span class="s2">&quot;dogs&quot;</span><span class="p">,</span>
- <span class="s2">&quot;raw_data&quot;</span><span class="o">:</span>
- <span class="p">{</span>
- <span class="s2">&quot;description&quot;</span><span class="o">:</span><span class="s2">&quot;small dog that likes to sit in windows&quot;</span><span class="p">,</span>
- <span class="s2">&quot;id&quot;</span><span class="o">:</span><span class="s2">&quot;tibetanspaniel&quot;</span>
- <span class="p">}</span>
-<span class="p">}</span>
-</pre></div>
-</div>
-<p>Make the necessary changes to the key-value pairs under <tt class="docutils literal"><span class="pre">raw_data</span></tt> and save them.</p>
-<p><strong>Edit a data bag item</strong></p>
-<p>To edit an item named &#8220;charlie&#8221; that is contained in a data bag named &#8220;admins&#8221;, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag edit admins charlie
-</pre></div>
-</div>
-<p>to open the $EDITOR. Once opened, you can update the data before saving it to the Chef server. For example, by changing:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="o">{</span>
- <span class="s2">&quot;id&quot;</span>: <span class="s2">&quot;charlie&quot;</span>
-<span class="o">}</span>
-</pre></div>
-</div>
-<p>to:</p>
-<div class="highlight-javascript"><div class="highlight"><pre><span class="p">{</span>
- <span class="s2">&quot;id&quot;</span><span class="o">:</span> <span class="s2">&quot;charlie&quot;</span><span class="p">,</span>
- <span class="s2">&quot;uid&quot;</span><span class="o">:</span> <span class="mi">1005</span><span class="p">,</span>
- <span class="s2">&quot;gid&quot;</span><span class="o">:</span> <span class="s2">&quot;ops&quot;</span><span class="p">,</span>
- <span class="s2">&quot;shell&quot;</span><span class="o">:</span> <span class="s2">&quot;/bin/zsh&quot;</span><span class="p">,</span>
- <span class="s2">&quot;comment&quot;</span><span class="o">:</span> <span class="s2">&quot;Crazy Charlie&quot;</span>
-<span class="p">}</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="from-file">
-<h2>from file<a class="headerlink" href="#from-file" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">from</span> <span class="pre">file</span></tt> argument is used to:</p>
-<ul class="simple">
-<li>Add a data bag item to a data bag</li>
-<li>Update the contents of an existing data bag item</li>
-</ul>
-<p>The data bag itself must already exist on the Chef server and must be specified as part of the command. The contents of the data bag item are specified using a JSON file. This JSON file may be located at a relative or absolute path; its location must be specified as part of the command. The JSON file that defines the contents of the data bag item must at least contain the name of the data bag item&#8212;<tt class="docutils literal"><span class="pre">&quot;id&quot;:</span> <span class="pre">&quot;name&quot;</span></tt>.</p>
-<div class="admonition warning">
-<p class="first admonition-title">Warning</p>
-<p class="last">A chef-client must be version 11.6 (or higher) when using the <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">data</span> <span class="pre">bag</span> <span class="pre">from</span> <span class="pre">file</span></tt> argument with the Enterprise Chef or Open Source Chef version 11 servers.</p>
-</div>
-<div class="section" id="id7">
-<h3>Syntax<a class="headerlink" href="#id7" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag from file DATA_BAG_NAME_or_PATH
-</pre></div>
-</div>
-</div>
-<div class="section" id="id8">
-<h3>Options<a class="headerlink" href="#id8" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span></tt>, <tt class="docutils literal"><span class="pre">--all</span></tt></dt>
-<dd>Use to upload all data bags found at the specified path.</dd>
-<dt><tt class="docutils literal"><span class="pre">--secret</span> <span class="pre">SECRET</span></tt></dt>
-<dd>The encryption key that is used for values contained within a data bag item. If <tt class="docutils literal"><span class="pre">secret</span></tt> is not specified, the chef-client will look for a secret at the path specified by the <tt class="docutils literal"><span class="pre">encrypted_data_bag_secret</span></tt> setting in the client.rb file.</dd>
-<dt><tt class="docutils literal"><span class="pre">--secret-file</span> <span class="pre">FILE</span></tt></dt>
-<dd>The path to the file that contains the encryption key.</dd>
-</dl>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">For encrypted data bag items, use <em>either</em> <tt class="docutils literal"><span class="pre">--secret</span></tt> or <tt class="docutils literal"><span class="pre">--secret-file</span></tt>, not both.</p>
-</div>
-</div>
-<div class="section" id="id9">
-<h3>Examples<a class="headerlink" href="#id9" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Create a data bag from a file</strong></p>
-<p>To create a data bag on the Chef server from a file:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag from file <span class="s2">&quot;path to JSON file&quot;</span>
-</pre></div>
-</div>
-<p><strong>Create an encrypted data bag from a file</strong></p>
-<p>To create a data bag named &#8220;devops_data&#8221; that contains encrypted data, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag from file devops_data --secret-file <span class="s2">&quot;path to decryption file&quot;</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="list">
-<h2>list<a class="headerlink" href="#list" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">list</span></tt> argument is used to view a list of data bags that are currently available on the Chef server.</p>
-<div class="section" id="id10">
-<h3>Syntax<a class="headerlink" href="#id10" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag list
-</pre></div>
-</div>
-</div>
-<div class="section" id="id11">
-<h3>Options<a class="headerlink" href="#id11" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-w</span></tt>, <tt class="docutils literal"><span class="pre">--with-uri</span></tt></dt>
-<dd>Use to show the corresponding URIs.</dd>
-</dl>
-</div>
-<div class="section" id="id12">
-<h3>Examples<a class="headerlink" href="#id12" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>View a list of data bags</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag list
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="show">
-<h2>show<a class="headerlink" href="#show" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">show</span></tt> argument is used to view the contents of a data bag.</p>
-<div class="section" id="id13">
-<h3>Syntax<a class="headerlink" href="#id13" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag show DATA_BAG_NAME <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id14">
-<h3>Options<a class="headerlink" href="#id14" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">DATA_BAG_ITEM</span></tt></dt>
-<dd>The name of a specific item within a data bag.</dd>
-<dt><tt class="docutils literal"><span class="pre">--secret</span> <span class="pre">SECRET</span></tt></dt>
-<dd>The encryption key that is used for values contained within a data bag item. If <tt class="docutils literal"><span class="pre">secret</span></tt> is not specified, the chef-client will look for a secret at the path specified by the <tt class="docutils literal"><span class="pre">encrypted_data_bag_secret</span></tt> setting in the client.rb file.</dd>
-<dt><tt class="docutils literal"><span class="pre">--secret-file</span> <span class="pre">FILE</span></tt></dt>
-<dd>The path to the file that contains the encryption key.</dd>
-</dl>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">For encrypted data bag items, use <em>either</em> <tt class="docutils literal"><span class="pre">--secret</span></tt> or <tt class="docutils literal"><span class="pre">--secret-file</span></tt>, not both.</p>
-</div>
-</div>
-<div class="section" id="id15">
-<h3>Examples<a class="headerlink" href="#id15" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Show a data bag</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag show admins
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>charlie
-</pre></div>
-</div>
-<p><strong>Show a data bag item</strong></p>
-<p>To show the contents of a specific item within data bag, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag show admins charlie
-</pre></div>
-</div>
-<p>to return:</p>
-<div class="highlight-bash"><div class="highlight"><pre>comment: Crazy Charlie
-gid: ops
-id: charlie
-shell: /bin/zsh
-uid: 1005
-</pre></div>
-</div>
-<p><strong>Show a data bag, encrypted</strong></p>
-<p>To show the contents of a data bag named <tt class="docutils literal"><span class="pre">passwords</span></tt> with an item that contains encrypted data named <tt class="docutils literal"><span class="pre">mysql</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag show passwords mysql
-</pre></div>
-</div>
-<p>to return:</p>
-<div class="highlight-javascript"><div class="highlight"><pre>## sample:
-{
- &quot;id&quot;: &quot;mysql&quot;,
- &quot;pass&quot;: &quot;trywgFA6R70NO28PNhMpGhEvKBZuxouemnbnAUQsUyo=\n&quot;,
- &quot;user&quot;: &quot;e/p+8WJYVHY9fHcEgAAReg==\n&quot;
-}
-</pre></div>
-</div>
-<p><strong>Show a data bag, decrypted</strong></p>
-<p>To show the decrypted contents of the same data bag, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag show --secret-file /path/to/decryption/file passwords mysql
-</pre></div>
-</div>
-<p>to return:</p>
-<div class="highlight-javascript"><div class="highlight"><pre>## sample:
-{
- &quot;id&quot;: &quot;mysql&quot;,
- &quot;pass&quot;: &quot;thesecret123&quot;,
- &quot;user&quot;: &quot;fred&quot;
-}
-</pre></div>
-</div>
-<p><strong>Show a data bag as JSON</strong></p>
-<p>To view information in JSON format, use the <tt class="docutils literal"><span class="pre">-F</span></tt> common option as part of the command like this:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag show admins -F json
-</pre></div>
-</div>
-<p>Other formats available include <tt class="docutils literal"><span class="pre">text</span></tt>, <tt class="docutils literal"><span class="pre">yaml</span></tt>, and <tt class="docutils literal"><span class="pre">pp</span></tt>.</p>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_delete.html b/distro/common/html/knife_delete.html
deleted file mode 100644
index 431ee1e5c1..0000000000
--- a/distro/common/html/knife_delete.html
+++ /dev/null
@@ -1,93 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife delete &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-delete">
-<h1>knife delete<a class="headerlink" href="#knife-delete" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife delete</strong> subcommand is used to delete an object from a Chef server. This subcommand works similar to <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">cookbook</span> <span class="pre">delete</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">data</span> <span class="pre">bag</span> <span class="pre">delete</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">environment</span> <span class="pre">delete</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">node</span> <span class="pre">delete</span></tt>, and <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">role</span> <span class="pre">delete</span></tt>, but with a single verb (and a single action).</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife delete <span class="o">[</span>PATTERN...<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">--both</span></tt></dt>
-<dd>Use to delete both local and remote copies of an object. Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--chef-repo-path</span> <span class="pre">PATH</span></tt></dt>
-<dd>The path to the chef-repo. This setting will override the default path to the chef-repo. Default: same value as specified by <tt class="docutils literal"><span class="pre">chef_repo_path</span></tt> in client.rb.</dd>
-<dt><tt class="docutils literal"><span class="pre">--concurrency</span></tt></dt>
-<dd>The number of allowed concurrent connections. Default: <tt class="docutils literal"><span class="pre">10</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--local</span></tt></dt>
-<dd>Use to delete only the local copy of an object. (A remote copy will not be deleted.) Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-r</span></tt>, <tt class="docutils literal"><span class="pre">--[no-]recurse</span></tt></dt>
-<dd>Use <tt class="docutils literal"><span class="pre">--recurse</span></tt> to delete directories recursively. Default: <tt class="docutils literal"><span class="pre">--no-recurse</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--repo-mode</span> <span class="pre">MODE</span></tt></dt>
-<dd>The layout of the local chef-repo. Possible values: <tt class="docutils literal"><span class="pre">static</span></tt>, <tt class="docutils literal"><span class="pre">everything</span></tt>, or <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>. Use <tt class="docutils literal"><span class="pre">static</span></tt> for just roles, environments, cookbooks, and data bags. By default, <tt class="docutils literal"><span class="pre">everything</span></tt> and <tt class="docutils literal"><span class="pre">hosted_everything</span></tt> are dynamically selected depending on the server type. Default: <tt class="docutils literal"><span class="pre">everything</span></tt> / <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>None.</p>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_deps.html b/distro/common/html/knife_deps.html
deleted file mode 100644
index 29bc2d6583..0000000000
--- a/distro/common/html/knife_deps.html
+++ /dev/null
@@ -1,143 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife deps &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-deps">
-<h1>knife deps<a class="headerlink" href="#knife-deps" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife deps</strong> subcommand is used to identify dependencies for a node, role, or cookbook.</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife deps <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">--chef-repo-path</span> <span class="pre">PATH</span></tt></dt>
-<dd>The path to the chef-repo. This setting will override the default path to the chef-repo. Default: same value as specified by <tt class="docutils literal"><span class="pre">chef_repo_path</span></tt> in client.rb.</dd>
-<dt><tt class="docutils literal"><span class="pre">--concurrency</span></tt></dt>
-<dd>The number of allowed concurrent connections. Default: <tt class="docutils literal"><span class="pre">10</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]recurse</span></tt></dt>
-<dd>Use <tt class="docutils literal"><span class="pre">--recurse</span></tt> to list dependencies recursively. This option can only be used when <tt class="docutils literal"><span class="pre">--tree</span></tt> is set to <tt class="docutils literal"><span class="pre">true</span></tt>. Default: <tt class="docutils literal"><span class="pre">--no-recurse</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--remote</span></tt></dt>
-<dd>Use to determine dependencies from objects located on the Chef server instead of in the local chef-repo. Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--repo-mode</span> <span class="pre">MODE</span></tt></dt>
-<dd>The layout of the local chef-repo. Possible values: <tt class="docutils literal"><span class="pre">static</span></tt>, <tt class="docutils literal"><span class="pre">everything</span></tt>, or <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>. Use <tt class="docutils literal"><span class="pre">static</span></tt> for just roles, environments, cookbooks, and data bags. By default, <tt class="docutils literal"><span class="pre">everything</span></tt> and <tt class="docutils literal"><span class="pre">hosted_everything</span></tt> are dynamically selected depending on the server type. Default: <tt class="docutils literal"><span class="pre">everything</span></tt> / <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--tree</span></tt></dt>
-<dd>Use to show dependencies in a visual tree structure (including duplicates, if they exist). Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Find dependencies for a node</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife deps nodes/node_name.json
-</pre></div>
-</div>
-<p><strong>Find dependencies for a role</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife deps roles/role_name.json
-</pre></div>
-</div>
-<p><strong>Find dependencies for a cookbook</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife deps cookbooks/cookbook_name.json
-</pre></div>
-</div>
-<p><strong>Find dependencies for an environment</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife deps environments/environment_name.json
-</pre></div>
-</div>
-<p><strong>Find dependencies for a combination of nodes, roles, and so on</strong></p>
-<p>To find the dependencies for a combination of nodes, cookbooks, roles, and/or environments:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife deps cookbooks/git.json cookbooks/github.json roles/base.json environments/desert.json nodes/mynode.json
-</pre></div>
-</div>
-<p><strong>Use a wildcard</strong></p>
-<p>A wildcard can be used to return all of the child nodes. For example, all of the environments:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife deps environments/*.json
-</pre></div>
-</div>
-<p><strong>Return as tree</strong></p>
-<p>Use the <tt class="docutils literal"><span class="pre">--tree</span></tt> option to view the results with structure:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife deps roles/webserver.json
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>roles/webserver.json
- roles/base.json
- cookbooks/github
- cookbooks/git
- cookbooks/users
- cookbooks/apache2
-</pre></div>
-</div>
-<p><strong>Pass knife deps output to knife upload</strong></p>
-<p>The output of <strong>knife deps</strong> can be passed to <strong>knife upload</strong>:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife upload <span class="sb">`</span>knife deps nodes/*.json
-</pre></div>
-</div>
-<p><strong>Pass knife deps output to knife xargs</strong></p>
-<p>The output of <strong>knife deps</strong> can be passed to <strong>knife xargs</strong>:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife deps nodes/*.json | xargs knife upload
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_diff.html b/distro/common/html/knife_diff.html
deleted file mode 100644
index 840548bb51..0000000000
--- a/distro/common/html/knife_diff.html
+++ /dev/null
@@ -1,123 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife diff &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-diff">
-<h1>knife diff<a class="headerlink" href="#knife-diff" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife diff</strong> subcommand is used to compare the differences between files and directories on the Chef server and in the chef-repo. For example, to compare files on the Chef server prior to an uploading or downloading files using the <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">download</span></tt> and <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">upload</span></tt> subcommands, or to ensure that certain files in multiple production environments are the same. This subcommand is similar to the <tt class="docutils literal"><span class="pre">git</span> <span class="pre">diff</span></tt> command that can be used to diff what is in the chef-repo with what is synced to a git repository.</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife diff <span class="o">[</span>PATTERN...<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">--chef-repo-path</span> <span class="pre">PATH</span></tt></dt>
-<dd>The path to the chef-repo. This setting will override the default path to the chef-repo. Default: same value as specified by <tt class="docutils literal"><span class="pre">chef_repo_path</span></tt> in client.rb.</dd>
-<dt><tt class="docutils literal"><span class="pre">--cookbook-version</span> <span class="pre">VERSION</span></tt></dt>
-<dd>The version of a cookbook to download.</dd>
-<dt><tt class="docutils literal"><span class="pre">--concurrency</span></tt></dt>
-<dd>The number of allowed concurrent connections. Default: <tt class="docutils literal"><span class="pre">10</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--diff-filter=[(A|D|M|T)...[*]]</span></tt></dt>
-<dd>Use to select only files that have been added (<tt class="docutils literal"><span class="pre">A</span></tt>), deleted (<tt class="docutils literal"><span class="pre">D</span></tt>), modified (<tt class="docutils literal"><span class="pre">M</span></tt>), and/or have had their type changed (<tt class="docutils literal"><span class="pre">T</span></tt>). Any combination of filter characters may be used, including no filter characters. Use <tt class="docutils literal"><span class="pre">*</span></tt> to select all paths if a file matches other criteria in the comparison. Default value: <tt class="docutils literal"><span class="pre">nil</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--name-only</span></tt></dt>
-<dd>Use to show only the names of modified files.</dd>
-<dt><tt class="docutils literal"><span class="pre">--name-status</span></tt></dt>
-<dd>Use to show only the names of files with a status of <tt class="docutils literal"><span class="pre">Added</span></tt>, <tt class="docutils literal"><span class="pre">Deleted</span></tt>, <tt class="docutils literal"><span class="pre">Modified</span></tt>, or <tt class="docutils literal"><span class="pre">Type</span> <span class="pre">Changed</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--no-recurse</span></tt></dt>
-<dd>Use <tt class="docutils literal"><span class="pre">--no-recurse</span></tt> to disable listing a directory recursively. Default: <tt class="docutils literal"><span class="pre">--recurse</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--repo-mode</span> <span class="pre">MODE</span></tt></dt>
-<dd>The layout of the local chef-repo. Possible values: <tt class="docutils literal"><span class="pre">static</span></tt>, <tt class="docutils literal"><span class="pre">everything</span></tt>, or <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>. Use <tt class="docutils literal"><span class="pre">static</span></tt> for just roles, environments, cookbooks, and data bags. By default, <tt class="docutils literal"><span class="pre">everything</span></tt> and <tt class="docutils literal"><span class="pre">hosted_everything</span></tt> are dynamically selected depending on the server type. Default: <tt class="docutils literal"><span class="pre">everything</span></tt> / <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Compare files that contain JSON data</strong></p>
-<p>To compare the <tt class="docutils literal"><span class="pre">base.json</span></tt> role to a <tt class="docutils literal"><span class="pre">webserver.json</span></tt> role, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife diff roles/base.json roles/webserver.json
-</pre></div>
-</div>
-<p><strong>Compare the chef-repo and the server</strong></p>
-<p>To compare the differences between the local chef-repo and the files that are on the Chef server, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife diff
-</pre></div>
-</div>
-<p><strong>Compare, then return results</strong></p>
-<p>To diff a node named <tt class="docutils literal"><span class="pre">node-lb</span></tt> and then only return files that have been added, deleted, modified, or changed, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife diff --name-status node-lb
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>node-lb/recipes/eip.rb
-node-lb/recipes/heartbeat-int.rb
-node-lb/templates/default/corpsite.conf.erb
-node-lb/files/default/wildcard.node.com.crt
-node-lb/files/default/wildcard.node.com.crt-2009
-node-lb/files/default/wildcard.node.com.key
-node-lb/.gitignore
-node-lb/Rakefile
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_download.html b/distro/common/html/knife_download.html
deleted file mode 100644
index ee473387d0..0000000000
--- a/distro/common/html/knife_download.html
+++ /dev/null
@@ -1,145 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife download &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-download">
-<h1>knife download<a class="headerlink" href="#knife-download" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife download</strong> subcommand is used to download roles, cookbooks, environments, nodes, and data bags from the Chef server to the current working directory. It can be used to back up data on the Chef server, inspect the state of one or more files, or to extract out-of-process changes users may have made to files on the Chef server, such as if a user made a change that bypassed version source control. This subcommand is often used in conjunction with <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">diff</span></tt>, which can be used to see exactly what changes will be downloaded, and then <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">upload</span></tt>, which does the opposite of <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">download</span></tt>.</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife download <span class="o">[</span>PATTERN...<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">--chef-repo-path</span> <span class="pre">PATH</span></tt></dt>
-<dd>The path to the chef-repo. This setting will override the default path to the chef-repo. Default: same value as specified by <tt class="docutils literal"><span class="pre">chef_repo_path</span></tt> in client.rb.</dd>
-<dt><tt class="docutils literal"><span class="pre">--concurrency</span></tt></dt>
-<dd>The number of allowed concurrent connections. Default: <tt class="docutils literal"><span class="pre">10</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--cookbook-version</span> <span class="pre">VERSION</span></tt></dt>
-<dd>The version of a cookbook to download.</dd>
-<dt><tt class="docutils literal"><span class="pre">-n</span></tt>, <tt class="docutils literal"><span class="pre">--dry-run</span></tt></dt>
-<dd>Use to take no action and only print out results. Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]diff</span></tt></dt>
-<dd>Use to download only new and modified files. Set to <tt class="docutils literal"><span class="pre">false</span></tt> to download all files. Default: <tt class="docutils literal"><span class="pre">--diff</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]force</span></tt></dt>
-<dd>Use <tt class="docutils literal"><span class="pre">--force</span></tt> to download files even when the file on the hard drive is identical to the object on the server (role, cookbook, etc.). By default, files are compared to see if they have equivalent content, and local files are only overwritten if they are different. Default: <tt class="docutils literal"><span class="pre">--no-force</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]purge</span></tt></dt>
-<dd>Use <tt class="docutils literal"><span class="pre">--purge</span></tt> to delete local files and directories that do not exist on the Chef server. By default, if a role, cookbook, etc. does not exist on the Chef server, the local file for said role will be left alone and NOT deleted. Default: <tt class="docutils literal"><span class="pre">--no-purge</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]recurse</span></tt></dt>
-<dd>Use <tt class="docutils literal"><span class="pre">--no-recurse</span></tt> to disable downloading a directory recursively. Default: <tt class="docutils literal"><span class="pre">--recurse</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--repo-mode</span> <span class="pre">MODE</span></tt></dt>
-<dd>The layout of the local chef-repo. Possible values: <tt class="docutils literal"><span class="pre">static</span></tt>, <tt class="docutils literal"><span class="pre">everything</span></tt>, or <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>. Use <tt class="docutils literal"><span class="pre">static</span></tt> for just roles, environments, cookbooks, and data bags. By default, <tt class="docutils literal"><span class="pre">everything</span></tt> and <tt class="docutils literal"><span class="pre">hosted_everything</span></tt> are dynamically selected depending on the server type. Default: <tt class="docutils literal"><span class="pre">everything</span></tt> / <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Download the entire chef-repo</strong></p>
-<p>To download the entire chef-repo from the Chef server, browse to the top level of the chef-repo and enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife download /
-</pre></div>
-</div>
-<p><strong>Download the /cookbooks directory</strong></p>
-<p>To download the <tt class="docutils literal"><span class="pre">cookbooks/</span></tt> directory from the Chef server, browse to the top level of the chef-repo and enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife download cookbooks
-</pre></div>
-</div>
-<p>or from anywhere in the chef-repo, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife download /cookbooks
-</pre></div>
-</div>
-<p><strong>Download the /environments directory</strong></p>
-<p>To download the <tt class="docutils literal"><span class="pre">environments/</span></tt> directory from the Chef server, browse to the top level of the chef-repo and enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife download environments
-</pre></div>
-</div>
-<p>or from anywhere in the chef-repo, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife download /environments
-</pre></div>
-</div>
-<p><strong>Download an environment</strong></p>
-<p>To download an environment named &#8220;production&#8221; from the Chef server, browse to the top level of the chef-repo and enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife download environments/production.json
-</pre></div>
-</div>
-<p>or from the <tt class="docutils literal"><span class="pre">environments/</span></tt> directory, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife download production.json
-</pre></div>
-</div>
-<p><strong>Download the /roles directory</strong></p>
-<p>To download the <tt class="docutils literal"><span class="pre">roles/</span></tt> directory from the Chef server, browse to the top level of the chef-repo and enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife download roles
-</pre></div>
-</div>
-<p>or from anywhere in the chef-repo, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife download /roles
-</pre></div>
-</div>
-<p><strong>Download cookbooks and roles</strong></p>
-<p>To download all cookbooks that start with &#8220;apache&#8221; and belong to the &#8220;webserver&#8221; role, browse to the top level of the chef-repo and enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span> knife download cookbooks/apache<span class="se">\*</span> roles/webserver.json
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_edit.html b/distro/common/html/knife_edit.html
deleted file mode 100644
index c9d26271a2..0000000000
--- a/distro/common/html/knife_edit.html
+++ /dev/null
@@ -1,89 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife edit &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-edit">
-<h1>knife edit<a class="headerlink" href="#knife-edit" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife edit</strong> subcommand is used to edit objects on the Chef server. This subcommand works similar to <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">cookbook</span> <span class="pre">edit</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">data</span> <span class="pre">bag</span> <span class="pre">edit</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">environment</span> <span class="pre">edit</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">node</span> <span class="pre">edit</span></tt>, and <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">role</span> <span class="pre">edit</span></tt>, but with a single verb (and a single action).</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife edit <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">--chef-repo-path</span> <span class="pre">PATH</span></tt></dt>
-<dd>The path to the chef-repo. This setting will override the default path to the chef-repo. Default: same value as specified by <tt class="docutils literal"><span class="pre">chef_repo_path</span></tt> in client.rb.</dd>
-<dt><tt class="docutils literal"><span class="pre">--concurrency</span></tt></dt>
-<dd>The number of allowed concurrent connections. Default: <tt class="docutils literal"><span class="pre">10</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--local</span></tt></dt>
-<dd>Use to show files in the local chef-repo instead of a remote location. Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--repo-mode</span> <span class="pre">MODE</span></tt></dt>
-<dd>The layout of the local chef-repo. Possible values: <tt class="docutils literal"><span class="pre">static</span></tt>, <tt class="docutils literal"><span class="pre">everything</span></tt>, or <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>. Use <tt class="docutils literal"><span class="pre">static</span></tt> for just roles, environments, cookbooks, and data bags. By default, <tt class="docutils literal"><span class="pre">everything</span></tt> and <tt class="docutils literal"><span class="pre">hosted_everything</span></tt> are dynamically selected depending on the server type. Default: <tt class="docutils literal"><span class="pre">everything</span></tt> / <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>None.</p>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_environment.html b/distro/common/html/knife_environment.html
deleted file mode 100644
index 75ef69f8a0..0000000000
--- a/distro/common/html/knife_environment.html
+++ /dev/null
@@ -1,326 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife environment &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-environment">
-<h1>knife environment<a class="headerlink" href="#knife-environment" title="Permalink to this headline">¶</a></h1>
-<p>An environment is a way to map an organization&#8217;s real-life workflow to what can be configured and managed when using Chef server. Every organization begins with a single environment called the <tt class="docutils literal"><span class="pre">_default</span></tt> environment, which cannot be modified (or deleted). Additional environments can be created to reflect each organization&#8217;s patterns and workflow. For example, creating <tt class="docutils literal"><span class="pre">production</span></tt>, <tt class="docutils literal"><span class="pre">staging</span></tt>, <tt class="docutils literal"><span class="pre">testing</span></tt>, and <tt class="docutils literal"><span class="pre">development</span></tt> environments. Generally, an environment is also associated with one (or more) cookbook versions.</p>
-<p>The <strong>knife environment</strong> subcommand is used to manage environments within a single organization on the Chef server.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<div class="section" id="compare">
-<h2>compare<a class="headerlink" href="#compare" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">compare</span></tt> argument is used to compare the cookbook version constraints that are set on one (or more) environments.</p>
-<p><strong>Syntax</strong></p>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment compare <span class="o">[</span>ENVIRONMENT_NAME...<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-<p><strong>Options</strong></p>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span></tt>, <tt class="docutils literal"><span class="pre">--all</span></tt></dt>
-<dd>Use to upload all environments found at the specified path.</dd>
-<dt><tt class="docutils literal"><span class="pre">-m</span></tt>, <tt class="docutils literal"><span class="pre">--mismatch</span></tt></dt>
-<dd>Use to show only matching versions.</dd>
-</dl>
-<p><strong>Example</strong></p>
-<p>To compare cookbook versions for a single environment:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment compare development
-</pre></div>
-</div>
-<p>to return something similar to:</p>
-<div class="highlight-bash"><div class="highlight"><pre> development
-apache 2.3.1
-windows 4.1.2
-</pre></div>
-</div>
-<p>To compare cookbook versions for multiple environments:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment compare development staging
-</pre></div>
-</div>
-<p>to return something similar to:</p>
-<div class="highlight-bash"><div class="highlight"><pre> development staging
-apache 2.3.1 1.2.2
-windows 4.1.2 1.0.0
-postgresql 1.0.0 1.0.0
-</pre></div>
-</div>
-<p>To compare all cookbook versions for all environments:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment compare --all
-</pre></div>
-</div>
-<p>to return something similar to:</p>
-<div class="highlight-bash"><div class="highlight"><pre> staging development
-<span class="nb">ulimit </span>latest latest
-redisio latest latest
-journly latest latest
-aws latest latest
-<span class="nb">test </span>latest latest
-unicorn latest latest
-sensu latest latest
-runit latest latest
-templater latest latest
-powershell latest latest
-openssl latest latest
-rbenv latest latest
-rabbitmq latest latest
-postgresql latest latest
-mysql latest latest
-ohai latest latest
-git latest latest
-erlang latest latest
-ssh_known_hosts latest latest
-nginx latest latest
-database latest latest
-yum latest latest
-xfs latest latest
-apt latest latest
-dmg latest latest
-chef_handler latest latest
-windows 1.0.0 4.1.2
-</pre></div>
-</div>
-</div>
-<div class="section" id="create">
-<h2>create<a class="headerlink" href="#create" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">create</span></tt> argument is used to add an environment object to the Chef server. When this argument is run, knife will open $EDITOR to enable editing of the <tt class="docutils literal"><span class="pre">ENVIRONMENT</span></tt> description field (unless a description is specified as part of the command). When finished, knife will add the environment to the Chef server.</p>
-<div class="section" id="syntax">
-<h3>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment create ENVIRONMENT_NAME -d DESCRIPTION
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h3>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-d</span> <span class="pre">DESCRIPTION</span></tt>, <tt class="docutils literal"><span class="pre">--description</span> <span class="pre">DESCRIPTION</span></tt></dt>
-<dd>The description of the environment. This value will populate the description field for the environment on the Chef server.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h3>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Create an environment</strong></p>
-<p>To create an environment named <tt class="docutils literal"><span class="pre">dev</span></tt> with a description of <tt class="docutils literal"><span class="pre">The</span> <span class="pre">development</span> <span class="pre">environment.</span></tt>:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment create dev -d <span class="s2">&quot;The development environment.&quot;</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="delete">
-<h2>delete<a class="headerlink" href="#delete" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">delete</span></tt> argument is used to delete an environment from a Chef server.</p>
-<div class="section" id="id1">
-<h3>Syntax<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment delete ENVIRONMENT_NAME
-</pre></div>
-</div>
-</div>
-<div class="section" id="id2">
-<h3>Options<a class="headerlink" href="#id2" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id3">
-<h3>Examples<a class="headerlink" href="#id3" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Delete an environment</strong></p>
-<p>To delete an environment named <tt class="docutils literal"><span class="pre">dev</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment delete dev
-</pre></div>
-</div>
-<p>Type <tt class="docutils literal"><span class="pre">Y</span></tt> to confirm a deletion.</p>
-</div>
-</div>
-<div class="section" id="edit">
-<h2>edit<a class="headerlink" href="#edit" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">edit</span></tt> argument is used to edit the attributes of an environment. When this argument is run, knife will open $EDITOR to enable editing of <tt class="docutils literal"><span class="pre">ENVIRONMENT</span></tt> attributes. When finished, knife will update the Chef server with those changes.</p>
-<div class="section" id="id4">
-<h3>Syntax<a class="headerlink" href="#id4" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment edit ENVIRONMENT_NAME
-</pre></div>
-</div>
-</div>
-<div class="section" id="id5">
-<h3>Options<a class="headerlink" href="#id5" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id6">
-<h3>Examples<a class="headerlink" href="#id6" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Edit an environment</strong></p>
-<p>To edit an environment named <tt class="docutils literal"><span class="pre">devops</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment edit devops
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="from-file">
-<h2>from file<a class="headerlink" href="#from-file" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">from</span> <span class="pre">file</span></tt> argument is used to add or update an environment using a JSON or Ruby DSL description. It must be run with the <tt class="docutils literal"><span class="pre">create</span></tt> or <tt class="docutils literal"><span class="pre">edit</span></tt> arguments.</p>
-<div class="section" id="id7">
-<h3>Syntax<a class="headerlink" href="#id7" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment <span class="o">[</span>create | edit<span class="o">]</span> from file FILE <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id8">
-<h3>Options<a class="headerlink" href="#id8" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span></tt>, <tt class="docutils literal"><span class="pre">--all</span></tt></dt>
-<dd>Use to upload all environments found at the specified path.</dd>
-</dl>
-</div>
-<div class="section" id="id9">
-<h3>Examples<a class="headerlink" href="#id9" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Create an environment from a JSON file</strong></p>
-<p>To add an environment using data contained in a JSON file:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment create devops from file <span class="s2">&quot;path to JSON file&quot;</span>
-</pre></div>
-</div>
-<p>or:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment edit devops from file <span class="s2">&quot;path to JSON file&quot;</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="list">
-<h2>list<a class="headerlink" href="#list" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">list</span></tt> argument is used to list all of the environments that are currently available on the Chef server.</p>
-<div class="section" id="id10">
-<h3>Syntax<a class="headerlink" href="#id10" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment list -w
-</pre></div>
-</div>
-</div>
-<div class="section" id="id11">
-<h3>Options<a class="headerlink" href="#id11" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-w</span></tt>, <tt class="docutils literal"><span class="pre">--with-uri</span></tt></dt>
-<dd>Use to show the corresponding URIs.</dd>
-</dl>
-</div>
-<div class="section" id="id12">
-<h3>Examples<a class="headerlink" href="#id12" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>View a list of environments</strong></p>
-<p>To view a list of environments:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment list -w
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="show">
-<h2>show<a class="headerlink" href="#show" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">show</span></tt> argument is used to display information about the specified environment.</p>
-<div class="section" id="id13">
-<h3>Syntax<a class="headerlink" href="#id13" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment show ENVIRONMENT_NAME
-</pre></div>
-</div>
-</div>
-<div class="section" id="id14">
-<h3>Options<a class="headerlink" href="#id14" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span> <span class="pre">ATTR</span></tt>, <tt class="docutils literal"><span class="pre">--attribute</span> <span class="pre">ATTR</span></tt></dt>
-<dd>The attribute (or attributes) to show.</dd>
-</dl>
-</div>
-<div class="section" id="id15">
-<h3>Examples<a class="headerlink" href="#id15" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Show environments</strong></p>
-<p>To view information about the <tt class="docutils literal"><span class="pre">dev</span></tt> environment enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife environment show dev
-</pre></div>
-</div>
-<p>to return:</p>
-<div class="highlight-bash"><div class="highlight"><pre>% knife environment show dev
-chef_type: environment
-cookbook_versions:
-default_attributes:
-description:
-json_class: Chef::Environment
-name: dev
-override_attributes:
-
-<span class="se">\\</span>
-<span class="se">\\</span>
-<span class="se">\\</span>
-<span class="se">\\</span>
-</pre></div>
-</div>
-<p><strong>Show environments as JSON</strong></p>
-<p>To view information in JSON format, use the <tt class="docutils literal"><span class="pre">-F</span></tt> common option as part of the command like this:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role show devops -F json
-</pre></div>
-</div>
-<p>Other formats available include <tt class="docutils literal"><span class="pre">text</span></tt>, <tt class="docutils literal"><span class="pre">yaml</span></tt>, and <tt class="docutils literal"><span class="pre">pp</span></tt>.</p>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_exec.html b/distro/common/html/knife_exec.html
deleted file mode 100644
index 656ee382dc..0000000000
--- a/distro/common/html/knife_exec.html
+++ /dev/null
@@ -1,220 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife exec &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-exec">
-<h1>knife exec<a class="headerlink" href="#knife-exec" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife exec</strong> subcommand uses the knife configuration file to execute Ruby scripts in the context of a fully configured chef-client. This subcommand is most often used to run scripts that will only access Chef server one time (or otherwise very infrequently). Use this subcommand any time that an operation does not warrant full usage of the knife subcommand library.</p>
-<div class="section" id="authenticated-api-requests">
-<h2>Authenticated API Requests<a class="headerlink" href="#authenticated-api-requests" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">exec</span></tt> subcommand can be used to make authenticated API requests to the Chef server using the following methods:</p>
-<table border="1" class="docutils">
-<colgroup>
-<col width="13%" />
-<col width="88%" />
-</colgroup>
-<thead valign="bottom">
-<tr class="row-odd"><th class="head">Method</th>
-<th class="head">Description</th>
-</tr>
-</thead>
-<tbody valign="top">
-<tr class="row-even"><td><tt class="docutils literal"><span class="pre">api.delete</span></tt></td>
-<td>Use to delete an object from the Chef server.</td>
-</tr>
-<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">api.get</span></tt></td>
-<td>Use to get the details of an object on the Chef server.</td>
-</tr>
-<tr class="row-even"><td><tt class="docutils literal"><span class="pre">api.post</span></tt></td>
-<td>Use to add an object to the Chef server.</td>
-</tr>
-<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">api.put</span></tt></td>
-<td>Use to update an object on the Chef server.</td>
-</tr>
-</tbody>
-</table>
-<p>These methods are used with the <tt class="docutils literal"><span class="pre">-E</span></tt> option, which executes that string locally on the workstation using chef-shell. These methods have the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife <span class="nb">exec</span> -E <span class="s1">&#39;api.method(/endpoint)&#39;</span>
-</pre></div>
-</div>
-<p>where:</p>
-<ul class="simple">
-<li><tt class="docutils literal"><span class="pre">api.method</span></tt> is the corresponding authentication method &#8212; <tt class="docutils literal"><span class="pre">api.delete</span></tt>, <tt class="docutils literal"><span class="pre">api.get</span></tt>, <tt class="docutils literal"><span class="pre">api.post</span></tt>, or <tt class="docutils literal"><span class="pre">api.put</span></tt></li>
-<li><tt class="docutils literal"><span class="pre">/endpoint</span></tt> is an endpoint in the Chef server API</li>
-</ul>
-<p>For example, to get the data for a node named &#8220;Example_Node&#8221;:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife <span class="nb">exec</span> -E <span class="s1">&#39;puts api.get(&quot;/nodes/Example_Node&quot;)&#39;</span>
-</pre></div>
-</div>
-<p>and to ensure that the output is visible in the console, add the <tt class="docutils literal"><span class="pre">puts</span></tt> in front of the API authorization request:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife <span class="nb">exec</span> -E <span class="s1">&#39;puts api.get(&quot;/nodes/Example_Node&quot;)&#39;</span>
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">puts</span></tt> is the shorter version of the <tt class="docutils literal"><span class="pre">$stdout.puts</span></tt> predefined variable in Ruby.</p>
-<p>The following example shows how to add a client named &#8220;IBM305RAMAC&#8221; and the <tt class="docutils literal"><span class="pre">/clients</span></tt> endpoint, and then return the private key for that user in the console:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ client_desc</span> <span class="o">=</span> <span class="o">{</span>
- <span class="s2">&quot;name&quot;</span> <span class="o">=</span>&gt; <span class="s2">&quot;IBM305RAMAC&quot;</span>,
- <span class="s2">&quot;admin&quot;</span> <span class="o">=</span>&gt; <span class="nb">false</span>
- <span class="o">}</span>
-
- <span class="nv">new_client</span> <span class="o">=</span> api.post<span class="o">(</span><span class="s2">&quot;/clients&quot;</span>, client_desc<span class="o">)</span>
- puts new_client<span class="o">[</span><span class="s2">&quot;private_key&quot;</span><span class="o">]</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="ruby-scripts">
-<h2>Ruby Scripts<a class="headerlink" href="#ruby-scripts" title="Permalink to this headline">¶</a></h2>
-<p>For Ruby scripts that will be run using the <tt class="docutils literal"><span class="pre">exec</span></tt> subcommand, note the following:</p>
-<blockquote>
-<div><ul class="simple">
-<li>The Ruby script must be located on the system from which knife is run (and not be located on any of the systems that knife will be managing).</li>
-<li>Shell commands will be run from a management workstation. For example, something like <tt class="docutils literal"><span class="pre">%x[ls</span> <span class="pre">-lash</span> <span class="pre">/opt/only-on-a-node]</span></tt> would give you the directory listing for the &#8220;opt/only-on-a-node&#8221; directory or a &#8220;No such file or directory&#8221; error if the file does not already exist locally.</li>
-<li>When the chef-shell DSL is available, the chef-client DSL will not be (unless the management workstation is also a chef-client). Without the chef-client DSL, a bash block cannot be used to run bash commands.</li>
-</ul>
-</div></blockquote>
-</div>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife <span class="nb">exec </span>SCRIPT <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-E</span> <span class="pre">CODE</span></tt>, <tt class="docutils literal"><span class="pre">--exec</span> <span class="pre">CODE</span></tt></dt>
-<dd>A string of code that will be executed.</dd>
-<dt><tt class="docutils literal"><span class="pre">-p</span> <span class="pre">PATH:PATH</span></tt>, <tt class="docutils literal"><span class="pre">--script-path</span> <span class="pre">PATH:PATH</span></tt></dt>
-<dd>A colon-separated path at which Ruby scripts are located. Use to override the default location for scripts. When this option is not specified, knife will look for scripts located in <tt class="docutils literal"><span class="pre">chef-repo/.chef/scripts</span></tt> directory.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Run Ruby scripts</strong></p>
-<p>There are three ways to use <strong>knife exec</strong> to run Ruby script files. For example:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife <span class="nb">exec</span> /path/to/script_file
-</pre></div>
-</div>
-<p>or:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife <span class="nb">exec</span> -E <span class="s1">&#39;RUBY CODE&#39;</span>
-</pre></div>
-</div>
-<p>or:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife <span class="nb">exec</span>
-RUBY CODE
-^D
-</pre></div>
-</div>
-<p><strong>Chef Knife status</strong></p>
-<p>To check the status of knife using a Ruby script named <tt class="docutils literal"><span class="pre">status.rb</span></tt> (which looks like):</p>
-<div class="highlight-ruby"><div class="highlight"><pre><span class="nb">printf</span> <span class="s2">&quot;%-5s %-12s %-8s %s</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="s2">&quot;Check In&quot;</span><span class="p">,</span> <span class="s2">&quot;Name&quot;</span><span class="p">,</span> <span class="s2">&quot;Ruby&quot;</span><span class="p">,</span> <span class="s2">&quot;Recipes&quot;</span>
-<span class="n">nodes</span><span class="o">.</span><span class="n">all</span> <span class="k">do</span> <span class="o">|</span><span class="n">n</span><span class="o">|</span>
- <span class="n">checkin</span> <span class="o">=</span> <span class="no">Time</span><span class="o">.</span><span class="n">at</span><span class="p">(</span><span class="n">n</span><span class="o">[</span><span class="s1">&#39;ohai_time&#39;</span><span class="o">]</span><span class="p">)</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">&quot;%F %R&quot;</span><span class="p">)</span>
- <span class="n">rubyver</span> <span class="o">=</span> <span class="n">n</span><span class="o">[</span><span class="s1">&#39;languages&#39;</span><span class="o">][</span><span class="s1">&#39;ruby&#39;</span><span class="o">][</span><span class="s1">&#39;version&#39;</span><span class="o">]</span>
- <span class="n">recipes</span> <span class="o">=</span> <span class="n">n</span><span class="o">.</span><span class="n">run_list</span><span class="o">.</span><span class="n">expand</span><span class="p">(</span><span class="n">_default</span><span class="p">)</span><span class="o">.</span><span class="n">recipes</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&quot;, &quot;</span><span class="p">)</span>
- <span class="nb">printf</span> <span class="s2">&quot;%-20s %-12s %-8s %s</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">checkin</span><span class="p">,</span> <span class="n">n</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">rubyver</span><span class="p">,</span> <span class="n">recipes</span>
-<span class="k">end</span>
-</pre></div>
-</div>
-<p>and is located in a directory named <tt class="docutils literal"><span class="pre">scripts/</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife <span class="nb">exec </span>scripts/status.rb
-</pre></div>
-</div>
-<p><strong>List available free memory</strong></p>
-<p>To show the available free memory for all nodes, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife <span class="nb">exec</span> -E <span class="s1">&#39;nodes.all {|n| puts &quot;#{n.name} has #{n.memory.total} free memory&quot;}&#39;</span>
-</pre></div>
-</div>
-<p><strong>List available search indexes</strong></p>
-<p>To list all of the available search indexes, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife <span class="nb">exec</span> -E <span class="s1">&#39;puts api.get(&quot;search&quot;).keys&#39;</span>
-</pre></div>
-</div>
-<p><strong>Query for multiple attributes</strong></p>
-<p>To query a node for multiple attributes using a Ruby script named <tt class="docutils literal"><span class="pre">search_attributes.rb</span></tt> (which looks like):</p>
-<div class="highlight-ruby"><div class="highlight"><pre><span class="sx">% cat </span><span class="n">scripts</span><span class="o">/</span><span class="n">search_attributes</span><span class="o">.</span><span class="n">rb</span>
-<span class="n">query</span> <span class="o">=</span> <span class="no">ARGV</span><span class="o">[</span><span class="mi">2</span><span class="o">]</span>
-<span class="n">attributes</span> <span class="o">=</span> <span class="no">ARGV</span><span class="o">[</span><span class="mi">3</span><span class="o">].</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;,&quot;</span><span class="p">)</span>
-<span class="nb">puts</span> <span class="s2">&quot;Your query: </span><span class="si">#{</span><span class="n">query</span><span class="si">}</span><span class="s2">&quot;</span>
-<span class="nb">puts</span> <span class="s2">&quot;Your attributes: </span><span class="si">#{</span><span class="n">attributes</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&quot; &quot;</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span>
-<span class="n">results</span> <span class="o">=</span> <span class="p">{}</span>
-<span class="n">search</span><span class="p">(</span><span class="ss">:node</span><span class="p">,</span> <span class="n">query</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">n</span><span class="o">|</span>
- <span class="n">results</span><span class="o">[</span><span class="n">n</span><span class="o">.</span><span class="n">name</span><span class="o">]</span> <span class="o">=</span> <span class="p">{}</span>
- <span class="n">attributes</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span><span class="o">|</span><span class="n">a</span><span class="o">|</span> <span class="n">results</span><span class="o">[</span><span class="n">n</span><span class="o">.</span><span class="n">name</span><span class="o">][</span><span class="n">a</span><span class="o">]</span> <span class="o">=</span> <span class="n">n</span><span class="o">[</span><span class="n">a</span><span class="o">]</span><span class="p">}</span>
-<span class="k">end</span>
-
-<span class="nb">puts</span> <span class="n">results</span>
-<span class="nb">exit</span> <span class="mi">0</span>
-</pre></div>
-</div>
-<p>enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre>% knife <span class="nb">exec </span>scripts/search_attributes.rb <span class="s2">&quot;hostname:test_system&quot;</span> ipaddress,fqdn
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>Your query: hostname:test_system
-Your attributes: ipaddress fqdn
-<span class="o">{</span><span class="s2">&quot;test_system.example.com&quot;</span><span class="o">=</span>&gt;<span class="o">{</span><span class="s2">&quot;ipaddress&quot;</span><span class="o">=</span>&gt;<span class="s2">&quot;10.1.1.200&quot;</span>, <span class="s2">&quot;fqdn&quot;</span><span class="o">=</span>&gt;<span class="s2">&quot;test_system.example.com&quot;</span><span class="o">}}</span>
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_index_rebuild.html b/distro/common/html/knife_index_rebuild.html
deleted file mode 100644
index 05bf640a81..0000000000
--- a/distro/common/html/knife_index_rebuild.html
+++ /dev/null
@@ -1,83 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife index rebuild &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-index-rebuild">
-<h1>knife index rebuild<a class="headerlink" href="#knife-index-rebuild" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife index rebuild</strong> subcommand is used to rebuild the search indexes for the open source Chef server. This operation is destructive and may take some time.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">This subcommand ONLY works when run against the open source Chef server version 10.x. This subcommand will NOT run against open source Chef server 11, Enterprise Chef (including hosted Enterprise Chef), or Private Chef.</p>
-</div>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife index rebuild
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>None.</p>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_list.html b/distro/common/html/knife_list.html
deleted file mode 100644
index 28135114fb..0000000000
--- a/distro/common/html/knife_list.html
+++ /dev/null
@@ -1,114 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife list &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-list">
-<h1>knife list<a class="headerlink" href="#knife-list" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife list</strong> subcommand is used to view a list of objects on the Chef server. This subcommand works similar to <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">cookbook</span> <span class="pre">list</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">data</span> <span class="pre">bag</span> <span class="pre">list</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">environment</span> <span class="pre">list</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">node</span> <span class="pre">list</span></tt>, and <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">role</span> <span class="pre">list</span></tt>, but with a single verb (and a single action).</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife list <span class="o">[</span>PATTERN...<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-1</span></tt></dt>
-<dd>Use to show only one column of results. Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--chef-repo-path</span> <span class="pre">PATH</span></tt></dt>
-<dd>The path to the chef-repo. This setting will override the default path to the chef-repo. Default: same value as specified by <tt class="docutils literal"><span class="pre">chef_repo_path</span></tt> in client.rb.</dd>
-<dt><tt class="docutils literal"><span class="pre">--concurrency</span></tt></dt>
-<dd>The number of allowed concurrent connections. Default: <tt class="docutils literal"><span class="pre">10</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-d</span></tt></dt>
-<dd>Use to prevent a directory&#8217;s children from showing when a directory matches a pattern. Default value: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-f</span></tt>, <tt class="docutils literal"><span class="pre">--flat</span></tt></dt>
-<dd>Use to show a list of file names. Set to <tt class="docutils literal"><span class="pre">false</span></tt> to view ls-like output. Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--local</span></tt></dt>
-<dd>Use to return only the contents of the local directory. Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-p</span></tt></dt>
-<dd>Use to show directories with trailing slashes (/). Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-R</span></tt></dt>
-<dd>Use to list directories recursively. Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--repo-mode</span> <span class="pre">MODE</span></tt></dt>
-<dd>The layout of the local chef-repo. Possible values: <tt class="docutils literal"><span class="pre">static</span></tt>, <tt class="docutils literal"><span class="pre">everything</span></tt>, or <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>. Use <tt class="docutils literal"><span class="pre">static</span></tt> for just roles, environments, cookbooks, and data bags. By default, <tt class="docutils literal"><span class="pre">everything</span></tt> and <tt class="docutils literal"><span class="pre">hosted_everything</span></tt> are dynamically selected depending on the server type. Default: <tt class="docutils literal"><span class="pre">everything</span></tt> / <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>List roles</strong></p>
-<p>For example, to view a list of roles on the Chef server:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife list roles/
-</pre></div>
-</div>
-<p><strong>List roles and environments</strong></p>
-<p>To view a list of roles and environments on the Chef server:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife list roles/ environments/
-</pre></div>
-</div>
-<p><strong>List everything</strong></p>
-<p>To view a list of absolutely everything on the Chef server:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife list -R /
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_node.html b/distro/common/html/knife_node.html
deleted file mode 100644
index a3d7a51d1c..0000000000
--- a/distro/common/html/knife_node.html
+++ /dev/null
@@ -1,458 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife node &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-node">
-<h1>knife node<a class="headerlink" href="#knife-node" title="Permalink to this headline">¶</a></h1>
-<p>A node is any physical, virtual, or cloud machine that is configured to be maintained by a chef-client.</p>
-<p>The <strong>knife node</strong> subcommand is used to manage the nodes that exist on a Chef server.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<div class="section" id="bulk-delete">
-<h2>bulk delete<a class="headerlink" href="#bulk-delete" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">bulk</span> <span class="pre">delete</span></tt> argument is used to delete one or more nodes that match a pattern defined by a regular expression. The regular expression must be within quotes and not be surrounded by forward slashes (/).</p>
-<div class="section" id="syntax">
-<h3>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node bulk delete REGEX
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h3>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="examples">
-<h3>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Bulk delete nodes</strong></p>
-<p>Use a regular expression to define the pattern used to bulk delete nodes:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node bulk delete <span class="s2">&quot;^[0-9]{3}$&quot;</span>
-</pre></div>
-</div>
-<p>Type <tt class="docutils literal"><span class="pre">Y</span></tt> to confirm a deletion.</p>
-</div>
-</div>
-<div class="section" id="create">
-<h2>create<a class="headerlink" href="#create" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">create</span></tt> argument is used to add a node to the Chef server. Node data is stored as JSON on the Chef server.</p>
-<div class="section" id="id1">
-<h3>Syntax<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node create NODE_NAME
-</pre></div>
-</div>
-</div>
-<div class="section" id="id2">
-<h3>Options<a class="headerlink" href="#id2" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id3">
-<h3>Examples<a class="headerlink" href="#id3" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Create a node</strong></p>
-<p>To add a node, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node create node1
-</pre></div>
-</div>
-<p>In the $EDITOR enter the node data in JSON:</p>
-<div class="highlight-javascript"><div class="highlight"><pre>## sample:
-{
- &quot;normal&quot;: {
- },
- &quot;name&quot;: &quot;foobar&quot;,
- &quot;override&quot;: {
- },
- &quot;default&quot;: {
- },
- &quot;json_class&quot;: &quot;Chef::Node&quot;,
- &quot;automatic&quot;: {
- },
- &quot;run_list&quot;: [
- &quot;recipe[zsh]&quot;,
- &quot;role[webserver]&quot;
- ],
- &quot;chef_type&quot;: &quot;node&quot;
-}
-</pre></div>
-</div>
-<p>When finished, save it.</p>
-</div>
-</div>
-<div class="section" id="delete">
-<h2>delete<a class="headerlink" href="#delete" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">delete</span></tt> argument is used to delete a node from the Chef server.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Deleting a node will not delete any corresponding API clients.</p>
-</div>
-<div class="section" id="id4">
-<h3>Syntax<a class="headerlink" href="#id4" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node delete NODE_NAME
-</pre></div>
-</div>
-</div>
-<div class="section" id="id5">
-<h3>Options<a class="headerlink" href="#id5" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id6">
-<h3>Examples<a class="headerlink" href="#id6" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Delete a node</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node delete node_name
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="edit">
-<h2>edit<a class="headerlink" href="#edit" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">edit</span></tt> argument is used to edit the details of a node on a Chef server. Node data is stored as JSON on the Chef server.</p>
-<div class="section" id="id7">
-<h3>Syntax<a class="headerlink" href="#id7" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node edit NODE_NAME <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id8">
-<h3>Options<a class="headerlink" href="#id8" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span></tt>, <tt class="docutils literal"><span class="pre">--all</span></tt></dt>
-<dd>Displays a node in the $EDITOR. By default, attributes that are default, override, or automatic are not shown.</dd>
-</dl>
-</div>
-<div class="section" id="id9">
-<h3>Examples<a class="headerlink" href="#id9" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Edit a node</strong></p>
-<p>To edit the data for a node named <tt class="docutils literal"><span class="pre">node1</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node edit node1 -a
-</pre></div>
-</div>
-<p>Update the role data in JSON:</p>
-<div class="highlight-javascript"><div class="highlight"><pre>## sample:
-{
- &quot;normal&quot;: {
- },
- &quot;name&quot;: &quot;node1&quot;,
- &quot;override&quot;: {
- },
- &quot;default&quot;: {
- },
- &quot;json_class&quot;: &quot;Chef::Node&quot;,
- &quot;automatic&quot;: {
- },
- &quot;run_list&quot;: [
- &quot;recipe[devops]&quot;,
- &quot;role[webserver]&quot;
- ],
- &quot;chef_type&quot;: &quot;node&quot;
-}
-</pre></div>
-</div>
-<p>When finished, save it.</p>
-</div>
-</div>
-<div class="section" id="from-file">
-<h2>from file<a class="headerlink" href="#from-file" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">from</span> <span class="pre">file</span></tt> argument is used to create a node using existing node data as a template.</p>
-<div class="section" id="id10">
-<h3>Syntax<a class="headerlink" href="#id10" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node from file FILE
-</pre></div>
-</div>
-</div>
-<div class="section" id="id11">
-<h3>Options<a class="headerlink" href="#id11" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id12">
-<h3>Examples<a class="headerlink" href="#id12" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Create a node using a JSON file</strong></p>
-<p>To add a node using data contained in a JSON file:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node from file <span class="s2">&quot;path to JSON file&quot;</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="list">
-<h2>list<a class="headerlink" href="#list" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">list</span></tt> argument is used to view all of the nodes that exist on a Chef server.</p>
-<div class="section" id="id13">
-<h3>Syntax<a class="headerlink" href="#id13" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node list <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id14">
-<h3>Options<a class="headerlink" href="#id14" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-w</span></tt>, <tt class="docutils literal"><span class="pre">--with-uri</span></tt></dt>
-<dd>Use to show the corresponding URIs.</dd>
-</dl>
-</div>
-<div class="section" id="id15">
-<h3>Examples<a class="headerlink" href="#id15" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>View a list of nodes</strong></p>
-<p>To verify the list of nodes that are registered with the Chef server, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node list
-</pre></div>
-</div>
-<p>to return something similar to:</p>
-<div class="highlight-bash"><div class="highlight"><pre>i-12345678
-rs-123456
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="run-list-add">
-<h2>run_list add<a class="headerlink" href="#run-list-add" title="Permalink to this headline">¶</a></h2>
-<p>A run-list defines all of the configuration settings that are necessary for a node that is under management by Chef to be put into the desired state. A run-list is:</p>
-<ul class="simple">
-<li>An ordered list of roles and/or recipes that are run in an exact order; if a recipe appears more than once in the run-list, the chef-client will never run that recipe twice</li>
-<li>Always specific to the node on which it runs, though it is possible for many nodes to have run-lists that are similar or even identical</li>
-<li>Stored as part of the node object on the Chef server</li>
-<li>Maintained using knife and uploaded to the Chef server or via the Chef management console user interface</li>
-</ul>
-<p>The <tt class="docutils literal"><span class="pre">run_list</span> <span class="pre">add</span></tt> argument is used to add run-list items (roles or recipes) to a node.</p>
-<p>A run-list must be in one of the following formats: fully qualified, cookbook, or default. Both roles and recipes must be in quotes, for example:</p>
-<div class="highlight-ruby"><div class="highlight"><pre><span class="s1">&#39;role[ROLE_NAME]&#39;</span>
-</pre></div>
-</div>
-<p>or</p>
-<div class="highlight-ruby"><div class="highlight"><pre><span class="s1">&#39;recipe[COOKBOOK::RECIPE_NAME]&#39;</span>
-</pre></div>
-</div>
-<p>Use a comma to separate roles and recipes when adding more than one item the run-list:</p>
-<div class="highlight-ruby"><div class="highlight"><pre><span class="s1">&#39;recipe[COOKBOOK::RECIPE_NAME],COOKBOOK::RECIPE_NAME,role[ROLE_NAME]&#39;</span>
-</pre></div>
-</div>
-<div class="section" id="id16">
-<h3>Syntax<a class="headerlink" href="#id16" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node run_list add NODE_NAME RUN_LIST_ITEM <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id17">
-<h3>Options<a class="headerlink" href="#id17" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span> <span class="pre">ITEM</span></tt>, <tt class="docutils literal"><span class="pre">--after</span> <span class="pre">ITEM</span></tt></dt>
-<dd>Use this to add the run list item after the specified run list item.</dd>
-<dt><tt class="docutils literal"><span class="pre">-b</span> <span class="pre">ITEM</span></tt>, <tt class="docutils literal"><span class="pre">--before</span> <span class="pre">ITEM</span></tt></dt>
-<dd>Use this to add the run list item before the specified run list item.</dd>
-</dl>
-</div>
-<div class="section" id="id18">
-<h3>Examples<a class="headerlink" href="#id18" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Add a role</strong></p>
-<p>To add a role to a run-list, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node run_list add node <span class="s1">&#39;role[ROLE_NAME]&#39;</span>
-</pre></div>
-</div>
-<p><strong>Add roles and recipes</strong></p>
-<p>To add roles and recipes to a run-list, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node run_list add node <span class="s1">&#39;recipe[COOKBOOK::RECIPE_NAME],recipe[COOKBOOK::RECIPE_NAME],role[ROLE_NAME]&#39;</span>
-</pre></div>
-</div>
-<p><strong>Add a recipe with a FQDN</strong></p>
-<p>To add a recipe to a run-list using the fully qualified format, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node run_list add node <span class="s1">&#39;recipe[COOKBOOK::RECIPE_NAME]&#39;</span>
-</pre></div>
-</div>
-<p><strong>Add a recipe with a cookbook</strong></p>
-<p>To add a recipe to a run-list using the cookbook format, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node run_list add node <span class="s1">&#39;COOKBOOK::RECIPE_NAME&#39;</span>
-</pre></div>
-</div>
-<p><strong>Add the default recipe</strong></p>
-<p>To add the default recipe of a cookbook to a run-list, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node run_list add node <span class="s1">&#39;COOKBOOK&#39;</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="run-list-remove">
-<h2>run_list remove<a class="headerlink" href="#run-list-remove" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">run_list</span> <span class="pre">remove</span></tt> argument is used to remove run-list items (roles or recipes) from a node. A recipe must be in one of the following formats: fully qualified, cookbook, or default. Both roles and recipes must be in quotes, for example: <tt class="docutils literal"><span class="pre">'role[ROLE_NAME]'</span></tt> or <tt class="docutils literal"><span class="pre">'recipe[COOKBOOK::RECIPE_NAME]'</span></tt>. Use a comma to separate roles and recipes when removing more than one, like this: <tt class="docutils literal"><span class="pre">'recipe[COOKBOOK::RECIPE_NAME],COOKBOOK::RECIPE_NAME,role[ROLE_NAME]'</span></tt>.</p>
-<div class="section" id="id19">
-<h3>Syntax<a class="headerlink" href="#id19" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node run_list remove NODE_NAME RUN_LIST_ITEM
-</pre></div>
-</div>
-</div>
-<div class="section" id="id20">
-<h3>Options<a class="headerlink" href="#id20" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id21">
-<h3>Examples<a class="headerlink" href="#id21" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Remove a role</strong></p>
-<p>To remove a role from a run-list, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node run_list remove node <span class="s1">&#39;role[ROLE_NAME]&#39;</span>
-</pre></div>
-</div>
-<p><strong>Remove a run-list</strong></p>
-<p>To remove a recipe from a run-list using the fully qualified format, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node run_list remove node <span class="s1">&#39;recipe[COOKBOOK::RECIPE_NAME]&#39;</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="show">
-<h2>show<a class="headerlink" href="#show" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">show</span></tt> argument is used to display information about a node.</p>
-<div class="section" id="id22">
-<h3>Syntax<a class="headerlink" href="#id22" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node show NODE_NAME <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id23">
-<h3>Options<a class="headerlink" href="#id23" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span> <span class="pre">ATTR</span></tt>, <tt class="docutils literal"><span class="pre">--attribute</span> <span class="pre">ATTR</span></tt></dt>
-<dd>The attribute (or attributes) to show.</dd>
-<dt><tt class="docutils literal"><span class="pre">-l</span></tt>, <tt class="docutils literal"><span class="pre">--long</span></tt></dt>
-<dd>Use to display all attributes in the output and to show the output as JSON.</dd>
-<dt><tt class="docutils literal"><span class="pre">-m</span></tt>, <tt class="docutils literal"><span class="pre">--medium</span></tt></dt>
-<dd>Use to display normal attributes in the output and to show the output as JSON.</dd>
-<dt><tt class="docutils literal"><span class="pre">-r</span></tt>, <tt class="docutils literal"><span class="pre">--run-list</span></tt></dt>
-<dd>Use to show only the run-list.</dd>
-</dl>
-</div>
-<div class="section" id="id24">
-<h3>Examples<a class="headerlink" href="#id24" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Show all data about nodes</strong></p>
-<p>To view all data for a node named <tt class="docutils literal"><span class="pre">build</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node show build
-</pre></div>
-</div>
-<p>to return:</p>
-<div class="highlight-bash"><div class="highlight"><pre>Node Name: build
-Environment: _default
-FQDN:
-IP:
-Run List:
-Roles:
-Recipes:
-Platform:
-</pre></div>
-</div>
-<p><strong>Show basic information about nodes</strong></p>
-<p>To show basic information about a node, truncated and nicely formatted:</p>
-<div class="highlight-bash"><div class="highlight"><pre>knife node show &lt;node_name&gt;
-</pre></div>
-</div>
-<p><strong>Show all data about nodes, truncated</strong></p>
-<p>To show all information about a node, nicely formatted:</p>
-<div class="highlight-bash"><div class="highlight"><pre>knife node show -l &lt;node_name&gt;
-</pre></div>
-</div>
-<p><strong>Show attributes</strong></p>
-<p>To list a single node attribute:</p>
-<div class="highlight-bash"><div class="highlight"><pre>knife node show &lt;node_name&gt; -a &lt;attribute_name&gt;
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">&lt;attribute_name&gt;</span></tt> is something like kernel or platform. (This doesn&#8217;t work for nested attributes like <tt class="docutils literal"><span class="pre">node[kernel][machine]</span></tt> because <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">node</span> <span class="pre">show</span></tt> doesn&#8217;t understand nested attributes.)</p>
-<p><strong>Show the FQDN</strong></p>
-<p>To view the FQDN for a node named <tt class="docutils literal"><span class="pre">i-12345678</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node show i-12345678 -a fqdn
-</pre></div>
-</div>
-<p>to return:</p>
-<div class="highlight-bash"><div class="highlight"><pre>fqdn: ip-10-251-75-20.ec2.internal
-</pre></div>
-</div>
-<p><strong>Show a run-list</strong></p>
-<p>To view the run list for a node named <tt class="docutils literal"><span class="pre">dev</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife node show dev -r
-</pre></div>
-</div>
-<p><strong>Show as JSON data</strong></p>
-<p>To view information in JSON format, use the <tt class="docutils literal"><span class="pre">-F</span></tt> common option as part of the command like this:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role show devops -F json
-</pre></div>
-</div>
-<p>Other formats available include <tt class="docutils literal"><span class="pre">text</span></tt>, <tt class="docutils literal"><span class="pre">yaml</span></tt>, and <tt class="docutils literal"><span class="pre">pp</span></tt>.</p>
-<p><strong>Show as raw JSON data</strong></p>
-<p>To view node information in raw JSON, use the <tt class="docutils literal"><span class="pre">-l</span></tt> or <tt class="docutils literal"><span class="pre">--long</span></tt> option:</p>
-<div class="highlight-bash"><div class="highlight"><pre>knife node show -l -F json &lt;node_name&gt;
-</pre></div>
-</div>
-<p>and/or:</p>
-<div class="highlight-bash"><div class="highlight"><pre>knife node show -l --format<span class="o">=</span>json &lt;node_name&gt;
-</pre></div>
-</div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_raw.html b/distro/common/html/knife_raw.html
deleted file mode 100644
index e661e9a84a..0000000000
--- a/distro/common/html/knife_raw.html
+++ /dev/null
@@ -1,110 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife raw &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-raw">
-<h1>knife raw<a class="headerlink" href="#knife-raw" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife raw</strong> subcommand is used to send a REST request to an endpoint in the Chef server API.</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife raw REQUEST_PATH <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-i</span> <span class="pre">FILE</span></tt>, <tt class="docutils literal"><span class="pre">--input</span> <span class="pre">FILE</span></tt></dt>
-<dd>The name of a file to be used with the <tt class="docutils literal"><span class="pre">PUT</span></tt> or a <tt class="docutils literal"><span class="pre">POST</span></tt> request.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]pretty</span></tt></dt>
-<dd>Use <tt class="docutils literal"><span class="pre">--no-pretty</span></tt> to disable pretty-print output for JSON. Default: <tt class="docutils literal"><span class="pre">--pretty</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-m</span> <span class="pre">METHOD</span></tt>, <tt class="docutils literal"><span class="pre">--method</span> <span class="pre">METHOD</span></tt></dt>
-<dd>The request method: <tt class="docutils literal"><span class="pre">DELETE</span></tt>, <tt class="docutils literal"><span class="pre">GET</span></tt>, <tt class="docutils literal"><span class="pre">POST</span></tt>, or <tt class="docutils literal"><span class="pre">PUT</span></tt>. Default value: <tt class="docutils literal"><span class="pre">GET</span></tt>.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>View a client</strong></p>
-<p>To view information about a client:</p>
-<div class="highlight-bash"><div class="highlight"><pre>knife raw /clients/&lt;client_name&gt;
-</pre></div>
-</div>
-<p><strong>View a node</strong></p>
-<p>To view information about a node:</p>
-<div class="highlight-bash"><div class="highlight"><pre>knife raw /nodes/&lt;node_name&gt;
-</pre></div>
-</div>
-<p><strong>Delete a data bag</strong></p>
-<p>To delete a data bag, enter a command similar to:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife raw -m DELETE /data/foo
-</pre></div>
-</div>
-<p>to return something similar to:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="o">{</span>
- <span class="s2">&quot;name&quot;</span>:<span class="s2">&quot;foo&quot;</span>,
- <span class="s2">&quot;json_class&quot;</span>:<span class="s2">&quot;Chef::DataBag&quot;</span>,
- <span class="s2">&quot;chef_type&quot;</span>:<span class="s2">&quot;data_bag&quot;</span>
-<span class="o">}</span>
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_recipe_list.html b/distro/common/html/knife_recipe_list.html
deleted file mode 100644
index bc527fc570..0000000000
--- a/distro/common/html/knife_recipe_list.html
+++ /dev/null
@@ -1,91 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife recipe list &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-recipe-list">
-<h1>knife recipe list<a class="headerlink" href="#knife-recipe-list" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife recipe list</strong> subcommand is used to view all of the recipes that are on a Chef server. A regular expression can be used to limit the results to recipes that match a specific pattern. The regular expression must be within quotes and not be surrounded by forward slashes (/).</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife recipe list REGEX
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>View a list of recipes</strong></p>
-<p>To view a list of recipes:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife recipe list <span class="s1">&#39;couchdb::*&#39;</span>
-</pre></div>
-</div>
-<p>to return:</p>
-<div class="highlight-bash"><div class="highlight"><pre>couchdb::main_monitors
-couchdb::master
-couchdb::default
-couchdb::org_cleanu
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_role.html b/distro/common/html/knife_role.html
deleted file mode 100644
index 844de48d37..0000000000
--- a/distro/common/html/knife_role.html
+++ /dev/null
@@ -1,295 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife role &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-role">
-<h1>knife role<a class="headerlink" href="#knife-role" title="Permalink to this headline">¶</a></h1>
-<p>A role is a way to define certain patterns and processes that exist across nodes in an organization as belonging to a single job function. Each role consists of zero (or more) attributes and a run-list. Each node can have zero (or more) roles assigned to it. When a role is run against a node, the configuration details of that node are compared against the attributes of the role, and then the contents of that role&#8217;s run-list are applied to the node&#8217;s configuration details. When a chef-client runs, it merges its own attributes and run-lists with those contained within each assigned role.</p>
-<p>The <strong>knife role</strong> subcommand is used to manage the roles that are associated with one or more nodes on a Chef server.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">To add a role to a node and then build out the run-list for that node, use the <a class="reference internal" href="knife_node.html"><em>knife node</em></a> sub-command and its <tt class="docutils literal"><span class="pre">run_list</span> <span class="pre">add</span></tt> argument.</p>
-</div>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<div class="section" id="bulk-delete">
-<h2>bulk delete<a class="headerlink" href="#bulk-delete" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">bulk</span> <span class="pre">delete</span></tt> argument is used to delete one or more roles that match a pattern defined by a regular expression. The regular expression must be within quotes and not be surrounded by forward slashes (/).</p>
-<div class="section" id="syntax">
-<h3>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role bulk delete REGEX
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h3>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="examples">
-<h3>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Bulk delete roles</strong></p>
-<p>Use a regular expression to define the pattern used to bulk delete roles:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role bulk delete <span class="s2">&quot;^[0-9]{3}$&quot;</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="create">
-<h2>create<a class="headerlink" href="#create" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">create</span></tt> argument is used to add a role to the Chef server. Role data is saved as JSON on the Chef server.</p>
-<div class="section" id="id1">
-<h3>Syntax<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role create ROLE_NAME <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id2">
-<h3>Options<a class="headerlink" href="#id2" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-d</span> <span class="pre">DESCRIPTION</span></tt>, <tt class="docutils literal"><span class="pre">--description</span> <span class="pre">DESCRIPTION</span></tt></dt>
-<dd>The description of the role. This value will populate the description field for the role on the Chef server.</dd>
-</dl>
-</div>
-<div class="section" id="id3">
-<h3>Examples<a class="headerlink" href="#id3" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Create a role</strong></p>
-<p>To add a role named <tt class="docutils literal"><span class="pre">role1</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role create role1
-</pre></div>
-</div>
-<p>In the $EDITOR enter the role data in JSON:</p>
-<div class="highlight-javascript"><div class="highlight"><pre>## sample:
-{
- &quot;name&quot;: &quot;role1&quot;,
- &quot;default_attributes&quot;: {
- },
- &quot;json_class&quot;: &quot;Chef::Role&quot;,
- &quot;run_list&quot;: [&#39;recipe[cookbook_name::recipe_name],
- role[role_name]&#39;
- ],
- &quot;description&quot;: &quot;&quot;,
- &quot;chef_type&quot;: &quot;role&quot;,
- &quot;override_attributes&quot;: {
- }
-}
-</pre></div>
-</div>
-<p>When finished, save it.</p>
-</div>
-</div>
-<div class="section" id="delete">
-<h2>delete<a class="headerlink" href="#delete" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">delete</span></tt> argument is used to delete a role from the Chef server.</p>
-<div class="section" id="id4">
-<h3>Syntax<a class="headerlink" href="#id4" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role delete ROLE_NAME
-</pre></div>
-</div>
-</div>
-<div class="section" id="id5">
-<h3>Options<a class="headerlink" href="#id5" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id6">
-<h3>Examples<a class="headerlink" href="#id6" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Delete a role</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role delete devops
-</pre></div>
-</div>
-<p>Type <tt class="docutils literal"><span class="pre">Y</span></tt> to confirm a deletion.</p>
-</div>
-</div>
-<div class="section" id="edit">
-<h2>edit<a class="headerlink" href="#edit" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">edit</span></tt> argument is used to edit role details on the Chef server.</p>
-<div class="section" id="id7">
-<h3>Syntax<a class="headerlink" href="#id7" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role edit ROLE_NAME
-</pre></div>
-</div>
-</div>
-<div class="section" id="id8">
-<h3>Options<a class="headerlink" href="#id8" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id9">
-<h3>Examples<a class="headerlink" href="#id9" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Edit a role</strong></p>
-<p>To edit the data for a role named <tt class="docutils literal"><span class="pre">role1</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role edit role1
-</pre></div>
-</div>
-<p>Update the role data in JSON:</p>
-<div class="highlight-javascript"><div class="highlight"><pre>## sample:
-{
- &quot;name&quot;: &quot;role1&quot;,
- &quot;default_attributes&quot;: {
- },
- &quot;json_class&quot;: &quot;Chef::Role&quot;,
- &quot;run_list&quot;: [&#39;recipe[cookbook_name::recipe_name],
- role[role_name]&#39;
- ],
- &quot;description&quot;: &quot;This is the description for the role1 role.&quot;,
- &quot;chef_type&quot;: &quot;role&quot;,
- &quot;override_attributes&quot;: {
- }
-}
-</pre></div>
-</div>
-<p>When finished, save it.</p>
-</div>
-</div>
-<div class="section" id="from-file">
-<h2>from file<a class="headerlink" href="#from-file" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">from</span> <span class="pre">file</span></tt> argument is used to create a role using existing JSON data as a template.</p>
-<div class="section" id="id10">
-<h3>Syntax<a class="headerlink" href="#id10" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role from file FILE
-</pre></div>
-</div>
-</div>
-<div class="section" id="id11">
-<h3>Options<a class="headerlink" href="#id11" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id12">
-<h3>Examples<a class="headerlink" href="#id12" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Create a role using JSON data</strong></p>
-<p>To view role details based on the values contained in a JSON file:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role from file <span class="s2">&quot;path to JSON file&quot;</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="list">
-<h2>list<a class="headerlink" href="#list" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">list</span></tt> argument is used to view a list of roles that are currently available on the Chef server.</p>
-<div class="section" id="id13">
-<h3>Syntax<a class="headerlink" href="#id13" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role list
-</pre></div>
-</div>
-</div>
-<div class="section" id="id14">
-<h3>Options<a class="headerlink" href="#id14" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-w</span></tt>, <tt class="docutils literal"><span class="pre">--with-uri</span></tt></dt>
-<dd>Use to show the corresponding URIs.</dd>
-</dl>
-</div>
-<div class="section" id="id15">
-<h3>Examples<a class="headerlink" href="#id15" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>View a list of roles</strong></p>
-<p>To view a list of roles on the Chef server and display the URI for each role returned, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role list -w
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="show">
-<h2>show<a class="headerlink" href="#show" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">show</span></tt> argument is used to view the details of a role.</p>
-<div class="section" id="id16">
-<h3>Syntax<a class="headerlink" href="#id16" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role show ROLE_NAME
-</pre></div>
-</div>
-</div>
-<div class="section" id="id17">
-<h3>Options<a class="headerlink" href="#id17" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span> <span class="pre">ATTR</span></tt>, <tt class="docutils literal"><span class="pre">--attribute</span> <span class="pre">ATTR</span></tt></dt>
-<dd>The attribute (or attributes) to show.</dd>
-</dl>
-</div>
-<div class="section" id="id18">
-<h3>Examples<a class="headerlink" href="#id18" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Show as JSON data</strong></p>
-<p>To view information in JSON format, use the <tt class="docutils literal"><span class="pre">-F</span></tt> common option as part of the command like this:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife role show devops -F json
-</pre></div>
-</div>
-<p>Other formats available include <tt class="docutils literal"><span class="pre">text</span></tt>, <tt class="docutils literal"><span class="pre">yaml</span></tt>, and <tt class="docutils literal"><span class="pre">pp</span></tt>.</p>
-<p><strong>Show as raw JSON data</strong></p>
-<p>To view node information in raw JSON, use the <tt class="docutils literal"><span class="pre">-l</span></tt> or <tt class="docutils literal"><span class="pre">--long</span></tt> option:</p>
-<div class="highlight-bash"><div class="highlight"><pre>knife node show -l -F json &lt;node_name&gt;
-</pre></div>
-</div>
-<p>and/or:</p>
-<div class="highlight-bash"><div class="highlight"><pre>knife node show -l --format<span class="o">=</span>json &lt;node_name&gt;
-</pre></div>
-</div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_search.html b/distro/common/html/knife_search.html
deleted file mode 100644
index c5006d155e..0000000000
--- a/distro/common/html/knife_search.html
+++ /dev/null
@@ -1,203 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife search &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-search">
-<h1>knife search<a class="headerlink" href="#knife-search" title="Permalink to this headline">¶</a></h1>
-<p>Search indexes allow queries to be made for any type of data that is indexed by the Chef server, including data bags (and data bag items), environments, nodes, and roles. A defined query syntax is used to support search patterns like exact, wildcard, range, and fuzzy. A search is a full-text query that can be done from several locations, including from within a recipe, by using the <tt class="docutils literal"><span class="pre">search</span></tt> subcommand in knife, the <tt class="docutils literal"><span class="pre">search</span></tt> method in the Recipe DSL, and by using the <tt class="docutils literal"><span class="pre">/search</span></tt> or <tt class="docutils literal"><span class="pre">/search/INDEX</span></tt> endpoints in the Chef server API. The search engine is based on Apache Solr and is run from the Chef server.</p>
-<p>The <strong>knife search</strong> subcommand is used run a search query for information that is indexed on a Chef server.</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife search INDEX SEARCH_QUERY
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">INDEX</span></tt> is one of <tt class="docutils literal"><span class="pre">client</span></tt>, <tt class="docutils literal"><span class="pre">environment</span></tt>, <tt class="docutils literal"><span class="pre">node</span></tt>, <tt class="docutils literal"><span class="pre">role</span></tt>, or the name of a data bag and <tt class="docutils literal"><span class="pre">SEARCH_QUERY</span></tt> is the search query syntax for the query that will be executed.</p>
-<p><tt class="docutils literal"><span class="pre">INDEX</span></tt> is implied if omitted, and will default to <tt class="docutils literal"><span class="pre">node</span></tt>. For example:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife search <span class="s1">&#39;*:*&#39;</span> -i
-</pre></div>
-</div>
-<p>will return something similar to:</p>
-<div class="highlight-bash"><div class="highlight"><pre>8 items found
-
-centos-62-dev
-opensuse-1203
-ubuntu-1304-dev
-ubuntu-1304-orgtest
-ubuntu-1204-ohai-test
-ubuntu-1304-ifcfg-test
-ohai-test
-win2k8-dev
-</pre></div>
-</div>
-<p>and is the same search as:</p>
-<div class="highlight-bash"><div class="highlight"><pre>$ knife search node &#39;*:*&quot; -i
-</pre></div>
-</div>
-<p>If the <tt class="docutils literal"><span class="pre">SEARCH_QUERY</span></tt> does not contain a colon character (<tt class="docutils literal"><span class="pre">:</span></tt>), then the default query pattern is <tt class="docutils literal"><span class="pre">tags:*#{&#64;query}*</span> <span class="pre">OR</span> <span class="pre">roles:*#{&#64;query}*</span> <span class="pre">OR</span> <span class="pre">fqdn:*#{&#64;query}*</span> <span class="pre">OR</span> <span class="pre">addresses:*#{&#64;query}*</span></tt>, which means the following two search queries are effectively the same:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife search ubuntu
-</pre></div>
-</div>
-<p>or:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife search node <span class="s2">&quot;tags:*ubuntu* OR roles:*ubuntu* OR fqdn:*ubuntu* (etc.)&quot;</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This sub-command has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span> <span class="pre">ATTR</span></tt>, <tt class="docutils literal"><span class="pre">--attribute</span> <span class="pre">ATTR</span></tt></dt>
-<dd>The attribute (or attributes) to show.</dd>
-<dt><tt class="docutils literal"><span class="pre">-b</span> <span class="pre">ROW</span></tt>, <tt class="docutils literal"><span class="pre">--start</span> <span class="pre">ROW</span></tt></dt>
-<dd>The row at which return results will begin.</dd>
-<dt><tt class="docutils literal"><span class="pre">-i</span></tt>, <tt class="docutils literal"><span class="pre">--id-only</span></tt></dt>
-<dd>Use to show only matching object IDs.</dd>
-<dt><tt class="docutils literal"><span class="pre">INDEX</span></tt></dt>
-<dd>The name of the index to be queried: <tt class="docutils literal"><span class="pre">client</span></tt>, <tt class="docutils literal"><span class="pre">environment</span></tt>, <tt class="docutils literal"><span class="pre">node</span></tt>, <tt class="docutils literal"><span class="pre">role</span></tt>, or <tt class="docutils literal"><span class="pre">DATA_BAG_NAME</span></tt>. Default index: <tt class="docutils literal"><span class="pre">node</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-l</span></tt>, <tt class="docutils literal"><span class="pre">--long</span></tt></dt>
-<dd>Use to display all attributes in the output and to show the output as JSON.</dd>
-<dt><tt class="docutils literal"><span class="pre">-m</span></tt>, <tt class="docutils literal"><span class="pre">--medium</span></tt></dt>
-<dd>Use to display normal attributes in the output and to show the output as JSON.</dd>
-<dt><tt class="docutils literal"><span class="pre">-o</span> <span class="pre">SORT</span></tt>, <tt class="docutils literal"><span class="pre">--sort</span> <span class="pre">SORT</span></tt></dt>
-<dd>The order in which search results will be sorted.</dd>
-<dt><tt class="docutils literal"><span class="pre">-q</span> <span class="pre">SEARCH_QUERY</span></tt>, <tt class="docutils literal"><span class="pre">--query</span> <span class="pre">SEARCH_QUERY</span></tt></dt>
-<dd>Use to protect search queries that start with a hyphen (-). A <tt class="docutils literal"><span class="pre">-q</span></tt> query may be specified as an argument or an option, but not both.</dd>
-<dt><tt class="docutils literal"><span class="pre">-r</span></tt>, <tt class="docutils literal"><span class="pre">--run-list</span></tt></dt>
-<dd>Use to show only the run-list.</dd>
-<dt><tt class="docutils literal"><span class="pre">-R</span> <span class="pre">INT</span></tt>, <tt class="docutils literal"><span class="pre">--rows</span> <span class="pre">INT</span></tt></dt>
-<dd>The number of rows to be returned.</dd>
-<dt><tt class="docutils literal"><span class="pre">SEARCH_QUERY</span></tt></dt>
-<dd>The search query used to identify a a list of items on a Chef server. This option uses the same syntax as the <tt class="docutils literal"><span class="pre">search</span></tt> sub-command.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Search by platform ID</strong></p>
-<p>To search for the IDs of all nodes running on the Amazon EC2 platform, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife search node <span class="s1">&#39;ec2:*&#39;</span> -i
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>4 items found
-
-ip-0A7CA19F.ec2.internal
-
-ip-0A58CF8E.ec2.internal
-
-ip-0A58E134.ec2.internal
-
-ip-0A7CFFD5.ec2.internal
-</pre></div>
-</div>
-<p><strong>Search by instance type</strong></p>
-<p>To search for the instance type (flavor) of all nodes running on the Amazon EC2 platform, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife search node <span class="s1">&#39;ec2:*&#39;</span> -a ec2.instance_type
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>4 items found
-
-ec2.instance_type: m1.large
-id: ip-0A7CA19F.ec2.internal
-
-ec2.instance_type: m1.large
-id: ip-0A58CF8E.ec2.internal
-
-ec2.instance_type: m1.large
-id: ip-0A58E134.ec2.internal
-
-ec2.instance_type: m1.large
-id: ip-0A7CFFD5.ec2.internal
-</pre></div>
-</div>
-<p><strong>Search by node</strong></p>
-<p>To search for all nodes running Ubuntu, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife search node <span class="s1">&#39;platform:ubuntu&#39;</span>
-</pre></div>
-</div>
-<p><strong>Search by node and environment</strong></p>
-<p>To search for all nodes running CentOS in the production environment, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife search node <span class="s1">&#39;chef_environment:production AND platform:centos&#39;</span>
-</pre></div>
-</div>
-<p><strong>Search for nested attributes</strong></p>
-<p>To find a nested attribute, use a pattern similar to the following:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife search node &lt;query_to_run&gt; -a &lt;main_attribute&gt;.&lt;nested_attribute&gt;
-</pre></div>
-</div>
-<p><strong>Search for multiple attributes</strong></p>
-<p>To build a search query to use more than one attribute, use an underscore (<tt class="docutils literal"><span class="pre">_</span></tt>) to separate each attribute. For example, the following query will search for all nodes running a specific version of Ruby:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife search node <span class="s2">&quot;languages_ruby_version:1.9.3&quot;</span>
-</pre></div>
-</div>
-<p><strong>Search for nested attributes using a search query</strong></p>
-<p>To build a search query that can find a nested attribute:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife search node name:&lt;node_name&gt; -a kernel.machine
-</pre></div>
-</div>
-<p><strong>Use a test query</strong></p>
-<p>To test a search query that will be used in a <strong>knife ssh</strong> command:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife search node <span class="s2">&quot;role:web NOT name:web03&quot;</span>
-</pre></div>
-</div>
-<p>where the query in the previous example will search all servers that have the <tt class="docutils literal"><span class="pre">web</span></tt> role, but not on the server named <tt class="docutils literal"><span class="pre">web03</span></tt>.</p>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_serve.html b/distro/common/html/knife_serve.html
deleted file mode 100644
index 9695d8d2bd..0000000000
--- a/distro/common/html/knife_serve.html
+++ /dev/null
@@ -1,79 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife serve &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-serve">
-<h1>knife serve<a class="headerlink" href="#knife-serve" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife serve</strong> subcommand is used to run a persistent chef-zero against the local chef-repo. (chef-zero is a lightweight Chef server that runs in-memory on the local machine.) This is the same as running the chef-client executable with the <tt class="docutils literal"><span class="pre">--local-mode</span></tt> option. The <tt class="docutils literal"><span class="pre">chef_repo_path</span></tt> is located automatically and the Chef server will bind to the first available port between <tt class="docutils literal"><span class="pre">8889</span></tt> and <tt class="docutils literal"><span class="pre">9999</span></tt>. <strong>knife serve</strong> will print the URL for the local Chef server, so that it may be added to the knife.rb file.</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife serve <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>None.</p>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_show.html b/distro/common/html/knife_show.html
deleted file mode 100644
index baae300399..0000000000
--- a/distro/common/html/knife_show.html
+++ /dev/null
@@ -1,104 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife show &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-show">
-<h1>knife show<a class="headerlink" href="#knife-show" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife show</strong> subcommand is used to view the details of one (or more) objects on the Chef server. This subcommand works similar to <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">cookbook</span> <span class="pre">show</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">data</span> <span class="pre">bag</span> <span class="pre">show</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">environment</span> <span class="pre">show</span></tt>, <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">node</span> <span class="pre">show</span></tt>, and <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">role</span> <span class="pre">show</span></tt>, but with a single verb (and a single action).</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife show <span class="o">[</span>PATTERN...<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span> <span class="pre">ATTR</span></tt>, <tt class="docutils literal"><span class="pre">--attribute</span> <span class="pre">ATTR</span></tt></dt>
-<dd>The attribute (or attributes) to show.</dd>
-<dt><tt class="docutils literal"><span class="pre">--chef-repo-path</span> <span class="pre">PATH</span></tt></dt>
-<dd>The path to the chef-repo. This setting will override the default path to the chef-repo. Default: same value as specified by <tt class="docutils literal"><span class="pre">chef_repo_path</span></tt> in client.rb.</dd>
-<dt><tt class="docutils literal"><span class="pre">--concurrency</span></tt></dt>
-<dd>The number of allowed concurrent connections. Default: <tt class="docutils literal"><span class="pre">10</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--local</span></tt></dt>
-<dd>Use to show local files instead of remote files.</dd>
-<dt><tt class="docutils literal"><span class="pre">--repo-mode</span> <span class="pre">MODE</span></tt></dt>
-<dd>The layout of the local chef-repo. Possible values: <tt class="docutils literal"><span class="pre">static</span></tt>, <tt class="docutils literal"><span class="pre">everything</span></tt>, or <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>. Use <tt class="docutils literal"><span class="pre">static</span></tt> for just roles, environments, cookbooks, and data bags. By default, <tt class="docutils literal"><span class="pre">everything</span></tt> and <tt class="docutils literal"><span class="pre">hosted_everything</span></tt> are dynamically selected depending on the server type. Default: <tt class="docutils literal"><span class="pre">everything</span></tt> / <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Show all cookbooks</strong></p>
-<p>To show all cookbooks in the <tt class="docutils literal"><span class="pre">cookbooks/</span></tt> directory:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife show cookbooks/
-</pre></div>
-</div>
-<p>or, (if already in the <tt class="docutils literal"><span class="pre">cookbooks/</span></tt> directory in the local chef-repo):</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife show
-</pre></div>
-</div>
-<p><strong>Show roles and environments</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife show roles/ environments/
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_ssh.html b/distro/common/html/knife_ssh.html
deleted file mode 100644
index 66a375186a..0000000000
--- a/distro/common/html/knife_ssh.html
+++ /dev/null
@@ -1,177 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife ssh &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-ssh">
-<h1>knife ssh<a class="headerlink" href="#knife-ssh" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife ssh</strong> subcommand is used to invoke SSH commands (in parallel) on a subset of nodes within an organization, based on the results of a <a class="reference external" href="http://docs.opscode.com/essentials_search.html">search query</a> made to the Chef server.</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssh SEARCH_QUERY SSH_COMMAND <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span> <span class="pre">SSH_ATTR</span></tt>, <tt class="docutils literal"><span class="pre">--attribute</span> <span class="pre">SSH_ATTR</span></tt></dt>
-<dd>The attribute that is used when opening the SSH connection. The default attribute is the FQDN of the host. Other possible values include a public IP address, a private IP address, or a hostname.</dd>
-<dt><tt class="docutils literal"><span class="pre">-A</span></tt>, <tt class="docutils literal"><span class="pre">--forward-agent</span></tt></dt>
-<dd>Use to enable SSH agent forwarding.</dd>
-<dt><tt class="docutils literal"><span class="pre">-C</span> <span class="pre">NUM</span></tt>, <tt class="docutils literal"><span class="pre">--concurrency</span> <span class="pre">NUM</span></tt></dt>
-<dd>The number of allowed concurrent connections.</dd>
-<dt><tt class="docutils literal"><span class="pre">-G</span> <span class="pre">GATEWAY</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-gateway</span> <span class="pre">GATEWAY</span></tt></dt>
-<dd>The SSH tunnel or gateway that is used to run a bootstrap action on a machine that is not accessible from the workstation.</dd>
-<dt><tt class="docutils literal"><span class="pre">-i</span> <span class="pre">IDENTITY_FILE</span></tt>, <tt class="docutils literal"><span class="pre">--identity-file</span> <span class="pre">IDENTIFY_FILE</span></tt></dt>
-<dd>The SSH identity file used for authentication. Key-based authentication is recommended.</dd>
-<dt><tt class="docutils literal"><span class="pre">-m</span></tt>, <tt class="docutils literal"><span class="pre">--manual-list</span></tt></dt>
-<dd>Use to define a search query as a space-separated list of servers. If there is more than one item in the list, put quotes around the entire list. For example: <tt class="docutils literal"><span class="pre">--manual-list</span> <span class="pre">&quot;server01</span> <span class="pre">server</span> <span class="pre">02</span> <span class="pre">server</span> <span class="pre">03&quot;</span></tt></dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]host-key-verify</span></tt></dt>
-<dd>Use <tt class="docutils literal"><span class="pre">--no-host-key-verify</span></tt> to disable host key verification. Default setting: <tt class="docutils literal"><span class="pre">--host-key-verify</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">OTHER</span></tt></dt>
-<dd>The shell type. Possible values: <tt class="docutils literal"><span class="pre">interactive</span></tt>, <tt class="docutils literal"><span class="pre">screen</span></tt>, <tt class="docutils literal"><span class="pre">tmux</span></tt>, <tt class="docutils literal"><span class="pre">macterm</span></tt>, or <tt class="docutils literal"><span class="pre">cssh</span></tt>. (<tt class="docutils literal"><span class="pre">csshx</span></tt> is deprecated in favor of <tt class="docutils literal"><span class="pre">cssh</span></tt>.)</dd>
-<dt><tt class="docutils literal"><span class="pre">-p</span> <span class="pre">PORT</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-port</span> <span class="pre">PORT</span></tt></dt>
-<dd>The SSH port.</dd>
-<dt><tt class="docutils literal"><span class="pre">-P</span> <span class="pre">PASSWORD</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-password</span> <span class="pre">PASSWORD</span></tt></dt>
-<dd>The SSH password. This can be used to pass the password directly on the command line. If this option is not specified (and a password is required) knife will prompt for the password.</dd>
-<dt><tt class="docutils literal"><span class="pre">SEARCH_QUERY</span></tt></dt>
-<dd>The search query used to return a list of servers to be accessed using SSH and the specified <tt class="docutils literal"><span class="pre">SSH_COMMAND</span></tt>. This option uses the same syntax as the search sub-command.</dd>
-<dt><tt class="docutils literal"><span class="pre">SSH_COMMAND</span></tt></dt>
-<dd>The command that will be run against the results of a search query.</dd>
-<dt><tt class="docutils literal"><span class="pre">-x</span> <span class="pre">USER_NAME</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-user</span> <span class="pre">USER_NAME</span></tt></dt>
-<dd>The SSH user name.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Find server uptime</strong></p>
-<p>To find the uptime of all of web servers running Ubuntu on the Amazon EC2 platform, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssh <span class="s2">&quot;role:web&quot;</span> <span class="s2">&quot;uptime&quot;</span> -x ubuntu -a ec2.public_hostname
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>ec2-174-129-127-206.compute-1.amazonaws.com 13:50:47 up 1 day, 23:26, 1 user, load average: 0.25, 0.18, 0.11
-ec2-67-202-63-102.compute-1.amazonaws.com 13:50:47 up 1 day, 23:33, 1 user, load average: 0.12, 0.13, 0.10
-ec2-184-73-9-250.compute-1.amazonaws.com 13:50:48 up 16:45, 1 user, load average: 0.30, 0.22, 0.13
-ec2-75-101-240-230.compute-1.amazonaws.com 13:50:48 up 1 day, 22:59, 1 user, load average: 0.24, 0.17, 0.11
-ec2-184-73-60-141.compute-1.amazonaws.com 13:50:48 up 1 day, 23:30, 1 user, load average: 0.32, 0.17, 0.15
-</pre></div>
-</div>
-<p><strong>Run the chef-client on all nodes</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssh <span class="s1">&#39;name:*&#39;</span> <span class="s1">&#39;sudo chef-client&#39;</span>
-</pre></div>
-</div>
-<p><strong>Force a chef-client run</strong></p>
-<p>To force a chef-client run on all of the web servers running Ubuntu on the Amazon EC2 platform, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssh <span class="s2">&quot;role:web&quot;</span> <span class="s2">&quot;sudo chef-client&quot;</span> -x ubuntu -a ec2.public_hostname
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>ec2-67-202-63-102.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:37 +0000<span class="o">]</span> INFO: Starting Chef Run <span class="o">(</span>Version 0.9.10<span class="o">)</span>
-ec2-174-129-127-206.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:37 +0000<span class="o">]</span> INFO: Starting Chef Run <span class="o">(</span>Version 0.9.10<span class="o">)</span>
-ec2-184-73-9-250.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:38 +0000<span class="o">]</span> INFO: Starting Chef Run <span class="o">(</span>Version 0.9.10<span class="o">)</span>
-ec2-75-101-240-230.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:38 +0000<span class="o">]</span> INFO: Starting Chef Run <span class="o">(</span>Version 0.9.10<span class="o">)</span>
-ec2-184-73-60-141.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:38 +0000<span class="o">]</span> INFO: Starting Chef Run <span class="o">(</span>Version 0.9.10<span class="o">)</span>
-ec2-174-129-127-206.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:39 +0000<span class="o">]</span> INFO: Chef Run <span class="nb">complete </span>in 1.419243 seconds
-ec2-174-129-127-206.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:39 +0000<span class="o">]</span> INFO: cleaning the checksum cache
-ec2-174-129-127-206.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:39 +0000<span class="o">]</span> INFO: Running report handlers
-ec2-174-129-127-206.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:39 +0000<span class="o">]</span> INFO: Report handlers <span class="nb">complete</span>
-ec2-67-202-63-102.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:39 +0000<span class="o">]</span> INFO: Chef Run <span class="nb">complete </span>in 1.578265 seconds
-ec2-67-202-63-102.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:39 +0000<span class="o">]</span> INFO: cleaning the checksum cache
-ec2-67-202-63-102.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:39 +0000<span class="o">]</span> INFO: Running report handlers
-ec2-67-202-63-102.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:39 +0000<span class="o">]</span> INFO: Report handlers <span class="nb">complete</span>
-ec2-184-73-9-250.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:40 +0000<span class="o">]</span> INFO: Chef Run <span class="nb">complete </span>in 1.638884 seconds
-ec2-184-73-9-250.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:40 +0000<span class="o">]</span> INFO: cleaning the checksum cache
-ec2-184-73-9-250.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:40 +0000<span class="o">]</span> INFO: Running report handlers
-ec2-184-73-9-250.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:40 +0000<span class="o">]</span> INFO: Report handlers <span class="nb">complete</span>
-ec2-75-101-240-230.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:40 +0000<span class="o">]</span> INFO: Chef Run <span class="nb">complete </span>in 1.540257 seconds
-ec2-75-101-240-230.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:40 +0000<span class="o">]</span> INFO: cleaning the checksum cache
-ec2-75-101-240-230.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:40 +0000<span class="o">]</span> INFO: Running report handlers
-ec2-75-101-240-230.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:40 +0000<span class="o">]</span> INFO: Report handlers <span class="nb">complete</span>
-ec2-184-73-60-141.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:40 +0000<span class="o">]</span> INFO: Chef Run <span class="nb">complete </span>in 1.502489 seconds
-ec2-184-73-60-141.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:40 +0000<span class="o">]</span> INFO: cleaning the checksum cache
-ec2-184-73-60-141.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:40 +0000<span class="o">]</span> INFO: Running report handlers
-ec2-184-73-60-141.compute-1.amazonaws.com <span class="o">[</span>Fri, 22 Oct 2010 14:18:40 +0000<span class="o">]</span> INFO: Report handlers <span class="nb">complete</span>
-</pre></div>
-</div>
-<p><strong>Run a command based on search query</strong></p>
-<p>To query for all nodes that have the <tt class="docutils literal"><span class="pre">webserver</span></tt> role and then use SSH to run the command <tt class="docutils literal"><span class="pre">sudo</span> <span class="pre">chef-client</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssh <span class="s2">&quot;role:webserver&quot;</span> <span class="s2">&quot;sudo chef-client&quot;</span>
-</pre></div>
-</div>
-<p><strong>Upgrade all nodes</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssh name:* <span class="s2">&quot;sudo aptitude upgrade -y&quot;</span>
-</pre></div>
-</div>
-<p><strong>Specify the shell type</strong></p>
-<p>To specify the shell type used on the nodes returned by a search query:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssh roles:opscode-omnitruck macterm
-</pre></div>
-</div>
-<p>where <tt class="docutils literal"><span class="pre">screen</span></tt> is one of the following values: <tt class="docutils literal"><span class="pre">cssh</span></tt>, <tt class="docutils literal"><span class="pre">interactive</span></tt>, <tt class="docutils literal"><span class="pre">macterm</span></tt>, <tt class="docutils literal"><span class="pre">screen</span></tt>, or <tt class="docutils literal"><span class="pre">tmux</span></tt>. If the node does not have the shell type installed, knife will return an error similar to the following:</p>
-<div class="highlight-bash"><div class="highlight"><pre>you need the rb-appscript gem to use knife ssh macterm.
-<span class="sb">`</span><span class="o">(</span>sudo<span class="o">)</span> gem install rb-appscript<span class="sb">`</span> to install
-ERROR: LoadError: cannot load such file -- appscript
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_ssl_check.html b/distro/common/html/knife_ssl_check.html
deleted file mode 100644
index 015647b4ad..0000000000
--- a/distro/common/html/knife_ssl_check.html
+++ /dev/null
@@ -1,151 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife ssl check &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-ssl-check">
-<h1>knife ssl check<a class="headerlink" href="#knife-ssl-check" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife ssl check</strong> subcommand is used to verify the SSL configuration for the Enterprise Chef and/or Open Source Chef servers, or at another location specified by a URL or URI.</p>
-<div class="admonition warning">
-<p class="first admonition-title">Warning</p>
-<p class="last">When verification of a remote server&#8217;s SSL certificate is disabled, the chef-client will issue a warning similar to &#8220;SSL validation of HTTPS requests is disabled. HTTPS connections are still encrypted, but the chef-client is not able to detect forged replies or man-in-the-middle attacks.&#8221; To configure SSL for the chef-client, set <tt class="docutils literal"><span class="pre">ssl_verify_mode</span></tt> to <tt class="docutils literal"><span class="pre">:verify_peer</span></tt> (recommended) <strong>or</strong> <tt class="docutils literal"><span class="pre">verify_api_cert</span></tt> to <tt class="docutils literal"><span class="pre">true</span></tt> in the client.rb file.</p>
-</div>
-<p><strong>Syntax</strong></p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssl check URI
-</pre></div>
-</div>
-<p><strong>Options</strong></p>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span> <span class="pre">SSH_ATTR</span></tt>, <tt class="docutils literal"><span class="pre">--attribute</span> <span class="pre">SSH_ATTR</span></tt></dt>
-<dd>The attribute that is used when opening the SSH connection. The default attribute is the FQDN of the host. Other possible values include a public IP address, a private IP address, or a hostname.</dd>
-<dt><tt class="docutils literal"><span class="pre">-A</span></tt>, <tt class="docutils literal"><span class="pre">--forward-agent</span></tt></dt>
-<dd>Use to enable SSH agent forwarding.</dd>
-<dt><tt class="docutils literal"><span class="pre">-c</span> <span class="pre">CONFIG_FILE</span></tt>, <tt class="docutils literal"><span class="pre">--config</span> <span class="pre">CONFIG_FILE</span></tt></dt>
-<dd>The configuration file to use.</dd>
-<dt><tt class="docutils literal"><span class="pre">-C</span> <span class="pre">NUM</span></tt>, <tt class="docutils literal"><span class="pre">--concurrency</span> <span class="pre">NUM</span></tt></dt>
-<dd>The number of allowed concurrent connections.</dd>
-<dt><tt class="docutils literal"><span class="pre">--chef-zero-port</span> <span class="pre">PORT</span></tt></dt>
-<dd>The port on which chef-zero will listen.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]color</span></tt></dt>
-<dd>Use to view colored output.</dd>
-<dt><tt class="docutils literal"><span class="pre">-d</span></tt>, <tt class="docutils literal"><span class="pre">--disable-editing</span></tt></dt>
-<dd>Use to prevent the $EDITOR from being opened and to accept data as-is.</dd>
-<dt><tt class="docutils literal"><span class="pre">--defaults</span></tt></dt>
-<dd>Use to have knife use the default value instead of asking a user to provide one.</dd>
-<dt><tt class="docutils literal"><span class="pre">-e</span> <span class="pre">EDITOR</span></tt>, <tt class="docutils literal"><span class="pre">--editor</span> <span class="pre">EDITOR</span></tt></dt>
-<dd>The $EDITOR that is used for all interactive commands.</dd>
-<dt><tt class="docutils literal"><span class="pre">-E</span> <span class="pre">ENVIRONMENT</span></tt>, <tt class="docutils literal"><span class="pre">--environment</span> <span class="pre">ENVIRONMENT</span></tt></dt>
-<dd>The name of the environment. When this option is added to a command, the command will run only against the named environment.</dd>
-<dt><tt class="docutils literal"><span class="pre">-F</span> <span class="pre">FORMAT</span></tt>, <tt class="docutils literal"><span class="pre">--format</span> <span class="pre">FORMAT</span></tt></dt>
-<dd>The output format: <tt class="docutils literal"><span class="pre">summary</span></tt> (default), <tt class="docutils literal"><span class="pre">text</span></tt>, <tt class="docutils literal"><span class="pre">json</span></tt>, <tt class="docutils literal"><span class="pre">yaml</span></tt>, and <tt class="docutils literal"><span class="pre">pp</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-G</span> <span class="pre">GATEWAY</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-gateway</span> <span class="pre">GATEWAY</span></tt></dt>
-<dd>The SSH tunnel or gateway that is used to run a bootstrap action on a machine that is not accessible from the workstation.</dd>
-<dt><tt class="docutils literal"><span class="pre">-h</span></tt>, <tt class="docutils literal"><span class="pre">--help</span></tt></dt>
-<dd>Shows help for the command.</dd>
-<dt><tt class="docutils literal"><span class="pre">-i</span> <span class="pre">IDENTITY_FILE</span></tt>, <tt class="docutils literal"><span class="pre">--identity-file</span> <span class="pre">IDENTIFY_FILE</span></tt></dt>
-<dd>The SSH identity file used for authentication. Key-based authentication is recommended.</dd>
-<dt><tt class="docutils literal"><span class="pre">-k</span> <span class="pre">KEY</span></tt>, <tt class="docutils literal"><span class="pre">--key</span> <span class="pre">KEY</span></tt></dt>
-<dd>The private key that knife will use to sign requests made by the API client to the Chef server.</dd>
-<dt><tt class="docutils literal"><span class="pre">-m</span></tt>, <tt class="docutils literal"><span class="pre">--manual-list</span></tt></dt>
-<dd>Use to define a search query as a space-separated list of servers. If there is more than one item in the list, put quotes around the entire list. For example: <tt class="docutils literal"><span class="pre">--manual-list</span> <span class="pre">&quot;server01</span> <span class="pre">server</span> <span class="pre">02</span> <span class="pre">server</span> <span class="pre">03&quot;</span></tt></dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]host-key-verify</span></tt></dt>
-<dd>Use <tt class="docutils literal"><span class="pre">--no-host-key-verify</span></tt> to disable host key verification. Default setting: <tt class="docutils literal"><span class="pre">--host-key-verify</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">OTHER</span></tt></dt>
-<dd>The shell type. Possible values: <tt class="docutils literal"><span class="pre">interactive</span></tt>, <tt class="docutils literal"><span class="pre">screen</span></tt>, <tt class="docutils literal"><span class="pre">tmux</span></tt>, <tt class="docutils literal"><span class="pre">macterm</span></tt>, or <tt class="docutils literal"><span class="pre">cssh</span></tt>. (<tt class="docutils literal"><span class="pre">csshx</span></tt> is deprecated in favor of <tt class="docutils literal"><span class="pre">cssh</span></tt>.)</dd>
-<dt><tt class="docutils literal"><span class="pre">-p</span> <span class="pre">PORT</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-port</span> <span class="pre">PORT</span></tt></dt>
-<dd>The SSH port.</dd>
-<dt><tt class="docutils literal"><span class="pre">-P</span> <span class="pre">PASSWORD</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-password</span> <span class="pre">PASSWORD</span></tt></dt>
-<dd>The SSH password. This can be used to pass the password directly on the command line. If this option is not specified (and a password is required) knife will prompt for the password.</dd>
-<dt><tt class="docutils literal"><span class="pre">--print-after</span></tt></dt>
-<dd>Use to show data after a destructive operation.</dd>
-<dt><tt class="docutils literal"><span class="pre">-s</span> <span class="pre">URL</span></tt>, <tt class="docutils literal"><span class="pre">--server-url</span> <span class="pre">URL</span></tt></dt>
-<dd>The URL for the Chef server.</dd>
-<dt><tt class="docutils literal"><span class="pre">SEARCH_QUERY</span></tt></dt>
-<dd>The search query used to return a list of servers to be accessed using SSH and the specified <tt class="docutils literal"><span class="pre">SSH_COMMAND</span></tt>. This option uses the same syntax as the search sub-command.</dd>
-<dt><tt class="docutils literal"><span class="pre">SSH_COMMAND</span></tt></dt>
-<dd>The command that will be run against the results of a search query.</dd>
-<dt><tt class="docutils literal"><span class="pre">-u</span> <span class="pre">USER</span></tt>, <tt class="docutils literal"><span class="pre">--user</span> <span class="pre">USER</span></tt></dt>
-<dd>The user name used by knife to sign requests made by the API client to the Chef server. Authentication will fail if the user name does not match the private key.</dd>
-<dt><tt class="docutils literal"><span class="pre">-v</span></tt>, <tt class="docutils literal"><span class="pre">--version</span></tt></dt>
-<dd>The version of the chef-client.</dd>
-<dt><tt class="docutils literal"><span class="pre">-V</span></tt>, <tt class="docutils literal"><span class="pre">--verbose</span></tt></dt>
-<dd>Set for more verbose outputs. Use <tt class="docutils literal"><span class="pre">-VV</span></tt> for maximum verbosity.</dd>
-<dt><tt class="docutils literal"><span class="pre">-x</span> <span class="pre">USER_NAME</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-user</span> <span class="pre">USER_NAME</span></tt></dt>
-<dd>The SSH user name.</dd>
-<dt><tt class="docutils literal"><span class="pre">-y</span></tt>, <tt class="docutils literal"><span class="pre">--yes</span></tt></dt>
-<dd>Use to respond to all confirmation prompts with &#8220;Yes&#8221;. knife will not ask for confirmation.</dd>
-<dt><tt class="docutils literal"><span class="pre">-z</span></tt>, <tt class="docutils literal"><span class="pre">--local-mode</span></tt></dt>
-<dd>Use to run the chef-client in local mode. This allows all commands that work against the Chef server to also work against the local chef-repo.</dd>
-</dl>
-<p><strong>Examples</strong></p>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Verify the SSL configuration for the Chef server</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssl check
-</pre></div>
-</div>
-<p><strong>Verify the SSL configuration for the chef-client</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssl check -c /etc/chef/client.rb
-</pre></div>
-</div>
-<p><strong>Verify an external server&#8217;s SSL certificate</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssl check URL_or_URI
-</pre></div>
-</div>
-<p>for example:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssl check https://www.getchef.com
-</pre></div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_ssl_fetch.html b/distro/common/html/knife_ssl_fetch.html
deleted file mode 100644
index 94a3511cfa..0000000000
--- a/distro/common/html/knife_ssl_fetch.html
+++ /dev/null
@@ -1,151 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife ssl fetch &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-ssl-fetch">
-<h1>knife ssl fetch<a class="headerlink" href="#knife-ssl-fetch" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife ssl fetch</strong> subcommand is used to copy SSL certificates from an HTTPS server to the <tt class="docutils literal"><span class="pre">trusted_certs_dir</span></tt> directory that is used by knife and the chef-client to store trusted SSL certificates. When these certificates match the hostname of the remote server, running <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">ssl</span> <span class="pre">fetch</span></tt> is the only step required to verify a remote server that is accessed by either knife or the chef-client.</p>
-<div class="admonition warning">
-<p class="first admonition-title">Warning</p>
-<p class="last">It is the user&#8217;s responsibility to verify the authenticity of every SSL certificate before downloading it to the <tt class="docutils literal"><span class="pre">trusted_certs_dir</span></tt> directory. knife will use any certificate in that directory as if it is a 100% trusted and authentic SSL certificate. knife will not be able to determine if any certificate in this directory has been tampered with, is forged, malicious, or otherwise harmful. Therefore it is essential that users take the proper steps before downloading certificates into this directory.</p>
-</div>
-<p><strong>Syntax</strong></p>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssl fetch URI_FOR_HTTPS_SERVER
-</pre></div>
-</div>
-<p><strong>Options</strong></p>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span> <span class="pre">SSH_ATTR</span></tt>, <tt class="docutils literal"><span class="pre">--attribute</span> <span class="pre">SSH_ATTR</span></tt></dt>
-<dd>The attribute that is used when opening the SSH connection. The default attribute is the FQDN of the host. Other possible values include a public IP address, a private IP address, or a hostname.</dd>
-<dt><tt class="docutils literal"><span class="pre">-A</span></tt>, <tt class="docutils literal"><span class="pre">--forward-agent</span></tt></dt>
-<dd>Use to enable SSH agent forwarding.</dd>
-<dt><tt class="docutils literal"><span class="pre">-c</span> <span class="pre">CONFIG_FILE</span></tt>, <tt class="docutils literal"><span class="pre">--config</span> <span class="pre">CONFIG_FILE</span></tt></dt>
-<dd>The configuration file to use.</dd>
-<dt><tt class="docutils literal"><span class="pre">-C</span> <span class="pre">NUM</span></tt>, <tt class="docutils literal"><span class="pre">--concurrency</span> <span class="pre">NUM</span></tt></dt>
-<dd>The number of allowed concurrent connections.</dd>
-<dt><tt class="docutils literal"><span class="pre">--chef-zero-port</span> <span class="pre">PORT</span></tt></dt>
-<dd>The port on which chef-zero will listen.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]color</span></tt></dt>
-<dd>Use to view colored output.</dd>
-<dt><tt class="docutils literal"><span class="pre">-d</span></tt>, <tt class="docutils literal"><span class="pre">--disable-editing</span></tt></dt>
-<dd>Use to prevent the $EDITOR from being opened and to accept data as-is.</dd>
-<dt><tt class="docutils literal"><span class="pre">--defaults</span></tt></dt>
-<dd>Use to have knife use the default value instead of asking a user to provide one.</dd>
-<dt><tt class="docutils literal"><span class="pre">-e</span> <span class="pre">EDITOR</span></tt>, <tt class="docutils literal"><span class="pre">--editor</span> <span class="pre">EDITOR</span></tt></dt>
-<dd>The $EDITOR that is used for all interactive commands.</dd>
-<dt><tt class="docutils literal"><span class="pre">-E</span> <span class="pre">ENVIRONMENT</span></tt>, <tt class="docutils literal"><span class="pre">--environment</span> <span class="pre">ENVIRONMENT</span></tt></dt>
-<dd>The name of the environment. When this option is added to a command, the command will run only against the named environment.</dd>
-<dt><tt class="docutils literal"><span class="pre">-F</span> <span class="pre">FORMAT</span></tt>, <tt class="docutils literal"><span class="pre">--format</span> <span class="pre">FORMAT</span></tt></dt>
-<dd>The output format: <tt class="docutils literal"><span class="pre">summary</span></tt> (default), <tt class="docutils literal"><span class="pre">text</span></tt>, <tt class="docutils literal"><span class="pre">json</span></tt>, <tt class="docutils literal"><span class="pre">yaml</span></tt>, and <tt class="docutils literal"><span class="pre">pp</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-G</span> <span class="pre">GATEWAY</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-gateway</span> <span class="pre">GATEWAY</span></tt></dt>
-<dd>The SSH tunnel or gateway that is used to run a bootstrap action on a machine that is not accessible from the workstation.</dd>
-<dt><tt class="docutils literal"><span class="pre">-h</span></tt>, <tt class="docutils literal"><span class="pre">--help</span></tt></dt>
-<dd>Shows help for the command.</dd>
-<dt><tt class="docutils literal"><span class="pre">-i</span> <span class="pre">IDENTITY_FILE</span></tt>, <tt class="docutils literal"><span class="pre">--identity-file</span> <span class="pre">IDENTIFY_FILE</span></tt></dt>
-<dd>The SSH identity file used for authentication. Key-based authentication is recommended.</dd>
-<dt><tt class="docutils literal"><span class="pre">-k</span> <span class="pre">KEY</span></tt>, <tt class="docutils literal"><span class="pre">--key</span> <span class="pre">KEY</span></tt></dt>
-<dd>The private key that knife will use to sign requests made by the API client to the Chef server.</dd>
-<dt><tt class="docutils literal"><span class="pre">-m</span></tt>, <tt class="docutils literal"><span class="pre">--manual-list</span></tt></dt>
-<dd>Use to define a search query as a space-separated list of servers. If there is more than one item in the list, put quotes around the entire list. For example: <tt class="docutils literal"><span class="pre">--manual-list</span> <span class="pre">&quot;server01</span> <span class="pre">server</span> <span class="pre">02</span> <span class="pre">server</span> <span class="pre">03&quot;</span></tt></dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]host-key-verify</span></tt></dt>
-<dd>Use <tt class="docutils literal"><span class="pre">--no-host-key-verify</span></tt> to disable host key verification. Default setting: <tt class="docutils literal"><span class="pre">--host-key-verify</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">OTHER</span></tt></dt>
-<dd>The shell type. Possible values: <tt class="docutils literal"><span class="pre">interactive</span></tt>, <tt class="docutils literal"><span class="pre">screen</span></tt>, <tt class="docutils literal"><span class="pre">tmux</span></tt>, <tt class="docutils literal"><span class="pre">macterm</span></tt>, or <tt class="docutils literal"><span class="pre">cssh</span></tt>. (<tt class="docutils literal"><span class="pre">csshx</span></tt> is deprecated in favor of <tt class="docutils literal"><span class="pre">cssh</span></tt>.)</dd>
-<dt><tt class="docutils literal"><span class="pre">-p</span> <span class="pre">PORT</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-port</span> <span class="pre">PORT</span></tt></dt>
-<dd>The SSH port.</dd>
-<dt><tt class="docutils literal"><span class="pre">-P</span> <span class="pre">PASSWORD</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-password</span> <span class="pre">PASSWORD</span></tt></dt>
-<dd>The SSH password. This can be used to pass the password directly on the command line. If this option is not specified (and a password is required) knife will prompt for the password.</dd>
-<dt><tt class="docutils literal"><span class="pre">--print-after</span></tt></dt>
-<dd>Use to show data after a destructive operation.</dd>
-<dt><tt class="docutils literal"><span class="pre">-s</span> <span class="pre">URL</span></tt>, <tt class="docutils literal"><span class="pre">--server-url</span> <span class="pre">URL</span></tt></dt>
-<dd>The URL for the Chef server.</dd>
-<dt><tt class="docutils literal"><span class="pre">SEARCH_QUERY</span></tt></dt>
-<dd>The search query used to return a list of servers to be accessed using SSH and the specified <tt class="docutils literal"><span class="pre">SSH_COMMAND</span></tt>. This option uses the same syntax as the search sub-command.</dd>
-<dt><tt class="docutils literal"><span class="pre">SSH_COMMAND</span></tt></dt>
-<dd>The command that will be run against the results of a search query.</dd>
-<dt><tt class="docutils literal"><span class="pre">-u</span> <span class="pre">USER</span></tt>, <tt class="docutils literal"><span class="pre">--user</span> <span class="pre">USER</span></tt></dt>
-<dd>The user name used by knife to sign requests made by the API client to the Chef server. Authentication will fail if the user name does not match the private key.</dd>
-<dt><tt class="docutils literal"><span class="pre">-v</span></tt>, <tt class="docutils literal"><span class="pre">--version</span></tt></dt>
-<dd>The version of the chef-client.</dd>
-<dt><tt class="docutils literal"><span class="pre">-V</span></tt>, <tt class="docutils literal"><span class="pre">--verbose</span></tt></dt>
-<dd>Set for more verbose outputs. Use <tt class="docutils literal"><span class="pre">-VV</span></tt> for maximum verbosity.</dd>
-<dt><tt class="docutils literal"><span class="pre">-x</span> <span class="pre">USER_NAME</span></tt>, <tt class="docutils literal"><span class="pre">--ssh-user</span> <span class="pre">USER_NAME</span></tt></dt>
-<dd>The SSH user name.</dd>
-<dt><tt class="docutils literal"><span class="pre">-y</span></tt>, <tt class="docutils literal"><span class="pre">--yes</span></tt></dt>
-<dd>Use to respond to all confirmation prompts with &#8220;Yes&#8221;. knife will not ask for confirmation.</dd>
-<dt><tt class="docutils literal"><span class="pre">-z</span></tt>, <tt class="docutils literal"><span class="pre">--local-mode</span></tt></dt>
-<dd>Use to run the chef-client in local mode. This allows all commands that work against the Chef server to also work against the local chef-repo.</dd>
-</dl>
-<p><strong>Examples</strong></p>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Fetch the SSL certificates used by Knife from the Chef server</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssl fetch
-</pre></div>
-</div>
-<p><strong>Fetch the SSL certificates used by the chef-client from the Chef server</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssl fetch -c /etc/chef/client.rb
-</pre></div>
-</div>
-<p><strong>Fetch SSL certificates from a URL or URI</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssl fetch URL_or_URI
-</pre></div>
-</div>
-<p>for example:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife ssl fetch https://www.getchef.com
-</pre></div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_status.html b/distro/common/html/knife_status.html
deleted file mode 100644
index 8273f11601..0000000000
--- a/distro/common/html/knife_status.html
+++ /dev/null
@@ -1,144 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife status &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-status">
-<h1>knife status<a class="headerlink" href="#knife-status" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife status</strong> subcommand is used to display a brief summary of the nodes on a Chef server, including the time of the most recent successful chef-client run.</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife status <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">QUERY</span></tt></dt>
-<dd>The search query used to identify a a list of items on a Chef server. This option uses the same syntax as the <tt class="docutils literal"><span class="pre">search</span></tt> sub-command.</dd>
-<dt><tt class="docutils literal"><span class="pre">-H</span></tt>, <tt class="docutils literal"><span class="pre">--hide-healthy</span></tt></dt>
-<dd>Use to hide nodes on which a chef-client run has occurred within the previous hour.</dd>
-<dt><tt class="docutils literal"><span class="pre">-l</span></tt>, <tt class="docutils literal"><span class="pre">--long</span></tt></dt>
-<dd>Use to display all attributes in the output and to show the output as JSON.</dd>
-<dt><tt class="docutils literal"><span class="pre">-m</span></tt>, <tt class="docutils literal"><span class="pre">--medium</span></tt></dt>
-<dd>Use to display normal attributes in the output and to show the output as JSON.</dd>
-<dt><tt class="docutils literal"><span class="pre">-r</span> <span class="pre">RUN_LIST</span></tt>, <tt class="docutils literal"><span class="pre">--run-list</span> <span class="pre">RUN_LIST</span></tt></dt>
-<dd>A comma-separated list of roles and/or recipes to be applied.</dd>
-<dt><tt class="docutils literal"><span class="pre">-s</span></tt>, <tt class="docutils literal"><span class="pre">--sort-reverse</span></tt></dt>
-<dd>Use to sort a list by last run time, descending.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>View status, include run-lists</strong></p>
-<p>To include run lists in the status, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife status --run-list
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>20 hours ago, dev-vm.chisamore.com, ubuntu 10.04, dev-vm.chisamore.com, 10.66.44.126, role<span class="o">[</span>lb<span class="o">]</span>.
-3 hours ago, i-225f954f, ubuntu 10.04, ec2-67-202-63-102.compute-1.amazonaws.com, 67.202.63.102, role<span class="o">[</span>web<span class="o">]</span>.
-3 hours ago, i-a45298c9, ubuntu 10.04, ec2-174-129-127-206.compute-1.amazonaws.com, 174.129.127.206, role<span class="o">[</span>web<span class="o">]</span>.
-3 hours ago, i-5272a43f, ubuntu 10.04, ec2-184-73-9-250.compute-1.amazonaws.com, 184.73.9.250, role<span class="o">[</span>web<span class="o">]</span>.
-3 hours ago, i-226ca64f, ubuntu 10.04, ec2-75-101-240-230.compute-1.amazonaws.com, 75.101.240.230, role<span class="o">[</span>web<span class="o">]</span>.
-3 hours ago, i-f65c969b, ubuntu 10.04, ec2-184-73-60-141.compute-1.amazonaws.com, 184.73.60.141, role<span class="o">[</span>web<span class="o">]</span>.
-</pre></div>
-</div>
-<p><strong>View status using a date range</strong></p>
-<p>To show the status for nodes on which the chef-client did not run successfully within the past hour, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife status --hide-healthy
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>1 hour ago, i-256f884f, ubuntu 12.04, ec2-67-202-63-102.compute-1.amazonaws.com, 67.202.63.102, role<span class="o">[</span>web<span class="o">]</span>.
-1 hour ago, i-a47823c9, ubuntu 10.04, ec2-174-129-127-206.compute-1.amazonaws.com, 184.129.143.111, role<span class="o">[</span>lb<span class="o">]</span>.
-</pre></div>
-</div>
-<p><strong>View status using a query</strong></p>
-<p>To show the status of a subset of nodes that are returned by a specific query, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife status <span class="s2">&quot;role:web&quot;</span> --run-list
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>3 hours ago, i-225f954f, ubuntu 10.04, ec2-67-202-63-102.compute-1.amazonaws.com, 67.202.63.102, role<span class="o">[</span>web<span class="o">]</span>.
-3 hours ago, i-a45298c9, ubuntu 10.04, ec2-174-129-127-206.compute-1.amazonaws.com, 174.129.127.206, role<span class="o">[</span>web<span class="o">]</span>.
-3 hours ago, i-5272a43f, ubuntu 10.04, ec2-184-73-9-250.compute-1.amazonaws.com, 184.73.9.250, role<span class="o">[</span>web<span class="o">]</span>.
-3 hours ago, i-226ca64f, ubuntu 10.04, ec2-75-101-240-230.compute-1.amazonaws.com, 75.101.240.230, role<span class="o">[</span>web<span class="o">]</span>.
-3 hours ago, i-f65c969b, ubuntu 10.04, ec2-184-73-60-141.compute-1.amazonaws.com, 184.73.60.141, role<span class="o">[</span>web<span class="o">]</span>.
-</pre></div>
-</div>
-<p><strong>View status for all nodes</strong></p>
-<p>To view the status of all nodes in the organization, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife status
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>20 hours ago, dev-vm.chisamore.com, ubuntu 10.04, dev-vm.chisamore.com, 10.66.44.126
-3 hours ago, i-225f954f, ubuntu 10.04, ec2-67-202-63-102.compute-1.amazonaws.com, 67.202.63.102
-3 hours ago, i-a45298c9, ubuntu 10.04, ec2-174-129-127-206.compute-1.amazonaws.com, 174.129.127.206
-3 hours ago, i-5272a43f, ubuntu 10.04, ec2-184-73-9-250.compute-1.amazonaws.com, 184.73.9.250
-3 hours ago, i-226ca64f, ubuntu 10.04, ec2-75-101-240-230.compute-1.amazonaws.com, 75.101.240.230
-3 hours ago, i-f65c969b, ubuntu 10.04, ec2-184-73-60-141.compute-1.amazonaws.com, 184.73.60.141
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_tag.html b/distro/common/html/knife_tag.html
deleted file mode 100644
index 59e6aea142..0000000000
--- a/distro/common/html/knife_tag.html
+++ /dev/null
@@ -1,138 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife tag &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-tag">
-<h1>knife tag<a class="headerlink" href="#knife-tag" title="Permalink to this headline">¶</a></h1>
-<p>A tag is a custom description that is applied to a node. A tag, once applied, can be helpful when managing nodes using knife or when building recipes by providing alternate methods of grouping similar types of information.</p>
-<p>The <strong>knife tag</strong> subcommand is used to apply tags to nodes on a Chef server.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<div class="section" id="create">
-<h2>create<a class="headerlink" href="#create" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">create</span></tt> argument is used to add one or more tags to a node.</p>
-<div class="section" id="syntax">
-<h3>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife tag create NODE_NAME <span class="o">[</span>TAG...<span class="o">]</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h3>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="examples">
-<h3>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Create tags</strong></p>
-<p>To create tags named <tt class="docutils literal"><span class="pre">seattle</span></tt>, <tt class="docutils literal"><span class="pre">portland</span></tt>, and <tt class="docutils literal"><span class="pre">vancouver</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife tag create node seattle portland vancouver
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="delete">
-<h2>delete<a class="headerlink" href="#delete" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">delete</span></tt> argument is used to delete one or more tags from a node.</p>
-<div class="section" id="id1">
-<h3>Syntax<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife tag delete NODE_NAME <span class="o">[</span>TAG...<span class="o">]</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id2">
-<h3>Options<a class="headerlink" href="#id2" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id3">
-<h3>Examples<a class="headerlink" href="#id3" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Delete tags</strong></p>
-<p>To delete tags named <tt class="docutils literal"><span class="pre">denver</span></tt> and <tt class="docutils literal"><span class="pre">phoenix</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife tag delete node denver phoenix
-</pre></div>
-</div>
-<p>Type <tt class="docutils literal"><span class="pre">Y</span></tt> to confirm a deletion.</p>
-</div>
-</div>
-<div class="section" id="list">
-<h2>list<a class="headerlink" href="#list" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">list</span></tt> argument is used to list all of the tags that have been applied to a node.</p>
-<div class="section" id="id4">
-<h3>Syntax<a class="headerlink" href="#id4" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife tag list <span class="o">[</span>NODE_NAME...<span class="o">]</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id5">
-<h3>Options<a class="headerlink" href="#id5" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id6">
-<h3>Examples<a class="headerlink" href="#id6" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>View a list of tags</strong></p>
-<p>To view the tags for a node named <tt class="docutils literal"><span class="pre">devops_prod1</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife tag list devops_prod1
-</pre></div>
-</div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_upload.html b/distro/common/html/knife_upload.html
deleted file mode 100644
index 692e4e4934..0000000000
--- a/distro/common/html/knife_upload.html
+++ /dev/null
@@ -1,153 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife upload &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-upload">
-<h1>knife upload<a class="headerlink" href="#knife-upload" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife upload</strong> subcommand is used to upload roles, cookbooks, environments, and data bags to the Chef server from the current working directory in the chef-repo. This subcommand is often used in conjunction with <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">diff</span></tt>, which can be used to see exactly what changes will be uploaded, and then <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">download</span></tt>, which does the opposite of <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">upload</span></tt>.</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife upload <span class="o">[</span>PATTERN...<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">--chef-repo-path</span> <span class="pre">PATH</span></tt></dt>
-<dd>The path to the chef-repo. This setting will override the default path to the chef-repo. Default: same value as specified by <tt class="docutils literal"><span class="pre">chef_repo_path</span></tt> in client.rb.</dd>
-<dt><tt class="docutils literal"><span class="pre">--concurrency</span></tt></dt>
-<dd>The number of allowed concurrent connections. Default: <tt class="docutils literal"><span class="pre">10</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]diff</span></tt></dt>
-<dd>Use to upload only new and modified files. Set to <tt class="docutils literal"><span class="pre">false</span></tt> to upload all files. Default: <tt class="docutils literal"><span class="pre">true</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]force</span></tt></dt>
-<dd>Use <tt class="docutils literal"><span class="pre">--force</span></tt> to upload roles, cookbooks, etc. even if the file in the directory is identical (by default, no <tt class="docutils literal"><span class="pre">POST</span></tt> or <tt class="docutils literal"><span class="pre">PUT</span></tt> is performed unless an actual change would be made). Default: <tt class="docutils literal"><span class="pre">--no-force</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]freeze</span></tt></dt>
-<dd>Use to require changes to a cookbook be included as a new version. Only the <tt class="docutils literal"><span class="pre">--force</span></tt> option can override this setting. Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-n</span></tt>, <tt class="docutils literal"><span class="pre">--dry-run</span></tt></dt>
-<dd>Use to take no action and only print out results. Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]purge</span></tt></dt>
-<dd>Use <tt class="docutils literal"><span class="pre">--purge</span></tt> to delete roles, cookbooks, etc. from the Chef server if their corresponding files do not exist in the chef-repo. By default, such objects are left alone and NOT purged. Default: <tt class="docutils literal"><span class="pre">--no-purge</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]recurse</span></tt></dt>
-<dd>Use <tt class="docutils literal"><span class="pre">--no-recurse</span></tt> to disable uploading a directory recursively. Default: <tt class="docutils literal"><span class="pre">--recurse</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--repo-mode</span> <span class="pre">MODE</span></tt></dt>
-<dd>The layout of the local chef-repo. Possible values: <tt class="docutils literal"><span class="pre">static</span></tt>, <tt class="docutils literal"><span class="pre">everything</span></tt>, or <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>. Use <tt class="docutils literal"><span class="pre">static</span></tt> for just roles, environments, cookbooks, and data bags. By default, <tt class="docutils literal"><span class="pre">everything</span></tt> and <tt class="docutils literal"><span class="pre">hosted_everything</span></tt> are dynamically selected depending on the server type. Default: <tt class="docutils literal"><span class="pre">everything</span></tt> / <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Upload the entire chef-repo</strong></p>
-<p>Browse to the top level of the chef-repo and enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife upload
-</pre></div>
-</div>
-<p>or from anywhere in the chef-repo, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife upload /
-</pre></div>
-</div>
-<p><strong>Upload the /cookbooks directory</strong></p>
-<p>Browse to the top level of the chef-repo and enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife upload cookbooks
-</pre></div>
-</div>
-<p>or from anywhere in the chef-repo, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife upload /cookbooks
-</pre></div>
-</div>
-<p><strong>Upload the /environments directory</strong></p>
-<p>Browse to the top level of the chef-repo and enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife upload environments
-</pre></div>
-</div>
-<p>or from anywhere in the chef-repo, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife upload /environments
-</pre></div>
-</div>
-<p><strong>Upload a single environment</strong></p>
-<p>Browse to the top level of the chef-repo and enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife upload environments/production.json
-</pre></div>
-</div>
-<p>or from the <tt class="docutils literal"><span class="pre">environments/</span></tt> directory, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife upload production.json
-</pre></div>
-</div>
-<p><strong>Upload the /roles directory</strong></p>
-<p>Browse to the top level of the chef-repo and enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife upload roles
-</pre></div>
-</div>
-<p>or from anywhere in the chef-repo, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife upload /roles
-</pre></div>
-</div>
-<p><strong>Upload cookbooks and roles</strong></p>
-<p>Browse to the top level of the chef-repo and enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife upload cookbooks/apache<span class="se">\*</span> roles/webserver.json
-</pre></div>
-</div>
-<p><strong>Use output of knife deps to pass command to knife upload</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife upload <span class="sb">`</span>knife deps nodes/*.json<span class="sb">`</span>
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_user.html b/distro/common/html/knife_user.html
deleted file mode 100644
index b49ddd585d..0000000000
--- a/distro/common/html/knife_user.html
+++ /dev/null
@@ -1,242 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife user &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-user">
-<h1>knife user<a class="headerlink" href="#knife-user" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife user</strong> subcommand is used to manage the list of users and their associated RSA public key-pairs.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">This subcommand ONLY works when run against the open source Chef server and will not run against Enterprise Chef (including hosted Enterprise Chef), or Private Chef.</p>
-</div>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<div class="section" id="create">
-<h2>create<a class="headerlink" href="#create" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">create</span></tt> argument is used to create a user. This process will generate an RSA key pair for the named user. The public key will be stored on the Chef server and the private key will be displayed on <tt class="docutils literal"><span class="pre">STDOUT</span></tt> or written to a named file.</p>
-<ul class="simple">
-<li>For the user, the private key should be copied to the system as <tt class="docutils literal"><span class="pre">/etc/chef/client.pem</span></tt>.</li>
-<li>For knife, the private key is typically copied to <tt class="docutils literal"><span class="pre">~/.chef/client_name.pem</span></tt> and referenced in the knife.rb configuration file.</li>
-</ul>
-<div class="section" id="syntax">
-<h3>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife user create USER_NAME <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h3>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span></tt>, <tt class="docutils literal"><span class="pre">--admin</span></tt></dt>
-<dd>Use to create a client as an admin client. This is required for any user to access Open Source Chef as an administrator. This option only works when used with the open source Chef server and will have no effect when used with Enterprise Chef.</dd>
-<dt><tt class="docutils literal"><span class="pre">-f</span> <span class="pre">FILE_NAME</span></tt>, <tt class="docutils literal"><span class="pre">--file</span> <span class="pre">FILE_NAME</span></tt></dt>
-<dd>Use to save a private key to the specified file name.</dd>
-<dt><tt class="docutils literal"><span class="pre">-p</span> <span class="pre">PASSWORD</span></tt>, <tt class="docutils literal"><span class="pre">--password</span> <span class="pre">PASSWORD</span></tt></dt>
-<dd>The user password.</dd>
-<dt><tt class="docutils literal"><span class="pre">--user-key</span> <span class="pre">FILE_NAME</span></tt></dt>
-<dd>All users are assigned a public key. Use to write the public key to a file.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h3>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Create a user</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife user create <span class="s2">&quot;Radio Birdman&quot;</span> -f /keys/user_name
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="delete">
-<h2>delete<a class="headerlink" href="#delete" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">delete</span></tt> argument is used to delete a registered user.</p>
-<div class="section" id="id1">
-<h3>Syntax<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife user delete USER_NAME
-</pre></div>
-</div>
-</div>
-<div class="section" id="id2">
-<h3>Options<a class="headerlink" href="#id2" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id3">
-<h3>Examples<a class="headerlink" href="#id3" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Delete a user</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife user delete <span class="s2">&quot;Steve Danno&quot;</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="edit">
-<h2>edit<a class="headerlink" href="#edit" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">edit</span></tt> argument is used to edit the details of a user. When this argument is run, knife will open $EDITOR. When finished, knife will update the Chef server with those changes.</p>
-<div class="section" id="id4">
-<h3>Syntax<a class="headerlink" href="#id4" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife user edit USER_NAME
-</pre></div>
-</div>
-</div>
-<div class="section" id="id5">
-<h3>Options<a class="headerlink" href="#id5" title="Permalink to this headline">¶</a></h3>
-<p>This command does not have any specific options.</p>
-</div>
-<div class="section" id="id6">
-<h3>Examples<a class="headerlink" href="#id6" title="Permalink to this headline">¶</a></h3>
-<p>None.</p>
-</div>
-</div>
-<div class="section" id="list">
-<h2>list<a class="headerlink" href="#list" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">list</span></tt> argument is used to view a list of registered users.</p>
-<div class="section" id="id7">
-<h3>Syntax<a class="headerlink" href="#id7" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife user list <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id8">
-<h3>Options<a class="headerlink" href="#id8" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-w</span></tt>, <tt class="docutils literal"><span class="pre">--with-uri</span></tt></dt>
-<dd>Use to show the corresponding URIs.</dd>
-</dl>
-</div>
-<div class="section" id="id9">
-<h3>Examples<a class="headerlink" href="#id9" title="Permalink to this headline">¶</a></h3>
-<p>None.</p>
-</div>
-</div>
-<div class="section" id="reregister">
-<h2>reregister<a class="headerlink" href="#reregister" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">reregister</span></tt> argument is used to regenerate an RSA key pair for a user. The public key will be stored on the Chef server and the private key will be displayed on <tt class="docutils literal"><span class="pre">STDOUT</span></tt> or written to a named file.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Running this argument will invalidate the previous RSA key pair, making it unusable during authentication to the Chef server.</p>
-</div>
-<div class="section" id="id10">
-<h3>Syntax<a class="headerlink" href="#id10" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife user reregister USER_NAME <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id11">
-<h3>Options<a class="headerlink" href="#id11" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-f</span> <span class="pre">FILE_NAME</span></tt>, <tt class="docutils literal"><span class="pre">--file</span> <span class="pre">FILE_NAME</span></tt></dt>
-<dd>Use to save a private key to the specified file name.</dd>
-</dl>
-</div>
-<div class="section" id="id12">
-<h3>Examples<a class="headerlink" href="#id12" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Regenerate the RSA key-pair</strong></p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife user reregister <span class="s2">&quot;Robert Younger&quot;</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="show">
-<h2>show<a class="headerlink" href="#show" title="Permalink to this headline">¶</a></h2>
-<p>The <tt class="docutils literal"><span class="pre">show</span></tt> argument is used to show the details of a user.</p>
-<div class="section" id="id13">
-<h3>Syntax<a class="headerlink" href="#id13" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife user show USER_NAME <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="id14">
-<h3>Options<a class="headerlink" href="#id14" title="Permalink to this headline">¶</a></h3>
-<p>This argument has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-a</span> <span class="pre">ATTR</span></tt>, <tt class="docutils literal"><span class="pre">--attribute</span> <span class="pre">ATTR</span></tt></dt>
-<dd>The attribute (or attributes) to show.</dd>
-</dl>
-</div>
-<div class="section" id="id15">
-<h3>Examples<a class="headerlink" href="#id15" title="Permalink to this headline">¶</a></h3>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Show user data</strong></p>
-<p>To view a user named <tt class="docutils literal"><span class="pre">Dennis</span> <span class="pre">Teck</span></tt>, enter:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife user show <span class="s2">&quot;Dennis Teck&quot;</span>
-</pre></div>
-</div>
-<p>to return something like:</p>
-<div class="highlight-bash"><div class="highlight"><pre>chef_type: user
-json_class: Chef::User
-name: Dennis Teck
-public_key:
-</pre></div>
-</div>
-<p><strong>Show user data as JSON</strong></p>
-<p>To view information in JSON format, use the <tt class="docutils literal"><span class="pre">-F</span></tt> common option as part of the command like this:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife user show <span class="s2">&quot;Dennis Teck&quot;</span> -F json
-</pre></div>
-</div>
-<p>(Other formats available include <tt class="docutils literal"><span class="pre">text</span></tt>, <tt class="docutils literal"><span class="pre">yaml</span></tt>, and <tt class="docutils literal"><span class="pre">pp</span></tt>, e.g. <tt class="docutils literal"><span class="pre">-F</span> <span class="pre">yaml</span></tt> for YAML.)</p>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_using.html b/distro/common/html/knife_using.html
deleted file mode 100644
index 4a3f88d350..0000000000
--- a/distro/common/html/knife_using.html
+++ /dev/null
@@ -1,210 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>Working with Knife &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="working-with-knife">
-<h1>Working with Knife<a class="headerlink" href="#working-with-knife" title="Permalink to this headline">¶</a></h1>
-<p>knife runs from a management workstation and sits in-between a Chef server and an organization&#8217;s infrastructure. knife interacts with a Chef server by using the same REST API that is used by a chef-client. Role-based authentication controls (RBAC) can be used to authorize changes when knife is run with Enterprise Chef. knife is configured during workstation setup, but subsequent modifications can be made using the knife.rb configuration file.</p>
-<div class="section" id="json-data-format">
-<h2>JSON Data Format<a class="headerlink" href="#json-data-format" title="Permalink to this headline">¶</a></h2>
-<p>Most data is entered using a text editor in JSON format, unless the <tt class="docutils literal"><span class="pre">--disable-editing</span></tt> option is entered as part of a command. (Encrypted data bags use YAML, which is a superset of JSON.) JSON is a common, language-independent data format that provides a simple text representation of arbitrary data structures. For more information about JSON, see <a class="reference external" href="http://www.json.org/">http://www.json.org/</a> or <a class="reference external" href="http://en.wikipedia.org/wiki/JSON">http://en.wikipedia.org/wiki/JSON</a>.</p>
-<div class="section" id="set-the-text-editor">
-<h3>Set the Text Editor<a class="headerlink" href="#set-the-text-editor" title="Permalink to this headline">¶</a></h3>
-<p>Some knife commands, such as <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">data</span> <span class="pre">bag</span> <span class="pre">edit</span></tt>, require that information be edited as JSON data using a text editor. For example, the following command:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife data bag edit admins admin_name
-</pre></div>
-</div>
-<p>will open up the text editor with data similar to:</p>
-<div class="highlight-javascript"><div class="highlight"><pre><span class="p">{</span>
- <span class="s2">&quot;id&quot;</span><span class="o">:</span> <span class="s2">&quot;admin_name&quot;</span>
-<span class="p">}</span>
-</pre></div>
-</div>
-<p>Changes to that file can then be made:</p>
-<div class="highlight-javascript"><div class="highlight"><pre><span class="p">{</span>
- <span class="s2">&quot;id&quot;</span><span class="o">:</span> <span class="s2">&quot;Justin C.&quot;</span>
- <span class="s2">&quot;description&quot;</span><span class="o">:</span> <span class="s2">&quot;I am passing the time by letting time pass over me ...&quot;</span>
-<span class="p">}</span>
-</pre></div>
-</div>
-<p>The type of text editor that is used by knife can be configured by adding an entry to the knife.rb file or by setting an <tt class="docutils literal"><span class="pre">EDITOR</span></tt> environment variable. For example, to configure the text editor to always open with vim, add the following to the knife.rb file:</p>
-<div class="highlight-ruby"><div class="highlight"><pre><span class="n">knife</span><span class="o">[</span><span class="ss">:editor</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&quot;/usr/bin/vim&quot;</span>
-</pre></div>
-</div>
-<p>When a Microsoft Windows file path is enclosed in a double-quoted string (&#8221; &#8221;), the same backslash character (<tt class="docutils literal"><span class="pre">\</span></tt>) that is used to define the file path separator is also used in Ruby to define an escape character. The knife.rb file is a Ruby file; therefore, file path separators must be escaped. In addition, spaces in the file path must be replaced with <tt class="docutils literal"><span class="pre">~1</span></tt> so that the length of each section within the file path is not more than 8 characters. For example, if EditPad Pro is the text editor of choice and is located at the following path:</p>
-<div class="highlight-python"><div class="highlight"><pre>C:\\Program Files (x86)\EditPad Pro\EditPad.exe
-</pre></div>
-</div>
-<p>the setting in the knife.rb file would be similar to:</p>
-<div class="highlight-ruby"><div class="highlight"><pre><span class="n">knife</span><span class="o">[</span><span class="ss">:editor</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&quot;C:</span><span class="se">\\</span><span class="s2">Progra~1</span><span class="se">\\</span><span class="s2">EditPa~1</span><span class="se">\\</span><span class="s2">EditPad.exe&quot;</span>
-</pre></div>
-</div>
-<p>One approach to working around the double- vs. single-quote issue is to put the single-quotes outside of the double-quotes. For example, for Notepad++:</p>
-<div class="highlight-ruby"><div class="highlight"><pre><span class="n">knife</span><span class="o">[</span><span class="ss">:editor</span><span class="o">]</span> <span class="o">=</span> <span class="s1">&#39;&quot;C:\Program Files (x86)\Notepad++\notepad++.exe -nosession -multiInst&quot;&#39;</span>
-</pre></div>
-</div>
-<p>for Sublime Text:</p>
-<div class="highlight-ruby"><div class="highlight"><pre><span class="n">knife</span><span class="o">[</span><span class="ss">:editor</span><span class="o">]</span> <span class="o">=</span> <span class="s1">&#39;&quot;C:\Program Files\Sublime Text 2\sublime_text.exe --wait&quot;&#39;</span>
-</pre></div>
-</div>
-<p>for TextPad:</p>
-<div class="highlight-ruby"><div class="highlight"><pre><span class="n">knife</span><span class="o">[</span><span class="ss">:editor</span><span class="o">]</span> <span class="o">=</span> <span class="s1">&#39;&quot;C:\Program Files (x86)\TextPad 7\TextPad.exe&quot;&#39;</span>
-</pre></div>
-</div>
-<p>and for vim:</p>
-<div class="highlight-ruby"><div class="highlight"><pre><span class="n">knife</span><span class="o">[</span><span class="ss">:editor</span><span class="o">]</span> <span class="o">=</span> <span class="s1">&#39;&quot;C:\Program Files (x86)\vim\vim74\gvim.exe&quot;&#39;</span>
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="using-quotes">
-<h2>Using Quotes<a class="headerlink" href="#using-quotes" title="Permalink to this headline">¶</a></h2>
-<p>Values can be entered with double quotes (&#8221; &#8221;) or single quotes (&#8216; &#8216;), but this should be done consistently.</p>
-</div>
-<div class="section" id="sub-commands">
-<h2>Sub-commands<a class="headerlink" href="#sub-commands" title="Permalink to this headline">¶</a></h2>
-<p>knife comes with a collection of built in subcommands that work together to provide all of the functionality required to take specific actions against any object in an organization, including cookbooks, nodes, roles, data bags, environments, and users. A knife plugin extends the functionality beyond built-in subcommands.</p>
-<p>knife has the following subcommands: <tt class="docutils literal"><span class="pre">bootstrap</span></tt>, <tt class="docutils literal"><span class="pre">client</span></tt>, <tt class="docutils literal"><span class="pre">configure</span></tt>, <tt class="docutils literal"><span class="pre">cookbook</span></tt>, <tt class="docutils literal"><span class="pre">cookbook</span> <span class="pre">site</span></tt>, <tt class="docutils literal"><span class="pre">data</span> <span class="pre">bag</span></tt>, <tt class="docutils literal"><span class="pre">delete</span></tt>, <tt class="docutils literal"><span class="pre">deps</span></tt>, <tt class="docutils literal"><span class="pre">diff</span></tt>, <tt class="docutils literal"><span class="pre">download</span></tt>, <tt class="docutils literal"><span class="pre">edit</span></tt>, <tt class="docutils literal"><span class="pre">environment</span></tt>, <tt class="docutils literal"><span class="pre">exec</span></tt>, <tt class="docutils literal"><span class="pre">index</span> <span class="pre">rebuild</span></tt>, <tt class="docutils literal"><span class="pre">list</span></tt>, <tt class="docutils literal"><span class="pre">node</span></tt>, <tt class="docutils literal"><span class="pre">recipe</span> <span class="pre">list</span></tt>, <tt class="docutils literal"><span class="pre">role</span></tt>, <tt class="docutils literal"><span class="pre">search</span></tt>, <tt class="docutils literal"><span class="pre">show</span></tt>, <tt class="docutils literal"><span class="pre">ssh</span></tt>, <tt class="docutils literal"><span class="pre">status</span></tt>, <tt class="docutils literal"><span class="pre">tag</span></tt>, <tt class="docutils literal"><span class="pre">upload</span></tt>, <tt class="docutils literal"><span class="pre">user</span></tt>, and <tt class="docutils literal"><span class="pre">xargs</span></tt>.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">The following subcommands run only against the open source Chef server: <tt class="docutils literal"><span class="pre">index</span> <span class="pre">rebuild</span></tt> and <tt class="docutils literal"><span class="pre">user</span></tt>.</p>
-</div>
-</div>
-<div class="section" id="verb-sub-commands">
-<h2>Verb Sub-commands<a class="headerlink" href="#verb-sub-commands" title="Permalink to this headline">¶</a></h2>
-<p>knife includes a set of subcommands that are built around common verbs: <tt class="docutils literal"><span class="pre">delete</span></tt>, <tt class="docutils literal"><span class="pre">deps</span></tt>, <tt class="docutils literal"><span class="pre">diff</span></tt>, <tt class="docutils literal"><span class="pre">download</span></tt>, <tt class="docutils literal"><span class="pre">edit</span></tt>, <tt class="docutils literal"><span class="pre">list</span></tt>, <tt class="docutils literal"><span class="pre">show</span></tt>, <tt class="docutils literal"><span class="pre">upload</span></tt>, <tt class="docutils literal"><span class="pre">xargs</span></tt>. These subcommands allow knife to issue commands that interact with any object stored in the chef-repo or stored on the Chef server. Some important principles behind this group of subcommands includes:</p>
-<ul class="simple">
-<li>A command that works with each object in the chef-repo. The subcommands specify the desired action (the &#8220;verb&#8221;), and then directory in which that object resides (<tt class="docutils literal"><span class="pre">clients</span></tt>, <tt class="docutils literal"><span class="pre">cookbooks/</span></tt>, <tt class="docutils literal"><span class="pre">data_bags/</span></tt>, <tt class="docutils literal"><span class="pre">environments/</span></tt>, <tt class="docutils literal"><span class="pre">nodes</span></tt>, <tt class="docutils literal"><span class="pre">roles/</span></tt>, and <tt class="docutils literal"><span class="pre">users</span></tt>). For example: <tt class="docutils literal"><span class="pre">download</span> <span class="pre">cookbooks/</span></tt></li>
-<li>A command that works with certain objects in Enterprise Chef, including <tt class="docutils literal"><span class="pre">acls</span></tt>, <tt class="docutils literal"><span class="pre">groups</span></tt>, and <tt class="docutils literal"><span class="pre">containers</span></tt></li>
-<li>Uses the Chef server as if it were a file system, allowing the chef-repo on the Chef server to behave like a mirror of the chef-repo on the workstation. The Chef server will have the same objects as the local chef-repo. To make changes to the files on the Chef server, just download files from the Chef server or upload files from the chef-repo</li>
-<li>The context from which a command is run matters. For example, when working in the <tt class="docutils literal"><span class="pre">roles/</span></tt> directory, knife will know what is being worked with. Enter <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">show</span> <span class="pre">base.json</span></tt> and knife will return the base role from the Chef server. From the chef-repo root, enter <tt class="docutils literal"><span class="pre">knife</span> <span class="pre">show</span> <span class="pre">roles/base.json</span></tt> to get the same result</li>
-<li>Parallel requests can be made to the Chef server and are configurable on a per-command basis</li>
-</ul>
-<div class="section" id="wildcard-search">
-<h3>Wildcard Search<a class="headerlink" href="#wildcard-search" title="Permalink to this headline">¶</a></h3>
-<p>A wildcard matching pattern can be used for substring matches that replace zero (or more) characters. There are two types of wildcard patterns:</p>
-<ul class="simple">
-<li>A question mark (”?”) can be used to replace exactly one character (as long as that character is not the first character)</li>
-<li>An asterisk (“*”) can be used to replace any number of characters (including zero)</li>
-</ul>
-<p>Wildcard patterns must be escaped (using a backslash) so that the wildcard itself can reach the Chef server. If they are not escaped, the wildcard is expanded into the actual filenames and knife will not know the wildcard was intended to be used. For example, if the Chef server has data bags named <tt class="docutils literal"><span class="pre">aardvarks</span></tt>, <tt class="docutils literal"><span class="pre">anagrams</span></tt>, and <tt class="docutils literal"><span class="pre">arp_tables</span></tt>, but the local file system only has <tt class="docutils literal"><span class="pre">aardvarks</span></tt> and <tt class="docutils literal"><span class="pre">anagrams</span></tt>, escaping vs. not escaping the wildcard pattern will yield different results:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife list data_bags/a<span class="se">\*</span>
-</pre></div>
-</div>
-<p>asks the Chef server for everything starting with the letter &#8220;a&#8221; and will return:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>aardvarks/ anagrams/ arp_tables/
-</pre></div>
-</div>
-<p>But, the following:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife list data_bags/a*
-</pre></div>
-</div>
-<p>will return:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>aardvarks/ anagrams/
-</pre></div>
-</div>
-<p>Which is the same as entering:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife list data_bags/aardvarks data_bags/anagrams
-</pre></div>
-</div>
-<p>to return:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>aardvarks/ anagrams/
-</pre></div>
-</div>
-</div>
-</div>
-<div class="section" id="plug-ins">
-<h2>Plug-ins<a class="headerlink" href="#plug-ins" title="Permalink to this headline">¶</a></h2>
-<p>Chef provides the following plugins, which work the same as built-in subcommands (including common options), but must be installed separately (using RubyGems): <strong>knife azure</strong>, <strong>knife bluebox</strong>, <strong>knife ec2</strong>, <strong>knife eucalyptus</strong>, <strong>knife google</strong>, <strong>knife hp</strong>, <strong>knife linode</strong>, <strong>knife openstack</strong>, <strong>knife rackspace</strong>, <strong>knife terremark</strong>, <strong>knife vcloud</strong>, and <strong>knife windows</strong>.</p>
-<p>The community provides many other plugins for knife: <a class="reference external" href="http://community.opscode.com/">http://community.opscode.com/</a>.</p>
-</div>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>All knife subcommands have the following syntax:</p>
-<blockquote>
-<div>knife subcommand [ARGUMENT] (options)</div></blockquote>
-<p>Each subcommand has its own set of arguments and options.</p>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">All syntax examples in this document show variables in ALL_CAPS. For example <tt class="docutils literal"><span class="pre">-u</span> <span class="pre">PORT_LIST</span></tt> (where PORT_LIST is a comma-separated list of local and public UDP ports) or <tt class="docutils literal"><span class="pre">-F</span> <span class="pre">FORMAT</span></tt> (where FORMAT determines the output format, either <tt class="docutils literal"><span class="pre">summary</span></tt>, <tt class="docutils literal"><span class="pre">text</span></tt>, <tt class="docutils literal"><span class="pre">json</span></tt>, <tt class="docutils literal"><span class="pre">yaml</span></tt>, or <tt class="docutils literal"><span class="pre">pp</span></tt>). These variables often require specific values that are unique to each organization.</p>
-</div>
-</div>
-<div class="section" id="many-users-same-repo">
-<h2>Many Users, Same Repo<a class="headerlink" href="#many-users-same-repo" title="Permalink to this headline">¶</a></h2>
-<p>It is possible for multiple users to access the Chef server using the same knife.rb file. (A user can even access multiple organizations if, for example, each instance of the chef-repo contained the same copy of the knife.rb file.) This can be done by adding the knife.rb file to the chef-repo, and then using environment variables to handle the user-specific credential details and/or sensitive values. For example:</p>
-<div class="highlight-ruby"><div class="highlight"><pre><span class="n">current_dir</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="bp">__FILE__</span><span class="p">)</span>
- <span class="n">user</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;OPSCODE_USER&#39;</span><span class="o">]</span> <span class="o">||</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;USER&#39;</span><span class="o">]</span>
- <span class="n">node_name</span> <span class="n">user</span>
- <span class="n">client_key</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;HOME&#39;</span><span class="o">]</span><span class="si">}</span><span class="s2">/.chef/</span><span class="si">#{</span><span class="n">user</span><span class="si">}</span><span class="s2">.pem&quot;</span>
- <span class="n">validation_client_name</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;ORGNAME&#39;</span><span class="o">]</span><span class="si">}</span><span class="s2">-validator&quot;</span>
- <span class="n">validation_key</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;HOME&#39;</span><span class="o">]</span><span class="si">}</span><span class="s2">/.chef/</span><span class="si">#{</span><span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;ORGNAME&#39;</span><span class="o">]</span><span class="si">}</span><span class="s2">-validator.pem&quot;</span>
- <span class="n">chef_server_url</span> <span class="s2">&quot;https://api.opscode.com/organizations/</span><span class="si">#{</span><span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;ORGNAME&#39;</span><span class="o">]</span><span class="si">}</span><span class="s2">&quot;</span>
- <span class="n">syntax_check_cache_path</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;HOME&#39;</span><span class="o">]</span><span class="si">}</span><span class="s2">/.chef/syntax_check_cache&quot;</span>
- <span class="n">cookbook_path</span> <span class="o">[</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">current_dir</span><span class="si">}</span><span class="s2">/../cookbooks&quot;</span><span class="o">]</span>
- <span class="n">cookbook_copyright</span> <span class="s2">&quot;Your Company, Inc.&quot;</span>
- <span class="n">cookbook_license</span> <span class="s2">&quot;apachev2&quot;</span>
- <span class="n">cookbook_email</span> <span class="s2">&quot;cookbooks@yourcompany.com&quot;</span>
-
- <span class="c1"># Amazon AWS</span>
- <span class="n">knife</span><span class="o">[</span><span class="ss">:aws_access_key_id</span><span class="o">]</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;AWS_ACCESS_KEY_ID&#39;</span><span class="o">]</span>
- <span class="n">knife</span><span class="o">[</span><span class="ss">:aws_secret_access_key</span><span class="o">]</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;AWS_SECRET_ACCESS_KEY&#39;</span><span class="o">]</span>
-
- <span class="c1"># Rackspace Cloud</span>
- <span class="n">knife</span><span class="o">[</span><span class="ss">:rackspace_api_username</span><span class="o">]</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;RACKSPACE_USERNAME&#39;</span><span class="o">]</span>
- <span class="n">knife</span><span class="o">[</span><span class="ss">:rackspace_api_key</span><span class="o">]</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;RACKSPACE_API_KEY&#39;</span><span class="o">]</span>
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/knife_xargs.html b/distro/common/html/knife_xargs.html
deleted file mode 100644
index a81478770c..0000000000
--- a/distro/common/html/knife_xargs.html
+++ /dev/null
@@ -1,122 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>knife xargs &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
- <div class="section" id="knife-xargs">
-<h1>knife xargs<a class="headerlink" href="#knife-xargs" title="Permalink to this headline">¶</a></h1>
-<p>The <strong>knife xargs</strong> subcommand is used to take patterns from standard input, download as JSON, run a command against the downloaded JSON, and then upload any changes.</p>
-<div class="section" id="syntax">
-<h2>Syntax<a class="headerlink" href="#syntax" title="Permalink to this headline">¶</a></h2>
-<p>This subcommand has the following syntax:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife xargs <span class="o">[</span>PATTERN...<span class="o">]</span> <span class="o">(</span>options<span class="o">)</span>
-</pre></div>
-</div>
-</div>
-<div class="section" id="options">
-<h2>Options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h2>
-<div class="admonition note">
-<p class="first admonition-title">Note</p>
-<p class="last">Review the list of <a class="reference internal" href="knife_common_options.html"><em>common options</em></a> available to this (and all) knife subcommands and plugins.</p>
-</div>
-<p>This subcommand has the following options:</p>
-<dl class="docutils">
-<dt><tt class="docutils literal"><span class="pre">-0</span></tt></dt>
-<dd>Use to show a <tt class="docutils literal"><span class="pre">NULL</span></tt> character (<tt class="docutils literal"><span class="pre">\0</span></tt>) instead of white space as the separator. Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--chef-repo-path</span> <span class="pre">PATH</span></tt></dt>
-<dd>The path to the chef-repo. This setting will override the default path to the chef-repo. Default: same value as specified by <tt class="docutils literal"><span class="pre">chef_repo_path</span></tt> in client.rb.</dd>
-<dt><tt class="docutils literal"><span class="pre">--concurrency</span></tt></dt>
-<dd>The number of allowed concurrent connections. Default: <tt class="docutils literal"><span class="pre">10</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]diff</span></tt></dt>
-<dd>Use to show a diff when a file changes. Default: <tt class="docutils literal"><span class="pre">--diff</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--dry-run</span></tt></dt>
-<dd>Use to prevent changes from being uploaded to the Chef server. Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--[no-]force</span></tt></dt>
-<dd>Use to force the upload of files even if they haven&#8217;t been changed. Default: <tt class="docutils literal"><span class="pre">--no-force</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-I</span> <span class="pre">REPLACE_STRING</span></tt>, <tt class="docutils literal"><span class="pre">--replace</span> <span class="pre">REPLACE_STRING</span></tt></dt>
-<dd>Use to define a string that will be used to replace all occurrences of a file name. Default: <tt class="docutils literal"><span class="pre">nil</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-J</span> <span class="pre">REPLACE_STRING</span></tt>, <tt class="docutils literal"><span class="pre">--replace-first</span> <span class="pre">REPLACE_STRING</span></tt></dt>
-<dd>Use to define a string that will be used to replace the first occurrence of a file name. Default: <tt class="docutils literal"><span class="pre">nil</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--local</span></tt></dt>
-<dd>Use to build or execute a command line against a local file. Set to <tt class="docutils literal"><span class="pre">false</span></tt> to build or execute against a remote file. Default: <tt class="docutils literal"><span class="pre">false</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-n</span> <span class="pre">MAX_ARGS</span></tt>, <tt class="docutils literal"><span class="pre">--max-args</span> <span class="pre">MAX_ARGS</span></tt></dt>
-<dd>The maximum number of arguments per command line. Default: <tt class="docutils literal"><span class="pre">nil</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-p</span> <span class="pre">[PATTERN...]</span></tt>, <tt class="docutils literal"><span class="pre">--pattern</span> <span class="pre">[PATTERN...]</span></tt></dt>
-<dd>One (or more) patterns for a command line. If this option is not specified, a list of patterns may be expected on standard input. Default: <tt class="docutils literal"><span class="pre">nil</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">--repo-mode</span> <span class="pre">MODE</span></tt></dt>
-<dd>The layout of the local chef-repo. Possible values: <tt class="docutils literal"><span class="pre">static</span></tt>, <tt class="docutils literal"><span class="pre">everything</span></tt>, or <tt class="docutils literal"><span class="pre">hosted_everything</span></tt>. Use <tt class="docutils literal"><span class="pre">static</span></tt> for just roles, environments, cookbooks, and data bags. By default, <tt class="docutils literal"><span class="pre">everything</span></tt> and <tt class="docutils literal"><span class="pre">hosted_everything</span></tt> are dynamically selected depending on the server type. Default value: <tt class="docutils literal"><span class="pre">default</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-s</span> <span class="pre">LENGTH</span></tt>, <tt class="docutils literal"><span class="pre">--max-chars</span> <span class="pre">LENGTH</span></tt></dt>
-<dd>The maximum size (in characters) for a command line. Default: <tt class="docutils literal"><span class="pre">nil</span></tt>.</dd>
-<dt><tt class="docutils literal"><span class="pre">-t</span></tt></dt>
-<dd>Use to run the print command on the command line. Default: <tt class="docutils literal"><span class="pre">nil</span></tt>.</dd>
-</dl>
-</div>
-<div class="section" id="examples">
-<h2>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h2>
-<p>The following examples show how to use this knife subcommand:</p>
-<p><strong>Use output of knife deps to pass command to knife xargs</strong></p>
-<p>The following examples show various ways of listing all nodes on the server, and then using Perl to replace <tt class="docutils literal"><span class="pre">grantmc</span></tt> with <tt class="docutils literal"><span class="pre">gmc</span></tt>:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife list <span class="s1">&#39;nodes/*&#39;</span> | knife xargs <span class="s2">&quot;perl -i -pe &#39;s/grantmc/gmc&#39;&quot;</span>
-</pre></div>
-</div>
-<p>or without quotes and the backslash escaped:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife list /nodes/<span class="se">\*</span> | knife xargs <span class="s2">&quot;perl -i -pe &#39;s/grantmc/gmc&#39;&quot;</span>
-</pre></div>
-</div>
-<p>or by using the <tt class="docutils literal"><span class="pre">--pattern</span></tt> option:</p>
-<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife xargs --pattern <span class="s1">&#39;/nodes.*&#39;</span> <span class="s2">&quot;perl -i -pe &#39;s/grantmc/gmc&#39;&quot;</span>
-</pre></div>
-</div>
-</div>
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/objects.inv b/distro/common/html/objects.inv
deleted file mode 100644
index 2dbcd31810..0000000000
--- a/distro/common/html/objects.inv
+++ /dev/null
Binary files differ
diff --git a/distro/common/html/search.html b/distro/common/html/search.html
deleted file mode 100644
index a7fe826ada..0000000000
--- a/distro/common/html/search.html
+++ /dev/null
@@ -1,82 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
- <title>Search &mdash; chef-client Man Pages</title>
-
- <link rel="stylesheet" href="_static/guide.css" type="text/css" />
- <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
- <script type="text/javascript">
- var DOCUMENTATION_OPTIONS = {
- URL_ROOT: './',
- VERSION: '',
- COLLAPSE_INDEX: false,
- FILE_SUFFIX: '.html',
- HAS_SOURCE: true
- };
- </script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
- <script type="text/javascript" src="_static/searchtools.js"></script>
-
-
- </head>
- <body>
-<div style="background-color: #212c35; text-align: left; padding: 0px 0px 0px 0px">
-<a href="http://docs.getchef.com/"><img src="_static/chef_html_logo.png" border="0" alt="Chef"/></a>
-</div>
-
-
-
-
- <div class="document">
- <div class="documentwrapper">
-
- <div class="body">
-
-
-<h1 id="search-documentation">Search the Documentation for Chef</h1>
-
-<div class="container">
-
-<p>
-From here you can use a scoped Google search query to search all of the documentation about Chef that is located at docs.getchef.com. (This page requires JavaScript be enabled to view the search box.)
-</p>
-
-<!-- Place this tag where you want both of the search box and the search results to render -->
-<gcse:search defaultToRefinement="Chef Documentation" webSearchResultSetSize="20"></gcse:search>
-
-&nbsp;<br>
-&nbsp;<br>
-&nbsp;<br>
-&nbsp;<br>
-&nbsp;<br>
-
-&nbsp;<br>
-&nbsp;<br>
-&nbsp;<br>
-&nbsp;<br>
-&nbsp;<br>
-
-</div>
-
-
- </div>
-
- </div>
-
-
- <div class="clearer"></div>
- </div>
-
-
-
-
- </body>
-</html> \ No newline at end of file
diff --git a/distro/common/html/searchindex.js b/distro/common/html/searchindex.js
deleted file mode 100644
index 956e91c737..0000000000
--- a/distro/common/html/searchindex.js
+++ /dev/null
@@ -1 +0,0 @@
-Search.setIndex({envversion:42,terms:{kickstart:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chisamor:12,poorli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],four:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],prefix:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dirnam:26,rsyslog:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],oldest:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chef_us:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],accur:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],service_nam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],umask:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],descript:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chef_typ:[0,14,31,32,4,7,36,19],under:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],slowest:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],replica:18,digit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],everi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cookbook_maintain:5,upstream:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],affect:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],month:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],csshx:[33,30,17],raw_data:36,cmd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],upload:[],rabbitmq:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rabbitmqctl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],verif:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],x86_64:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],proxy_url:29,hord:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],application_java:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hint_fil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],direct:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],consequ:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],second:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],aggreg:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ips_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],start_chef:29,even:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],supervis:18,hide:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],asid:27,peer_or_non:29,"new":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],net:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],topolog:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],metadata:[],default_attribut:[4,0],kilobyt:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],run_list_item:[32,9,34],displai:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],never:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],macports_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],here:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],num_vers:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],recipe_nam:[32,0],host_head:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],path:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],interpret:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hosted_everyth:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dry:[6,35,28,5],erl_cal:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rubocop:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],url_or_uri:[30,17],chefspec:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],databagitem:36,runlist:[9,34],brought:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],substr:26,unix:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],printf:1,hipchat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],txt:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],unit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],describ:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],would:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bundler:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],call:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],asset:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],recommend:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],indiana:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],type:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],until:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fastcgi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],relat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],server_url:27,notic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],warn:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],oc_bifrost:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],exce:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],relai:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],killal:34,hold:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],must:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gecod:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],join:1,henri:18,orgnam:[26,19,29],setup:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],work:[],bluebox:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],raid1:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],erb:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fnmatch:31,root:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],could:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ss6p92l_sca:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],overrid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],give:[34,1],smtp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],elrepo:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],indic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],want:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],keep:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],end:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],quot:[],eni:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],vagrant:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],how:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],env:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],verifi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],config:[],updat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],after:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],lab:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],emac:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],befor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],windows_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],arch:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],parallel:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],demonstr:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],request_path:14,attempt:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],client_nam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],opaqu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bootstrap:[],credenti:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],exclud:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],alias:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],maintain:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],environ:[],danno:7,enter:[12,21,34,0,14,1,32,33,15,31,4,5,6,7,37,35,26,36,19],order:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],oper:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],softwar:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],over:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],becaus:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],vari:[29,18],cli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],generic_execut:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],denver:37,better:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],persist:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],erlang:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],serverspec:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],split:1,them:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],woken:34,thei:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],proce:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rackspaceknif:5,"40g":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],choic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],changelog:31,conflict:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],timeout:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],each:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],debug:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],search_queri:[33,15,30,17,5],eacc:34,side:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mean:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],voxel:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],log_loc:29,extract:[6,9,20,13],linod:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],network:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],reg_sz:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],god:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],newli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],content:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rewrit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],devops_prod1:37,billing_admin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dsc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],prioriti:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],http_request:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],putti:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gunicorn:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],written:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ntp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],situat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],free:1,fred:36,qword:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],node1:32,kit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"1_0_0":5,"1_0_1":5,"1_0_2":5,"1_0_3":5,reconfigur:[],sigkil:18,reg_dword_big_endian:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],whateverthedefaultmightb:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],openssh:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],openssl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],filter:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],iso:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],temporari:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],user:[],pristin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rang:[12,15,34],render:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chefignor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],independ:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],capac:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],restrict:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hook:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],instruct:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],alreadi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],messag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],netfx:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],primari:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],role1:0,rewritten:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tinydn:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],top:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sometim:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mercuri:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],master:[],too:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],amqp_us:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],john:18,listen:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cloudform:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],iptabl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],consol:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"8wjyvhy9fhcegaareg":36,namespac:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tool:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],erchef:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"0a58cf8":15,yield:26,"10g":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bookshelf:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sha1:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],max_arg:28,auxw:34,target:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],provid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tree:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],zero:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],project:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],matter:26,gnupg:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],entri:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],minut:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],provis:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],behavior:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ram:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mine:29,unicast:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],raw:[],pessimist:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],seed:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],application_rubi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mint:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],snow:29,chefservicefeatur:29,blue:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],though:32,usernam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],glob:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],object:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],regular:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],s001:34,specifi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],letter:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],breakpoint:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bsd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],don:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],doc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],metal:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dog:36,doe:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cookbook_licens:26,wildcard:[],teck:7,unchang:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],antartica:29,dot:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bomb:[9,27,34],runit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],opposit:[6,13,35,20,18],whitelist:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],random:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ruby_block:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],syntax:[],radio:7,identifi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],make:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],celeri:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],absolut:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],layout:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"0a58e134":15,holder:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],configur:[],apach:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],lwrp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ldap:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],folder:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],oct:33,likewis:5,stop:[],compli:18,amazon:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],servermanagercmd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],report:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],youtub:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bar:[9,27,34],method:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],runa:34,reload:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],zabbix:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],no_proxy_url_or_ip:29,groupinstal:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],num:[33,30,17],mandatori:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],result:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],respons:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],noinput:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],key_fil:34,mdadm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],best:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rsa_kei:19,awar:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],said:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],databas:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],multiinst:26,sigint:18,solr4:18,irb:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],irc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],approach:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],databag:14,attribut:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],extend:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],were:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],extens:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],policyfil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],toler:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],advertis:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],kitchen:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],protect:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],easi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],met:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],howev:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],against:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fedora13:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],logic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],countri:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],login:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],com:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rehash:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],publishset:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],trunk:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],loader:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],your_email:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],usr1:34,diff:[],trust:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],assum:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],duplic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hints_path:29,chrome:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fri:33,three:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],been:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],trigger:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],basic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],homepath:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hesit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],quickli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],life:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],file_edit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],suppress:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],worker:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],telnet:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],argument:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],verify_api_cert:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],child:25,"catch":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ident:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],aix:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],data_bag_nam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gnu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],servic:[],properti:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],calcul:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],unsolv:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dashboard:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],nexenta:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],powershel:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],seven:18,remount:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],player:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],exit:[9,34,1],conf:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sever:15,amout:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],growl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],perform:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],suggest:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],use_last_modifi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],couchdb:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],preserv:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],descend:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],djbdn:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],syncd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],complet:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],raid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],nil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rail:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],orgmapp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rais:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],portal:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],unicorn:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tune:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mirror_expir:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],kept:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],scenario:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"30t21":5,flush_cach:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],name_of_premium_featur:18,inherit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],contact:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],thi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gzip:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],everyth:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],left:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],protocol:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],just:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sigusr1:34,bandwidth:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],human:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],name_of_servic:18,yet:18,languag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],previous:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],reboot:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mod_php:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],had:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],macport:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],els:29,save:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ubuntu12:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ubuntu10:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],lag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],opt:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],applic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cookbook_vers:[4,5,31],mayb:[9,27,34],metabas:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fusion:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],json_class:[0,14,31,32,4,7,36,19],shadow:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pingabl:18,daemon:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],specif:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],deprec:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],nrpe:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],arbitrari:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],manual:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],graylog:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],deploy_revis:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],public_kei:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sublime_text:26,specifii:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],el6:18,underli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],multi_str:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],right:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],interv:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],percentag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tibetanspaniel:36,intern:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],successfulli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],transmiss:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],knife_config:29,total:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],setloc:29,deploy:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],track:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fog:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],select:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],condit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],foo:[9,27,14,34],localhost:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],core:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],plu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],uncompress:5,insecur:18,repositori:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],anagram:26,actions_messag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"super":34,grizzli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],subkei:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],plug:[],postgresql:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],surround:[13,0,31,32,3,20,19],birdman:7,svn_argument:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],horizon:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],commit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"float":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],profession:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bound:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],diwb:18,down:18,run_list:[],storag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],eth1:18,git:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],suffici:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],support:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],nova:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"class":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],avail:[],reli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],wordpress:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],editor:[],jane:18,sha512:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],war:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],lowest:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],head:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],noevict:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],form:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],forc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],some:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],forg:[13,30,17],useradd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"25t23":5,icmp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"true":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],reset:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],wmi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],middle_nam:18,attr:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ssh_known_host:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],maximum:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mtu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],inaccur:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fundament:31,opensus:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],featur:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],openbsd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],classic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],decrypt:36,sale:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],diagnost:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],exist:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],glanc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ship:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],check:[],sticki:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],assembl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],vista:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],groupmod:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],encrypt:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],when:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],actor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],win_wget_p:29,role:[],test:[],roll:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],node:[],notif:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],intend:26,phoenix:37,kvm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],asterisk:[26,18],devop:[0,31,32,4,5,19],stompserv:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],intent:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],consid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sql:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],younger:7,search_attribut:1,faster:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],anywher:[6,35],pbkdf2:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ignor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],time:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],push:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],backward:27,impli:15,skip:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],consum:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],redis2:5,netbsd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],row:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],zookeep:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],varnish:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],middl:[13,17],depend:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],zone:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pem:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],decim:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],installonlypkg:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],comun:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],decis:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],jvm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],text:[],downtim:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],aspx:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],application_python:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sourc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],string:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],org_nam:18,cookbooks_path:9,cloudstack:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],lru:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],template_filenam:29,brows:[6,35],public_hostnam:33,script_fil:1,pkgbuild:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],administr:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],level:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],did:[12,18],iter:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],magnet:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],item:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cooki:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dir:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],validation_kei:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],prevent:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bffcreat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],trend:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sign:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cost:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],port:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"5272a43f":12,raid5:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],appear:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],icinga:5,repli:[13,17],current:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],id3lib:5,reg_binari:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],deriv:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],executionpolici:29,gener:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],unauthor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chef11:18,chef12:18,modif:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],address:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],along:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],redmin:5,wait:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],box:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chef_environ:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],invit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],netdev:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],checksum:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],behav:26,healthi:12,regardless:29,rightscal:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],extra:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],modul:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],test_system:1,prefer:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],peer:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],leav:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],seattl:37,visibl:1,instal:[],post:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],regex:[32,3,31,0,19],memori:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sensu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],subvers:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],msn:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],handler:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],msi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],criteria:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],checkout:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],azur:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rabbitmq_chef:5,visual:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tandem:18,templat:[],log_directori:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],effort:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],role_nam:[32,0,25],proxy_cache_path:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tokyo:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],uniqu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cat:1,descriptor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],profitbrick:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],graphit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],can:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],www:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],opscode_erchef:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],purpos:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],nearest:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],container_servic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],stream:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],backslash:[26,28],agent:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],topic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],critic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mirrorlist:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],occur:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],alwai:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sundai:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],multipl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gem_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ping:18,uptim:[33,18],write:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mixlib:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],purg:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],map:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],product:[13,21,35,15,4,6,18,20,29],omnitruck:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],max:28,clone:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sp4:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],identity_fil:[33,30,17,29],appnam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mac:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hklm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mai:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],log_level:[9,34,29],roundrobin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],data:[],man:[],freshli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],nullsoft:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],purge_before_symlink:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],logwatch:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],inform:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],preced:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],combin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],talk:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],port_list:26,config_fil:[10,30,17],ssh_wrapper:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],partial_search:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ttl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gitignor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],still:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dynam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],entiti:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],conjunct:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],group:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],monitor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],duplex:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],platform:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gem:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mail:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],non:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],main_monitor:3,rake:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],initi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],safari:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],half:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],nov:34,superset:26,provision:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],discuss:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],term:[],name:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],drop:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],revert:18,separ:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],full_nam:18,compil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],failov:18,domain:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],replac:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],individu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],name_of_packag:18,continu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],unlock:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gnu_parallel:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],year:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],happen:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],subnet:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],shown:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"3rd":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],space:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"100g":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],profil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],vrrp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],internet:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],correct:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hkey_us:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],orgtest:15,newsiz:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],migrat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],argv:1,mime:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],org:[],"byte":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],care:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],reusabl:31,wai:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],frequenc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],synchron:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],thing:18,place:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fa0fc4abf3f6787aeb5c3c5c35de667c:31,router:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],principl:26,think:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],frequent:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],first:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],origin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],directli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],carri:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],onc:[],arrai:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"long":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],oppos:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],uncaught:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],open:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],predefin:1,size:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],iam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],given:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],reprepro:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],silent:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],install_chef:29,ssh_command:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],iaa:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],baremetalcloud:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],citi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cumul:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],averag:33,white:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],apt_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],json_attrib:29,environment_nam:[9,4,25],hub:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],especi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],provinc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],copi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],full_control:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],artifact:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],broadcast:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],minitest:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"short":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],enclos:26,mostli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],john_smith:18,pecl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],than:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],png:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],serv:[],wide:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sbuild:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],windows_servic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],posix:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],balanc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],optimist:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],zsh:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pre:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fork:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],config_cont:29,pro:26,delim:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ani:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],client_kei:[26,34],ant:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],medium:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],smartos_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cassandra:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],extralarg:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],engin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],destroi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],note:1,sendfil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ideal:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],copyright_hold:31,take:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],noth:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],channel:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],begin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sure:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],trace:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],normal:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],buffer:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],compress:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],statu:[],instiki:5,timestamped_deploi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pair:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],collectstat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],later:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],drive:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],reg_expand_sz:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],runtim:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],superblock:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],expand_str:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],salt:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],netdev_interfac:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],show:[],encrypted_data_bag_secret:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],concurr:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],permiss:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sysctl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],help:[],xml:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],onli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],explicitli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],moneta:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],favor:[33,30,17],gceserviceaccount:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],transact:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],activ:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],state:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dword:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hello_world:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],analyt:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sighup:18,nearli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],variou:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],get:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],stomp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],secondari:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],repo:[],ssl:[],cannot:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ssh:[],requir:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],debian5:29,foodcrit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],aptitud:33,netscalar:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],aris:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],where:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],summari:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],wiki:[26,5],kernel:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],installshield:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],spork:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],endloc:29,drbd0:18,data_bag:[26,36,14],xenserv:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],concern:18,detect:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],review:[0,1,2,3,4,25,6,7,8,11,12,14,15,16,35,29,21,22,23,24,5,28,19,31,32,33,36,37],label:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],behind:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],volatil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],between:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dockerfil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"import":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],across:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sname:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],parent:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],node_nam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],screen:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],solaris_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],supermarket:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],syntax_check_cache_path:26,come:26,tue:34,gpasswd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],uuid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],librato:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pychef:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],library_nam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],datamapp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mani:[],runcontext:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],reindex:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],color:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],period:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],symfoni:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],colon:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],generic_writ:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cancel:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dsc_mof:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],poll:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bluepil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ultim:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],org_full_nam:18,west:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rebuild:[],replace_str:28,mark:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],spiceweasel:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rebuilt:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],my_cookbook:31,rubi:[],editpad:26,those:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"case":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],eip:21,pedant:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mount:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],invok:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],base64:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],region:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],suse:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],application_nginx:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],stdout:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],metric:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chef_client:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],airbrak:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cluster:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ascii:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],aa384235:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],develop:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],author:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],media:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],same:[],binari:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],html:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],document:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],week:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],finish:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],webserv:[21,32,33,34,25,6,35,9,27,29],nest:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],confidenti:36,driver:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],capabl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],openldap:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],improv:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],extern:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],repoforg:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],appropri:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],megabyt:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],without:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],trusted_certs_dir:[13,30],model:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],resource_collect:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],a29d6f254577b830091f140c3a78b1f:31,execut:[],loaderror:33,key_nam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rest:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],kill:[],aspect:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],org_cleanu:3,touch:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],passphras:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],http_proxi:29,speed:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],aws_access_key_id:26,samba:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],display_nam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hint:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],except:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],apache2:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],identif:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],instrument:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],query_to_run:15,ruby1:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pill:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],earli:18,around:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ohai:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],read:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],traffic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],platform_vers:31,world:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],yyyymmddhhmmss:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mof:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],iftop:5,integ:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],server:[],either:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],output:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rubyv:1,manag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cisco:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],glesi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],server01:[33,30,17],freez:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rsync:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],keytab:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],easili:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],definit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],appscript:33,keyston:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],highcpu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],portage_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],knife:[],refer:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],power:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],notepad:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],inspect:[6,13,20],broken:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],a45298c9:12,starttim:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],found:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],berksfil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bazaar:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],appli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],comparison:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],central:9,ack:5,gplv2:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gplv3:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],acl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],percona:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],act:27,backup:[],processor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],effici:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],max_siz:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],insuffici:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],your:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],charli:36,hkey_current_config:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],log:[],daemontool:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],simultan:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],overwrit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],start:[],interfac:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ipv4:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ipv6:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],svn:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],enough:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bundl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],untar:5,cabinet:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],opensolari:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],activemq:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],conclus:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],longer:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chefclientfeatur:29,pull:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],possibl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"default":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pacman:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bucket:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],powershell_script:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],vhd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],embed:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],connect:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cbc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],uid:36,creat:[],certain:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],remote_directori:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chef_handl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],decreas:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fail2ban:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],file:[],fill:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],incorrect:34,file_maxbyt:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],googl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],prepend:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],field:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],valid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],you:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],architectur:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],openid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],codecademi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],registri:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sequenc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],symbol:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pear:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fsck:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],snitch:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dropbox:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pool:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],netdev_lag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],reduc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],directori:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cookbook_copyright:26,mask:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mash:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],use_etag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],escap:[26,28],cpu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],actions_consum:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],scm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],represent:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],all:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],selinux:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],forbidden:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ibm305ramac:1,lacp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],java_opt:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],netdev_l2_interfac:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],follow:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],disk:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],children:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sympa:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dsl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],init:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],program:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],app_conf:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],scratch:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],introduc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cloudkick:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],global:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],premium:18,fals:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],checkin:1,subcommand:[],util:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],verb:[],mechan:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],failur:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],veri:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ossec:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],excluded_memb:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],list:[],last_nam:18,gerritt:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],recipe_fil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],plain:36,user_nam:[33,30,7,17,18],pid_fil:34,enterpris:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],drbd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sync:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],past:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],syslog:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rate:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],design:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pass:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ssh_attr:[33,30,17],further:18,current_dir:26,proxi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],what:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],yum_repositori:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sub:[],section:[13,20,26],abl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],brief:[12,13,20],rackspac:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],delet:[],abbrevi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],version:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],noprofil:29,"public":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],prereleas:29,full:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hash:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],berkelei:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],multilib:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],solari:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],excess:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gandi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],standard:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],modifi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],valu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],thrift:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],search:[],memcach:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],prior:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],amount:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pick:34,action:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],warrant:[13,20,1],via:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],transit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tmux:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],vim:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],filenam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],vip:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],establish:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],redisio:4,proceed:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],regist:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],two:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],validation_client_nam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],more:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],flat:8,association_us:18,wrapper:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],desir:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],site:[],flag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],particular:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],known:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],compani:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],destin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cach:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],installroot:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],psql:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],none:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],endpoint:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hour:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dep:[],dev:[12,32,15,34,4,18,9,27],histori:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],oktawav:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],remain:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hkey_local_machin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],caveat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],learn:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],deb:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],nagio:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],external_url:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],prompt:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],scan:34,share:[],bootstrap_directori:29,accept:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],verify_non:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],minimum:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],poni:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],explor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chef_data_bag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],csh:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],first_nam:18,secur:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rather:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],anoth:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pxe_dust:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],simpl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],distro:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],regener:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],resourc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],referenc:[7,19],vlan:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fstype:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rbac:26,perl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],data_bag_item_dogs_tibetanspaniel:36,associ:[],github:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],postfix:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],created_at:5,django:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],caus:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],allkei:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],logrot:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],opscod:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rotat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],templatefortextstr:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],i386:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],through:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],htop:5,paramet:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],create_dirs_before_symlink:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],systemd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sql_databas:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],exact:[32,15],pend:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cookbook_nam:[0,25,5,31],bypass:[6,13,20],"return":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],graylog2:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],timestamp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],framework:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],admin_nam:26,troubleshoot:18,hkey_classes_root:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],authent:[],"1password":5,userprofil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],token:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],average_r:5,compris:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fulli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],unicod:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],only_if:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],truncat:[32,5],denni:7,harm:[13,30],"300mb":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hard:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],crontab:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],expect:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],new_client:1,create_wait_m:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],portland:37,beyond:26,event:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ftp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],vancouv:37,robert:7,publish:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],etag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],print:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],occurr:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],file_nam:[19,7,31,18],gpl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],qualifi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],asp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],devops_data:36,advanc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],upon:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],campfir:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],effect:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],quick:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],reason:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],base:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],put:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],workstat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bash:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],basi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],thread:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],launch:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],omit:[15,19],perman:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],heartbeat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],assign:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],excurs:18,notifi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],upper:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],number:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],env_vari:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],done:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],stdlib:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],blank:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],stabl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],miss:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],file_atomic_upd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pgdg:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],differ:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],script:[],ipaddress:1,interact:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],unrestrict:29,least:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],checkpoint:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],win2k8:15,statement:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],zeromq:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],scheme:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],journli:4,jetti:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],store:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],schema:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dpkg_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],storm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],part:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pars:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],consult:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dpkg:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],reinstal:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],grep:[34,5,29],remot:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],remov:[],reg_qword:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],secret_access_kei:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],randomli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],comput:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gvim:26,packag:[],expir:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dedic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],testmast:18,berkshelf:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],built:[26,18],equival:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],also:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],centos5:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rakefil:21,build:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],stackforg:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],splai:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],compat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pipelin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],distribut:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],exec:[],previou:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],reach:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],quota:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],most:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],private_kei:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],preprod:18,clear:34,cover:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],destruct:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],clojur:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],clean:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],packagecloud:18,configuration_data_script:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],microsoft:[],carefulli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],xcode:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],alphanumer:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ignore_failur:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],session:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fine:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],affin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],firewal:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bff:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pretti:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],solut:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],jtimberman:[5,29],darwin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],yml:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],everysec:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],unus:[7,19],chef_gem:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"__file__":26,express:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],verify_p:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],nativ:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mainten:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fastest:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],restart:[],"225f954f":12,data_bag_name_or_path:36,crt:21,boost:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],your_company_nam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],common:[],gelf:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cmdlet:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],syntax_check_cach:26,certif:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],set:[],dump:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],creator:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],startup:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ifconfig:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],see:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],arg:28,reserv:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ark:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],flavor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cssh:[33,30,17],git_ssh:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],example_nod:1,prempt_delai:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],someth:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],subscript:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],altern:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],solo:[],chef_nod:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gemfil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],numer:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],javascript:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],succeed:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],distinguish:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],solr:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],popul:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],satisfi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],reg_multi_sz:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],delimit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],configuration_data:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],alon:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],thor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pdn:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],context:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],access_key_id:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],lash:1,vault:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],load:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],markdown:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],point:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],schedul:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],uptod:18,header:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],shutdown:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ucspi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],desktop:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],backend:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],authz:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rackspace_api_kei:26,unsuccess:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],java:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],devic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],add:[],empti:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],secret:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],strategi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],atomic_upd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],homebrew_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],togeth:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],imag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rspec:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],understand:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"0_8_0":5,look:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],registry_kei:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],frozen:31,hkcc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bill:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],batch:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],durat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],formatt:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"while":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],corpsit:21,abov:29,error:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hkcr:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],maradn:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],loop:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hkcu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],real:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],motd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],readm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],client_desc:1,dynect:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],itself:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cento:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],skype:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],vcloud:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],unmount:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fedora:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],grant:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],belong:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hadoop:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],shorter:1,octal:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],languages_ruby_vers:15,higher:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],x86:26,"0_7_0":5,optim:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cloud:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],wherea:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],inflat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],alert:5,jpackag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],lxc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],nosess:26,typic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],recent:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],lower:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],task:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],lib:29,older:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],british_sea_pow:29,ssl_verify_mod:[13,17,29],person:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],apptastic_tier_nam:[9,27,34],reflect:4,docker:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rbenv:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],propos:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],override_attribut:[4,34,27,0,9],mysql:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],openstack:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"07z":5,password:[],busser:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],web03:15,win:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],input:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tell:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],subsequ:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],app:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],vendor:5,obsolet:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fqdn_or_ip_address:29,format:[],ipmi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],main_attribut:15,local_download_path:29,nginx:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],exceptionclass:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],characterist:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],success:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],signal:18,svlogd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],resolv:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],elaps:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],collect:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],princip:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],api:[],encount:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],vsphere:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],often:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],simplifi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],add_formatt:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],acknowledg:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],creation:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],macterm:[33,30,17],back:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],unspecifi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sampl:[32,36,0,18],staticfil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],force_overrid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mirror:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chef_rol:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],virtualenv:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],scale:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],lamin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],per:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],attribute_nam:32,retri:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],larg:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],undon:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],slash:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],prod:18,proc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],snort:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],machin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sql_user:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],agreement:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],infra:18,step:[13,30,34],apptast:[9,27,34],wget:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],crond:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ufw:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],generic_read:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],constraint:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],drbdadm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],idl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],block:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],instance_typ:15,nsi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hudson:5,ohai_tim:1,smart_o_s_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],opscodesupport:5,within:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ensur:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rundeck:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],errno:34,question:26,arctic_hint:29,fast:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],custom:[],includ:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],suit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],forward:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],properli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ifcfg:15,textpad:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],remote_source_msi_url:29,pwd:29,link:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],translat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],newer:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],atom:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],noninteract:29,line:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],penguin:29,info:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],utc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],consist:[26,0],munin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],groovi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],nscd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"export":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],similar:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],nsca:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],supervisor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],doesn:32,repres:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"char":28,incomplet:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],home:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],curl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],titl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sequenti:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],invalid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"_imag":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],transport:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],peopl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],nice:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],deseri:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mongodb:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],meaning:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chef_server_url:[26,34,27,29],eval:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],splunk:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ladvd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],desert:25,lang:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"1024mb":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],infrequ:[13,20,1],algorithm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],confirm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],yourcompani:26,depth:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chef_overview_attribut:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hello:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],endtim:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],code:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],partial:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],queri:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],groupadd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],trywgfa6r70no28pnhmpghevkbzuxouemnbnauqsuyo:36,steve:7,privat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ulimit:4,elsewher:[],send:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],junip:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fatal:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sent:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],getchef:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],passiv:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],vlc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],volum:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],recip:[],magic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],netdev_vlan:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],id_rsa:29,geograph:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hive:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"try":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pleas:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],malici:[13,30],startup_typ:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],jdk:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cron:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],slackwar:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],download:[],click:34,append:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ps1:29,index:[],turn:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],compar:[],a47823c9:12,winrm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],find:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],access:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],f65c969b:12,logloc:[9,34],isapi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hku:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bodi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],let:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ubuntu:[],becom:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sinc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],convert:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],copyright:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],overwritten:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],aardvark:26,larger:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],steadi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fetch:[],converg:34,cert:29,rpm_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ctl:[],chang:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],honor:27,fstab:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],firefox:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ago:12,danger:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],spec_help:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],approxim:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gatewai:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],apt:[],"boolean":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],smartmon:31,redi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pxe:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],wix:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],from:[],zip:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],commun:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],doubl:26,upgrad:[],nexu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],next:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],websit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],few:[34,18],use_conditional_get:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],usr:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sort:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],src:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mismatch:4,about:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],trail:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"transient":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],starter:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],account:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],retriev:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tunnel:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],alia:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],crazi:36,hint_nam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],control:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sqlite:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],weaker:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tar:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],process:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],lock:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sudo:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cookbook_collect:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],high:[],tag:[],proprietari:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tarbal:[5,18],someurlher:31,symlink_before_migr:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],delai:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sit:[26,36],tamper:[13,30],zenpack:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],reg_dword:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],subdirectori:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],instead:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],opscode_us:26,zendmd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],msdn:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],somelongurlher:31,overridden:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],watch:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],apicli:19,tier:[],chef_repo_path:[21,13,22,35,25,23,6,24,8,11,28],physic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tenant:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],alloc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],delete_kei:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],essenti:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bind:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],zenoss:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],correspond:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],issu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],client_foo:19,allow:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],yum_globalconfig:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],aws_secret_access_kei:26,jira:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],restorecon:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],comma:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sql_ro_us:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],infrastructur:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],openvpn:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],asa:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bittorr:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],therefor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],keepaliv:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],greater:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],python:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],auto:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dai:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],auth:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],yum_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rubygem:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],recipe_url:9,front:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],file_cache_path:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],trac:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],anyth:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],edit:[],radiant:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pacman_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"0a7cffd5":15,resource_nam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],all_cap:26,subset:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],jane_do:18,chunk:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],meta:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"static":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ec2:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],citrix:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],patch:5,special:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],out:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],variabl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gentoo:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bag:[],armor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],erlang_solut:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bad:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rails_enterpris:5,categori:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],suitabl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rel:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rem:29,hardwar:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"_default":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"56g":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],local_destination_msi_path:29,red:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sql_server:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],shut:18,insid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],workflow:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],manipul:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],standalon:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],releas:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],shortest:18,qpid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],stackscript:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],s3_bucket:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ask:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fqdn:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],david:18,length:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],outsid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],retain:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cookbook_fil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],respond:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],polici:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],echo:29,date:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],puppet:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pgp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],kerbero:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],owner:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],facil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],underscor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],erubi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],licens:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mkdir:29,system:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],arista:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],attach:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],attack:[13,17],privaci:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],termin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"final":29,uri_for_https_serv:30,udp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],shell:[],big:18,fuzzi:15,shallow:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rdoc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rsa:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],exactli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],haven:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],passenger_apache2:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],homedr:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],structur:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],charact:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sens:5,sensit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],start_tim:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],plaintext:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],remote_fil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],inno:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],download_directori:31,have:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tabl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bootstrap_proxi:29,cfengin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],freebsd_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],min:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rout:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],atim:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],accuraci:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],which:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"256f884f":12,datacent:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],zlib:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],force_default:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],reposerv:29,unless:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],freebsd:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fidel:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],who:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],oracl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],discov:29,cipher:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],deploi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],xarg:[],kuwata:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],segment:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],why:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],push_job:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],placement:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],p180:29,url:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],request:[],uri:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],deni:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],yum:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],determin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],jenkin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],editpa:26,millisecond:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],wikipedia:26,verbos:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bring:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],nagl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],redirect:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],inlin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],locat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],launchpad:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rackspace_usernam:26,terremark:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"26am":34,jar:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sendmail:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],should:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],local:[],contribut:5,"226ca64f":12,notat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],familiar:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],passeng:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],autom:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],beam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],increas:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dsc_script:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],enabl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],organ:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],twice:32,sudoer:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],num_to_keep:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sha:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],stuff:[9,27,34],integr:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],partit:18,contain:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chef_zero:34,view:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],debconf:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],conform:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],legaci:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],libshadow:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],signatur:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],easy_install_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],elast:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],temporarili:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],brightbox:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],imagemagick:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],xxxxx:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],closer:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],datadog:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],impos:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],correctli:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pattern:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],machine_execut:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],vim74:26,sublim:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],progress:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],application_php:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],email:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],kei:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],retry_delai:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],job:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],entir:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],homebrew:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],swift:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],addit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],plugin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],admin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],equal:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],etc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],instanc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ami:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cinder:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sigterm:18,testclient:19,strftime:1,etm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],comment:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],extrasmal:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hyphen:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chmod:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],solv:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],b4c32f2:5,respect:31,rpm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mailto:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cookbook_email:26,yaml:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bluelock:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dword_big_endian:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],enable_apptast:[9,27,34],compon:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],json:[],treat:[9,27,34],scriptabl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ia2itmjrsw8:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],immedi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],capistrano:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],both:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],vmware:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rvm:29,last:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],thesecret123:36,present:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],replic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],need:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mvc3:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],defin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],passord:18,hkey_current_us:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],flowdock:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],code_gener:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],helper:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],squid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],slicehost:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],archiv:[9,5],dual:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],lightweight:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],incom:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],revis:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],parti:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],member:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],handl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],amazonec2tag:5,infer:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],backtrac:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],http:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],hostnam:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],again:36,keepal:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"null":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],nested_attribut:15,iop:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],machine_batch:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],identify_fil:[33,30,17],logfil:[9,34],php:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tftp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],expand:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cosmet:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],center:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],not_if:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],well:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],command:[],digitalocean:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fail:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],setx:29,latest:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],newest:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],data_bag_item:36,less:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tcp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],end_tim:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],machine_fil:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],webui:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sdanna:5,dsc_resourc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],sku:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],web:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],gmc:28,smith:18,omnibu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],myhelpermodul:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],apparmor:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],foobar:32,logger:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"4d44b5b":5,match:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cookbook_path:26,cpan:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],know:26,mynod:[25,29],cookbook_descript:5,recurs:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],insert:5,tail:[],resid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],like:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],fsync:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],latest_vers:5,amazonaw:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],necessari:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mustach:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],soft:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],page:[],apachev2:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],shef:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],eucalyptu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],revers:12,twitter:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],kdc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],msiexec:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],chef_data_bag_item:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],flush:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],proper:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],small:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],librari:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tmp:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],cookbookvers:31,leaf:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],leak:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],redis_lb:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],qr_knife_web:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"0a7ca19f":15,mode:[],investig:18,throttl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],usag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],symlink:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],maven:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],vhost:5,host:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],stage:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],homesick:5,sbin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ntlm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],actual:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],justin:26,column:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dism:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],haproxi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],loftninja:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],disabl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],own:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],automat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],guard:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],webpi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],smarto:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],merb:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],virtualbox:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],merg:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],omnio:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],transfer:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],machine_imag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],singl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],appl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],downgrad:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],progra:26,"var":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],exampleorg:19,"function":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],waldendud:18,subscrib:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],baseurl:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],addloc:29,bff_packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],oauth:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],highest:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],bug:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],count:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],succe:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],made:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],wise:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],arp_tabl:26,node_ip_address:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dmg:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],whether:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rc1:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],troubl:18,asynchron:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],record:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],below:18,limit:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],indefinit:[9,34],lvm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],otherwis:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],problem:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],actions_web:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],epel:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],grantmc:[28,18],evalu:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],ceilomet:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],dure:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pid:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],updated_at:5,ephemer:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],implement:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mtime:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],pip:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],inc:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],mutual:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],boot:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],detail:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],virtual:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],other:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],branch:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],riak:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],upstart:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],juno:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rackspace_api_usernam:26,"100m":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],thoreau:18,sbdm:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],debian:[],webpicmdlin:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"25z":5,sphinx:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],tomcat:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],scientif:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],rule:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],blog:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],emerg:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"0_8_1":5,cookbook:[]},objtypes:{},objnames:{},filenames:["knife_role","knife_exec","knife_configure","knife_recipe_list","knife_environment","knife_cookbook_site","knife_download","knife_user","knife_list","ctl_chef_solo","knife_common_options","knife_delete","knife_status","index","knife_raw","knife_search","knife_index_rebuild","knife_ssl_check","ctl_chef_server","knife_client","knife","knife_diff","knife_edit","knife_show","knife_serve","knife_deps","knife_using","ctl_chef_shell","knife_xargs","knife_bootstrap","knife_ssl_fetch","knife_cookbook","knife_node","knife_ssh","ctl_chef_client","knife_upload","knife_data_bag","knife_tag"],titles:["knife role","knife exec","knife configure","knife recipe list","knife environment","knife cookbook site","knife download","knife user","knife list","chef-solo","Common Options","knife delete","knife status","chef-client Man Pages","knife raw","knife search","knife index rebuild","knife ssl check","chef-server-ctl (executable)","knife client","knife","knife diff","knife edit","knife show","knife serve","knife deps","Working with Knife","chef-shell","knife xargs","knife bootstrap","knife ssl fetch","knife cookbook","knife node","knife ssh","chef-client","knife upload","knife data bag","knife tag"],objects:{},titleterms:{help:18,execut:18,show:[0,31,32,4,5,23,7,18,36,19],text:26,syntax:[0,1,2,3,4,25,6,7,8,11,12,14,15,16,35,19,21,22,23,24,5,26,28,29,31,32,33,36,37],privileg:34,configur:2,reregist:[7,19],window:[34,29],local:18,format:26,subcommand:18,stop:18,repo:26,ssl:[30,17],verb:26,ssh:33,password:18,restart:18,recip:3,term:18,tail:18,edit:[0,22,4,32,7,18,36,19],list:[0,31,32,3,37,4,5,7,18,8,36,19],upload:[35,31],authent:1,server:18,bag:36,common:10,kill:18,remov:[32,18],set:26,chef:[34,9,27,18,13],instal:[5,18],download:[6,31,5,18],unshar:5,index:16,statu:[12,18],sub:26,compar:4,delet:[36,0,31,32,37,4,7,18,11,19],knife:[0,1,2,3,4,5,6,7,8,11,12,13,14,15,16,17,35,19,20,21,22,23,24,25,26,28,29,30,31,32,33,36,37],metadata:31,solo:9,run:34,add:[32,18],ubuntu:29,org:18,plug:26,search:[26,15,5],cleans:18,page:13,recov:18,ctl:18,mani:26,backup:18,onc:18,apt:29,api:1,run_list:32,linux:34,diff:21,from:[32,4,36,0,31],log:18,script:1,data:[26,36],rebuild:16,upgrad:18,custom:29,avail:18,start:18,json:26,master:18,editor:26,shell:27,option:[0,1,2,3,4,25,6,7,8,9,10,11,12,14,15,16,35,19,21,22,23,24,5,27,28,29,31,32,33,34,36,37],rubi:1,reconfigur:18,bulk:[32,31,0,19],hup:18,uninstal:18,serv:24,packag:18,dep:25,servic:18,work:26,fetch:30,bootstrap:29,creat:[0,31,32,37,4,7,18,36,19],"int":18,share:5,site:5,templat:29,mode:27,high:18,raw:14,tag:37,file:[0,31,32,4,18,36],check:17,quot:26,same:26,client:[13,34,19],role:0,test:31,environ:4,config:18,node:32,elev:34,exec:1,user:[26,7,18],xarg:28,associ:[],debian:29,man:13,gather:18,request:1,exampl:[0,1,2,3,4,25,6,7,8,9,11,12,14,15,16,35,19,21,22,23,24,5,28,29,31,32,33,34,36,37],command:26,wildcard:26,disassoci:[],cookbook:[5,31],microsoft:29}}) \ No newline at end of file
diff --git a/distro/common/man/man1/README.md b/distro/common/man/man1/README.md
deleted file mode 100644
index 9a915fb4cc..0000000000
--- a/distro/common/man/man1/README.md
+++ /dev/null
@@ -1,58 +0,0 @@
-# Man pages for Knife
-
-The source of the Chef Documentation is located at
-http://docs.opscode.com/.
-
-This README documents how the man pages for all of the Knife subcommands
-that are built into the chef-client are managed.
-
-## Source Files
-
-The source files are located in the chef-docs repository:
-https://github.com/opscode/chef-docs
-
-Each Knife subcommand has its own source folder. The folder naming
-pattern begins with man_.
-
-Each man page is a single file called index.html.
-
-In the conf.py file, the following settings are unique to each man page:
-
-`today` setting is used to define the Chef version. This is because we
-don't want an arbitrary date populated in the file, yet we still need a
-version number. For example: `today = 'Chef 11.8`.
-
-`project` setting is set to be the same as the name of the subcommand.
-For example: `project = u'knife-foo'`.
-
-`Options for man page output` settings are set to be similar across all
-man pages, but each one needs to be tailored specifically for the name
-of the man page.
-
-All of the other settings in the General Configuration section should be
-left alone. These exist to ensure that all of the doc builds are sharing
-the right common elements and have the same overall presentation.
-
-## Building Docs
-
-The docs are built using Sphinx and must be set to the `-b man` output.
-Currently, the man pages are built locally and then added to the Chef
-builds in chef-master.
-
-## Editing
-
-These files should never be edited. All of the content is pulled in from
-elsewhere in the chef-docs repo at build time. If changes need to be
-made, those changes are done elsewhere and then the man pages must be
-rebuilt. This is to help ensure that all of the changes are made across
-all of the locations in which these documents need to live. For example,
-by design, every Knife subcommand with a man page also has an HTML doc
-at docs.opscode.com/knife_foo.html.
-
-## License
-
-[Creative Commons Attribution 3.0 Unported License](http://creativecommons.org/licenses/by/3.0/)
-
-## Questions?
-
-Open an [Issue](https://github.com/opscode/chef-docs/issues) and ask.
diff --git a/distro/common/man/man1/chef-shell.1 b/distro/common/man/man1/chef-shell.1
deleted file mode 100644
index df004c5b0f..0000000000
--- a/distro/common/man/man1/chef-shell.1
+++ /dev/null
@@ -1,194 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "CHEF-SHELL" "1" "Chef 12.0" "" "chef-shell"
-.SH NAME
-chef-shell \- The man page for the chef-shell command line tool.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-chef\-shell is a recipe debugging tool that allows the use of breakpoints within recipes. chef\-shell runs as an Interactive Ruby (IRb) session. chef\-shell supports both recipe and attribute file syntax, as well as interactive debugging features.
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-chef\-shell is the new name for Shef as of Chef 11.x\&. chef\-shell is backwards compatible and aside from the name change, has the same set of functionality as with previous releases.
-.UNINDENT
-.UNINDENT
-.sp
-The chef\-shell executable is run as a command\-line tool.
-.SH MODES
-.sp
-chef\-shell is tool that allows knife to be run using an Interactive Ruby (IRb) session. chef\-shell currently supports recipe and attribute file syntax, as well as interactive debugging features. chef\-shell has three run modes:
-.TS
-center;
-|l|l|.
-_
-T{
-Mode
-T} T{
-Description
-T}
-_
-T{
-Standalone
-T} T{
-No cookbooks are loaded, and the run list is empty. This mode is the default.
-T}
-_
-T{
-Solo
-T} T{
-chef\-shell acts as a chef\-solo client. It attempts to load the chef\-solo configuration file and JSON attributes. If the JSON attributes set a run list, it will be honored. Cookbooks will be loaded in the same way that chef\-solo loads them. chef\-solo mode is activated with the \fB\-s\fP or \fB\-\-solo\fP command line option, and JSON attributes are specified in the same way as for chef\-solo, with \fB\-j /path/to/chef\-solo.json\fP\&.
-T}
-_
-T{
-Client
-T} T{
-chef\-shell acts as a chef\-client\&. During startup, it reads the chef\-client configuration file and contacts the Chef server to get attributes and cookbooks. The run list will be set in the same way as normal chef\-client runs. chef\-client mode is activated with the \fB\-z\fP or \fB\-\-client\fP options. You can also specify the configuration file with \fB\-c CONFIG\fP and the server URL with \fB\-S SERVER_URL\fP\&.
-T}
-_
-.TE
-.SH OPTIONS
-.sp
-This command has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-chef\-shell OPTION VALUE OPTION VALUE ...
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-This command has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a\fP, \fB\-\-standalone\fP
-Use to run chef\-shell in standalone mode.
-.TP
-.B \fB\-c CONFIG\fP, \fB\-\-config CONFIG\fP
-The configuration file to use.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-j PATH\fP, \fB\-\-json\-attributes PATH\fP
-The path to a file that contains JSON data.
-.sp
-Use this option to define a \fBrun_list\fP object. For example, a JSON file similar to:
-.INDENT 7.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-"run_list": [
- "recipe[base]",
- "recipe[foo]",
- "recipe[bar]",
- "role[webserver]"
-],
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-may be used by running \fBchef\-client \-j path/to/file.json\fP\&.
-.sp
-In certain situations this option may be used to update \fBnormal\fP attributes.
-.sp
-\fBWARNING:\fP
-.INDENT 7.0
-.INDENT 3.5
-Any other attribute type that is contained in this JSON file will be treated as a \fBnormal\fP attribute. For example, attempting to update \fBoverride\fP attributes using the \fB\-j\fP option:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-{
- "name": "dev\-99",
- "description": "Install some stuff",
- "override_attributes": {
- "apptastic": {
- "enable_apptastic": "false",
- "apptastic_tier_name": "dev\-99.bomb.com"
- }
- }
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-will result in a node object similar to:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-{
- "name": "maybe\-dev\-99",
- "normal": {
- "name": "dev\-99",
- "description": "Install some stuff",
- "override_attributes": {
- "apptastic": {
- "enable_apptastic": "false",
- "apptastic_tier_name": "dev\-99.bomb.com"
- }
- }
- }
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.UNINDENT
-.UNINDENT
-.TP
-.B \fB\-l LEVEL\fP, \fB\-\-log\-level LEVEL\fP
-The level of logging that will be stored in a log file.
-.TP
-.B \fB\-s\fP, \fB\-\-solo\fP
-Use to run chef\-shell in chef\-solo mode.
-.TP
-.B \fB\-S CHEF_SERVER_URL\fP, \fB\-\-server CHEF_SERVER_URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-z\fP, \fB\-\-client\fP
-Use to run chef\-shell in chef\-client mode.
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-bootstrap.1 b/distro/common/man/man1/knife-bootstrap.1
deleted file mode 100644
index a4a699872f..0000000000
--- a/distro/common/man/man1/knife-bootstrap.1
+++ /dev/null
@@ -1,215 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-BOOTSTRAP" "1" "Chef 12.0" "" "knife bootstrap"
-.SH NAME
-knife-bootstrap \- The man page for the knife bootstrap subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-A bootstrap is a process that installs the chef\-client on a target system so that it can run as a chef\-client and communicate with a Chef server\&.
-.sp
-The \fBknife bootstrap\fP subcommand is used to run a bootstrap operation that installs the chef\-client on the target system. The bootstrap operation must specify the IP address or FQDN of the target system.
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife bootstrap FQDN_or_IP_ADDRESS (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-A\fP, \fB\-\-forward\-agent\fP
-Use to enable SSH agent forwarding.
-.TP
-.B \fB\-\-bootstrap\-curl\-options OPTIONS\fP
-Use to specify arbitrary options to be added to the bootstrap command when using cURL\&. This option may not be used in the same command with \fB\-\-bootstrap\-install\-command\fP\&.
-.TP
-.B \fB\-\-bootstrap\-install\-command COMMAND\fP
-Use to execute a custom installation command sequence for the chef\-client\&. This option may not be used in the same command with \fB\-\-bootstrap\-curl\-options\fP, \fB\-\-bootstrap\-install\-sh\fP, or \fB\-\-bootstrap\-wget\-options\fP\&.
-.TP
-.B \fB\-\-bootstrap\-install\-sh URL\fP
-Use to fetch and execute an installation script at the specified URL. This option may not be used in the same command with \fB\-\-bootstrap\-install\-command\fP\&.
-.TP
-.B \fB\-\-bootstrap\-no\-proxy NO_PROXY_URL_or_IP\fP
-A URL or IP address that specifies a location that should not be proxied.
-.sp
-\fBNOTE:\fP
-.INDENT 7.0
-.INDENT 3.5
-This option is used internally by Chef to help verify bootstrap operations during testing and should never be used during an actual bootstrap operation.
-.UNINDENT
-.UNINDENT
-.TP
-.B \fB\-\-bootstrap\-proxy PROXY_URL\fP
-The proxy server for the node that is the target of a bootstrap operation.
-.TP
-.B \fB\-\-bootstrap\-version VERSION\fP
-The version of the chef\-client to install.
-.TP
-.B \fB\-\-bootstrap\-wget\-options OPTIONS\fP
-Use to specify arbitrary options to be added to the bootstrap command when using GNU Wget\&. This option may not be used in the same command with \fB\-\-bootstrap\-install\-command\fP\&.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-G GATEWAY\fP, \fB\-\-ssh\-gateway GATEWAY\fP
-The SSH tunnel or gateway that is used to run a bootstrap action on a machine that is not accessible from the workstation.
-.TP
-.B \fB\-\-hint HINT_NAME[=HINT_FILE]\fP
-Use to specify an Ohai hint to be set on the target node.
-.sp
-Ohai hints are used to tell Ohai something about the system that it is running on that it would not be able to discover itself. An Ohai hint exists if a JSON file exists in the hint directory with the same name as the hint. For example, calling \fBhint?(\(aqantartica\(aq)\fP in an Ohai plugin would return an empty hash if the file \fBantartica.json\fP existed in the hints directory, and return nil if the file does not exist.
-.sp
-If the hint file contains JSON content, it will be returned as a hash from the call to \fBhint?\fP\&.
-.INDENT 7.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-{
- "snow": true,
- "penguins": "many"
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.INDENT 7.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-arctic_hint = hint?(\(aqantartica\(aq)
-if arctic_hint[\(aqsnow\(aq]
- "There are #{arctic_hint[\(aqpenguins\(aq]} penguins here."
-else
- "There is no snow here, and penguins like snow."
-end
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-The default directory in which hint files are located is \fB/etc/chef/ohai/hints/\fP\&. Use the \fBOhai::Config[:hints_path]\fP setting in the client.rb file to customize this location.
-.sp
-\fBHINT_FILE\fP is the name of the JSON file. \fBHINT_NAME\fP is the name of a hint in a JSON file. Use multiple \fB\-\-hint\fP options to specify multiple hints.
-.TP
-.B \fB\-i IDENTITY_FILE\fP, \fB\-\-identity\-file IDENTITY_FILE\fP
-The SSH identity file used for authentication. Key\-based authentication is recommended.
-.TP
-.B \fB\-j JSON_ATTRIBS\fP, \fB\-\-json\-attributes JSON_ATTRIBS\fP
-A JSON string that is added to the first run of a chef\-client\&.
-.TP
-.B \fB\-N NAME\fP, \fB\-\-node\-name NAME\fP
-The name of the node.
-.TP
-.B \fB\-\-[no\-]host\-key\-verify\fP
-Use \fB\-\-no\-host\-key\-verify\fP to disable host key verification. Default setting: \fB\-\-host\-key\-verify\fP\&.
-.TP
-.B \fB\-\-[no\-]node\-verify\-api\-cert\fP
-Use \fBverify_api_cert\fP to only do SSL validation of the Chef server connection; may be needed if the chef\-client needs to talk to other services that have broken SSL certificates. If this option is not specified, the setting for \fBverify_api_cert\fP in the configuration file is applied.
-.TP
-.B \fB\-\-node\-ssl\-verify\-mode PEER_OR_NONE\fP
-The verify mode for HTTPS requests.
-.sp
-Use \fB:verify_none\fP to do no validation of SSL certificates.
-.sp
-Use \fB:verify_peer\fP to do validation of all SSL certificates, including the Chef server connections, S3 connections, and any HTTPS \fBremote_file\fP resource URLs used in the chef\-client run. This is the recommended setting.
-.sp
-If this option is not specified, the setting for \fBssl_verify_mode\fP in the configuration file is applied.
-.TP
-.B \fB\-p PORT\fP, \fB\-\-ssh\-port PORT\fP
-The SSH port.
-.TP
-.B \fB\-P PASSWORD\fP, \fB\-\-ssh\-password PASSWORD\fP
-The SSH password. This can be used to pass the password directly on the command line. If this option is not specified (and a password is required) knife will prompt for the password.
-.TP
-.B \fB\-\-prerelease\fP
-Use to install pre\-release gems.
-.TP
-.B \fB\-r RUN_LIST\fP, \fB\-\-run\-list RUN_LIST\fP
-A comma\-separated list of roles and/or recipes to be applied.
-.TP
-.B \fB\-\-secret SECRET\fP
-The encryption key that is used for values contained within a data bag item.
-.TP
-.B \fB\-\-secret\-file FILE\fP
-The path to the file that contains the encryption key.
-.TP
-.B \fB\-\-sudo\fP
-Use to execute a bootstrap operation with sudo\&.
-.TP
-.B \fB\-t TEMPLATE\fP, \fB\-\-bootstrap\-template TEMPLATE\fP
-Use to specify the bootstrap template to use. This may specify the name of a bootstrap template\-\-\-\fBchef\-full\fP, for example\-\-\-or it may specify the full path to an Embedded Ruby (ERB) template that defines a custom bootstrap. Default value: \fBchef\-full\fP, which installs the chef\-client using the omnibus installer on all supported platforms.
-.TP
-.B \fB\-\-use\-sudo\-password\fP
-Use to perform a bootstrap operation with sudo; specify the password with the \fB\-P\fP (or \fB\-\-ssh\-password\fP) option.
-.TP
-.B \fB\-V \-V\fP
-Use to run the initial chef\-client run at the \fBdebug\fP log\-level (e.g. \fBchef\-client \-l debug\fP).
-.TP
-.B \fB\-x USERNAME\fP, \fB\-\-ssh\-user USERNAME\fP
-The SSH user name.
-.UNINDENT
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife bootstrap 192.168.1.1 \-x username \-P PASSWORD \-\-sudo
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife bootstrap 192.168.1.1 \-x username \-i ~/.ssh/id_rsa \-\-sudo
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-client.1 b/distro/common/man/man1/knife-client.1
deleted file mode 100644
index 9dbd174c71..0000000000
--- a/distro/common/man/man1/knife-client.1
+++ /dev/null
@@ -1,443 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-CLIENT" "1" "Chef 12.0" "" "knife client"
-.SH NAME
-knife-client \- The man page for the knife client subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-Every request made by the chef\-client to the Chef server must be an authenticated request using the Chef server API and a private key. When the chef\-client makes a request to the Chef server, the chef\-client authenticates each request using a private key located in \fB/etc/chef/client.pem\fP\&.
-.sp
-However, during the first chef\-client run, this private key does not exist. Instead, the chef\-client will attempt to use the private key assigned to the chef\-validator, located in \fB/etc/chef/validation.pem\fP\&. (If, for any reason, the chef\-validator is unable to make an authenticated request to the Chef server, the initial chef\-client run will fail.)
-.sp
-During the initial chef\-client run, the chef\-client will register with the Chef server using the private key assigned to the chef\-validator, after which the chef\-client will obtain a \fBclient.pem\fP private key for all future authentication requests to the Chef server\&.
-.sp
-After the initial chef\-client run has completed successfully, the chef\-validator is no longer required and may be deleted from the node. Use the \fBdelete_validation\fP recipe found in the \fBchef\-client\fP cookbook (\fI\%https://github.com/opscode\-cookbooks/chef\-client\fP) to remove the chef\-validator\&.
-.sp
-The \fBknife client\fP subcommand is used to manage an API client list and their associated RSA public key\-pairs. This allows authentication requests to be made to the Chef server by any entity that uses the Chef server API, such as the chef\-client and knife\&.
-.SH COMMON OPTIONS
-.sp
-The following options may be used with any of the arguments available to the \fBknife client\fP subcommand:
-.INDENT 0.0
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.SH BULK DELETE
-.sp
-The \fBbulk delete\fP argument is used to delete any API client that matches a pattern defined by a regular expression. The regular expression must be within quotes and not be surrounded by forward slashes (\fB/\fP).
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife client bulk delete REGEX
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.SH CREATE
-.sp
-The \fBcreate\fP argument is used to create a new API client\&. This process will generate an RSA key pair for the named API client\&. The public key will be stored on the Chef server and the private key will be displayed on \fBSTDOUT\fP or written to a named file.
-.INDENT 0.0
-.IP \(bu 2
-For the chef\-client, the private key should be copied to the system as \fB/etc/chef/client.pem\fP\&.
-.IP \(bu 2
-For knife, the private key is typically copied to \fB~/.chef/client_name.pem\fP and referenced in the knife.rb configuration file.
-.UNINDENT
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife client create CLIENT_NAME (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a\fP, \fB\-\-admin\fP
-Use to create a client as an admin client. This is required for any user to access Open Source Chef as an administrator. This option only works when used with the open source Chef server and will have no effect when used with Enterprise Chef\&.
-.TP
-.B \fB\-f FILE\fP, \fB\-\-file FILE\fP
-Use to save a private key to the specified file name.
-.TP
-.B \fB\-\-validator\fP
-Use to create the client as the chef\-validator\&. Default value: \fBtrue\fP\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To create a chef\-client that can access the Chef server API as an administrator\-\-\-sometimes referred to as an "API chef\-client"\-\-\-with the name "exampleorg" and save its private key to a file, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife client create exampleorg \-a \-f "/etc/chef/client.pem"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-When running the \fBcreate\fP argument on Enterprise Chef, be sure to omit the \fB\-a\fP option:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife client create exampleorg \-f "/etc/chef/client.pem"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH DELETE
-.sp
-The \fBdelete\fP argument is used to delete a registered API client\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife client delete CLIENT_NAME
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-To delete a client with the name "client_foo", enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife client delete client_foo
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Type \fBY\fP to confirm a deletion.
-.SH EDIT
-.sp
-The \fBedit\fP argument is used to edit the details of a registered API client\&. When this argument is run, knife will open $EDITOR to enable editing of the \fBadmin\fP attribute. (None of the other attributes should be changed using this argument.) When finished, knife will update the Chef server with those changes.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife client edit CLIENT_NAME
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-To edit a client with the name "exampleorg", enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife client edit exampleorg
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH LIST
-.sp
-The \fBlist\fP argument is used to view a list of registered API client\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife client list (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-w\fP, \fB\-\-with\-uri\fP
-Use to show the corresponding URIs.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To verify the API client list for the Chef server, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife client list
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something similar to:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-exampleorg
-i\-12345678
-rs\-123456
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To verify that an API client can authenticate to the
-Chef server correctly, try getting a list of clients using \fB\-u\fP and \fB\-k\fP options to specify its name and private key:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife client list \-u ORGNAME \-k .chef/ORGNAME.pem
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH REREGISTER
-.sp
-The \fBreregister\fP argument is used to regenerate an RSA key pair for an API client\&. The public key will be stored on the Chef server and the private key will be displayed on \fBSTDOUT\fP or written to a named file.
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-Running this argument will invalidate the previous RSA key pair, making it unusable during authentication to the Chef server\&.
-.UNINDENT
-.UNINDENT
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife client reregister CLIENT_NAME (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-f FILE_NAME\fP, \fB\-\-file FILE_NAME\fP
-Use to save a private key to the specified file name.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To regenerate the RSA key pair for a client named "testclient" and save it to a file named "rsa_key", enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife client regenerate testclient \-f rsa_key
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH SHOW
-.sp
-The \fBshow\fP argument is used to show the details of an API client\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife client show CLIENT_NAME (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a ATTR\fP, \fB\-\-attribute ATTR\fP
-The attribute (or attributes) to show.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To view a client named "testclient", enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife client show testclient
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-admin: false
-chef_type: client
-json_class: Chef::ApiClient
-name: testclient
-public_key:
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To view information in JSON format, use the \fB\-F\fP common option as part of the command like this:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role show devops \-F json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&.
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-configure.1 b/distro/common/man/man1/knife-configure.1
deleted file mode 100644
index 91eb69f1c6..0000000000
--- a/distro/common/man/man1/knife-configure.1
+++ /dev/null
@@ -1,161 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-CONFIGURE" "1" "Chef 12.0" "" "knife configure"
-.SH NAME
-knife-configure \- The man page for the knife configure subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife configure\fP subcommand is used to create the knife.rb and client.rb files so that they can be distributed to workstations and nodes.
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax when creating a knife.rb file:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife configure (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-and the following syntax when creating a client.rb file:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife configure client DIRECTORY
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-\-admin\-client\-key PATH\fP
-The path to the private key used by the client, typically a file named \fBadmin.pem\fP\&.
-.TP
-.B \fB\-\-admin\-client\-name NAME\fP
-The name of the client, typically the name of the admin client.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-i\fP, \fB\-\-initial\fP
-Use to create a API client, typically an administrator client on a freshly\-installed Chef server\&.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-r REPO\fP, \fB\-\-repository REPO\fP
-The path to the chef\-repo\&.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-\-validation\-client\-name NAME\fP
-The name of the validation client.
-.TP
-.B \fB\-\-validation\-key PATH\fP
-The path to the validation key used by the client, typically a file named \fBvalidation.pem\fP\&.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife configure
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife configure client \(aq/directory\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-cookbook-site.1 b/distro/common/man/man1/knife-cookbook-site.1
deleted file mode 100644
index acfcf6b882..0000000000
--- a/distro/common/man/man1/knife-cookbook-site.1
+++ /dev/null
@@ -1,552 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-COOKBOOK-SITE" "1" "Chef 12.0" "" "knife cookbook site"
-.SH NAME
-knife-cookbook-site \- The man page for the knife cookbook site subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The Cookbooks Site API is used to provide access to the cookbooks community hosted at \fI\%https://supermarket.getchef.com/cookbooks\fP\&. All of the cookbooks in the community are accessible through a RESTful API located at \fI\%https://supermarket.getchef.com/api/v1/cookbooks\fP by using any of the supported endpoints. In most cases, using knife and the \fBknife cookbook site\fP sub\-command (and any of its arguments) is the recommended method of interacting with these cookbooks, but in some cases, using the Cookbooks Site API directly may make sense.
-.sp
-The \fBknife cookbook site\fP subcommand is used to interact with cookbooks that are located at \fI\%https://supermarket.getchef.com/cookbooks\fP\&. A user account is required for any community actions that write data to this site. The following arguments do not require a user account: \fBdownload\fP, \fBsearch\fP, \fBinstall\fP, and \fBlist\fP\&.
-.SH COMMON OPTIONS
-.sp
-The following options may be used with any of the arguments available to the \fBknife cookbook site\fP subcommand:
-.INDENT 0.0
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.SH DOWNLOAD
-.sp
-The \fBdownload\fP argument is used to download a cookbook from the community website. A cookbook will be downloaded as a tar.gz archive and placed in the current working directory. If a cookbook (or cookbook version) has been deprecated and the \fB\-\-force\fP option is not used, knife will alert the user that the cookbook is deprecated and then will provide the name of the most recent non\-deprecated version of that cookbook.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook site download COOKBOOK_NAME [COOKBOOK_VERSION] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fBCOOKBOOK_VERSION\fP
-The version of a cookbook to be downloaded. If a cookbook has only one version, this option does not need to be specified. If a cookbook has more than one version and this option is not specified, the most recent version of the cookbook will be downloaded.
-.TP
-.B \fB\-f FILE\fP, \fB\-\-file FILE\fP
-The file to which a cookbook download is written.
-.TP
-.B \fB\-\-force\fP
-Use to overwrite an existing directory.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To download the cookbook \fBgetting\-started\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook site download getting\-started
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-Downloading getting\-started from the cookbooks site at version 0.3.0 to
- /Users/sdanna/opscodesupport/getting\-started\-0.3.0.tar.gz
-Cookbook saved: /Users/sdanna/opscodesupport/getting\-started\-0.3.0.tar.gz
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH INSTALL
-.sp
-The \fBinstall\fP argument is used to install a cookbook that has been downloaded from the community site to a local git repository . This action uses the git version control system in conjunction with the \fI\%https://supermarket.getchef.com/cookbooks\fP site to install community\-contributed cookbooks to the local chef\-repo\&. Using this argument does the following:
-.INDENT 0.0
-.INDENT 3.5
-.INDENT 0.0
-.IP 1. 3
-A new "pristine copy" branch is created in git for tracking the upstream.
-.IP 2. 3
-All existing versions of a cookbook are removed from the branch.
-.IP 3. 3
-The cookbook is downloaded from \fI\%https://supermarket.getchef.com/cookbooks\fP in the tar.gz format.
-.IP 4. 3
-The downloaded cookbook is untarred and its contents are committed to git and a tag is created.
-.IP 5. 3
-The "pristine copy" branch is merged into the master branch.
-.UNINDENT
-.UNINDENT
-.UNINDENT
-.sp
-This process allows the upstream cookbook in the master branch to be modified while letting git maintain changes as a separate patch. When an updated upstream version becomes available, those changes can be merged while maintaining any local modifications.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook site install COOKBOOK_NAME [COOKBOOK_VERSION] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-b\fP, \fB\-\-use\-current\-branch\fP
-Use to ensure that the current branch is used.
-.TP
-.B \fB\-B BRANCH\fP, \fB\-\-branch BRANCH\fP
-The name of the default branch. This will default to the master branch.
-.TP
-.B \fBCOOKBOOK_VERSION\fP
-The version of the cookbook to be installed. If a version is not specified, the most recent version of the cookbook will be installed.
-.TP
-.B \fB\-D\fP, \fB\-\-skip\-dependencies\fP
-Use to ensure that all cookbooks to which the installed cookbook has a dependency will not be installed.
-.TP
-.B \fB\-o PATH:PATH\fP, \fB\-\-cookbook\-path PATH:PATH\fP
-The directory in which cookbooks are created. This can be a colon\-separated path.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To install the cookbook \fBgetting\-started\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook site install getting\-started
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-Installing getting\-started to /Users/sdanna/opscodesupport/.chef/../cookbooks
-Checking out the master branch.
-Creating pristine copy branch chef\-vendor\-getting\-started
-Downloading getting\-started from the cookbooks site at version 0.3.0 to
- /Users/sdanna/opscodesupport/.chef/../cookbooks/getting\-started.tar.gz
-Cookbook saved: /Users/sdanna/opscodesupport/.chef/../cookbooks/getting\-started.tar.gz
-Removing pre\-existing version.
-Uncompressing getting\-started version /Users/sdanna/opscodesupport/.chef/../cookbooks.
-removing downloaded tarball
-1 files updated, committing changes
-Creating tag cookbook\-site\-imported\-getting\-started\-0.3.0
-Checking out the master branch.
-Updating 4d44b5b..b4c32f2
-Fast\-forward
- cookbooks/getting\-started/README.rdoc | 4 +++
- cookbooks/getting\-started/attributes/default.rb | 1 +
- cookbooks/getting\-started/metadata.json | 29 ++++++++++++++++++++
- cookbooks/getting\-started/metadata.rb | 6 ++++
- cookbooks/getting\-started/recipes/default.rb | 23 +++++++++++++++
- .../templates/default/chef\-getting\-started.txt.erb | 5 +++
- 6 files changed, 68 insertions(+), 0 deletions(\-)
- create mode 100644 cookbooks/getting\-started/README.rdoc
- create mode 100644 cookbooks/getting\-started/attributes/default.rb
- create mode 100644 cookbooks/getting\-started/metadata.json
- create mode 100644 cookbooks/getting\-started/metadata.rb
- create mode 100644 cookbooks/getting\-started/recipes/default.rb
- create mode 100644 cookbooks/getting\-started/templates/default/chef\-getting\-started.txt.erb
-Cookbook getting\-started version 0.3.0 successfully installed
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH LIST
-.sp
-The \fBlist\fP argument is used to view a list of cookbooks that are currently available at \fI\%https://supermarket.getchef.com/cookbooks\fP\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook site list
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-w\fP, \fB\-\-with\-uri\fP
-Use to show the corresponding URIs.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To view a list of cookbooks at \fI\%https://supermarket.getchef.com/cookbooks\fP server, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook site list
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-1password homesick rabbitmq
-7\-zip hostname rabbitmq\-management
-AmazonEC2Tag hosts rabbitmq_chef
-R hosts\-awareness rackspaceknife
-accounts htop radiant
-ack\-grep hudson rails
-activemq icinga rails_enterprise
-ad id3lib redis\-package
-ad\-likewise iftop redis2
-ant iis redmine
-[...truncated...]
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH SEARCH
-.sp
-The \fBsearch\fP argument is used to search for a cookbook at \fI\%https://supermarket.getchef.com/cookbooks\fP\&. A search query is used to return a list of cookbooks at \fI\%https://supermarket.getchef.com/cookbooks\fP and uses the same syntax as the \fBknife search\fP sub\-command.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook site search SEARCH_QUERY (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-To search for all of the cookbooks that can be used with Apache, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook site search apache*
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-apache2:
- cookbook: https://supermarket.chef.io/api/v1/cookbooks/apache2
- cookbook_description: Installs and configures apache2 using Debian symlinks with helper definitions
- cookbook_maintainer: opscode
- cookbook_name: apache2
-instiki:
- cookbook: https://supermarket.chef.io/api/v1/cookbooks/instiki
- cookbook_description: Installs instiki, a Ruby on Rails wiki server under passenger+Apache2.
- cookbook_maintainer: jtimberman
- cookbook_name: instiki
-kickstart:
- cookbook: https://supermarket.chef.io/api/v1/cookbooks/kickstart
- cookbook_description: Creates apache2 vhost and serves a kickstart file.
- cookbook_maintainer: opscode
- cookbook_name: kickstart
-[...truncated...]
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH SHARE
-.sp
-The \fBshare\fP argument is used to add a cookbook to \fI\%https://supermarket.getchef.com/cookbooks\fP\&. This action will require a user account and a certificate for \fI\%https://supermarket.getchef.com\fP\&. By default, knife will use the user name and API key that is identified in the configuration file used during the upload; otherwise these values must be specified on the command line or in an alternate configuration file. If a cookbook already exists on \fI\%https://supermarket.getchef.com/cookbooks\fP, then only an owner or maintainer of that cookbook can make updates.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook site share COOKBOOK_NAME CATEGORY (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fBCATEGORY\fP
-The cookbook category: \fB"Databases"\fP, \fB"Web Servers"\fP, \fB"Process Management"\fP, \fB"Monitoring & Trending"\fP, \fB"Programming Languages"\fP, \fB"Package Management"\fP, \fB"Applications"\fP, \fB"Networking"\fP, \fB"Operating Systems & Virtualization"\fP, \fB"Utilities"\fP, or \fB"Other"\fP\&.
-.TP
-.B \fB\-n\fP, \fB\-\-dry\-run\fP
-Use to take no action and only print out results. Default: \fBfalse\fP\&.
-.TP
-.B \fB\-o PATH:PATH\fP, \fB\-\-cookbook\-path PATH:PATH\fP
-The directory in which cookbooks are created. This can be a colon\-separated path.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To share a cookbook named \fBapache2\fP:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook site share "apache2" "Web Servers"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH SHOW
-.sp
-The \fBshow\fP argument is used to view information about a cookbook on \fI\%https://supermarket.getchef.com/cookbooks\fP\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook site show COOKBOOK_NAME [COOKBOOK_VERSION]
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fBCOOKBOOK_VERSION\fP
-The version of a cookbook to be shown. If a cookbook has only one version, this option does not need to be specified. If a cookbook has more than one version and this option is not specified, a list of cookbook versions will be returned.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To show the details for a cookbook named \fBhaproxy\fP:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook site show haproxy
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-average_rating:
-category: Networking
-created_at: 2009\-10\-25T23:51:07Z
-description: Installs and configures haproxy
-external_url:
-latest_version: https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_3
-maintainer: opscode
-name: haproxy
-updated_at: 2011\-06\-30T21:53:25Z
-versions:
- https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_3
- https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_2
- https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_1
- https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_0
- https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/0_8_1
- https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/0_8_0
- https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/0_7_0
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To view information in JSON format, use the \fB\-F\fP common option as part of the command like this:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role show devops \-F json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&.
-.SH UNSHARE
-.sp
-The \fBunshare\fP argument is used to stop the sharing of a cookbook at \fI\%https://supermarket.getchef.com/cookbooks\fP\&. Only the maintainer of a cookbook may perform this action.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook site unshare COOKBOOK_NAME
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-To unshare a cookbook named \fBgetting\-started\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook site unshare getting\-started
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-cookbook.1 b/distro/common/man/man1/knife-cookbook.1
deleted file mode 100644
index ce74c9dd8c..0000000000
--- a/distro/common/man/man1/knife-cookbook.1
+++ /dev/null
@@ -1,770 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-COOKBOOK" "1" "Chef 12.0" "" "knife cookbook"
-.SH NAME
-knife-cookbook \- The man page for the knife cookbook subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-A cookbook is the fundamental unit of configuration and policy distribution. Each cookbook defines a scenario, such as everything needed to install and configure MySQL, and then it contains all of the components that are required to support that scenario, including:
-.INDENT 0.0
-.IP \(bu 2
-Attribute values that are set on nodes
-.IP \(bu 2
-Definitions that allow the creation of reusable collections of resources
-.IP \(bu 2
-File distributions
-.IP \(bu 2
-Libraries that extend the chef\-client and/or provide helpers to Ruby code
-.IP \(bu 2
-Recipes that specify which resources to manage and the order in which those resources will be applied
-.IP \(bu 2
-Custom resources and providers
-.IP \(bu 2
-Templates
-.IP \(bu 2
-Versions
-.IP \(bu 2
-Metadata about recipes (including dependencies), version constraints, supported platforms, and so on
-.UNINDENT
-.sp
-The \fBknife cookbook\fP subcommand is used to interact with cookbooks that are located on the Chef server or the local chef\-repo\&.
-.SH COMMON OPTIONS
-.sp
-The following options may be used with any of the arguments available to the \fBknife cookbook\fP subcommand:
-.INDENT 0.0
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.SH BULK DELETE
-.sp
-The \fBbulk delete\fP argument is used to delete cookbook files that match a pattern defined by a regular expression. The regular expression must be within quotes and not be surrounded by forward slashes (/).
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook bulk delete REGEX (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-p\fP, \fB\-\-purge\fP
-Use to entirely remove a cookbook (or cookbook version) from the Chef server\&. This action should be used carefully because only one copy of any single file is stored on the Chef server\&. Consequently, purging a cookbook will disable any other cookbook that references one or more files from a cookbook that has been purged.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-Use a regular expression to define the pattern used to bulk delete cookbooks:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook bulk delete "^[0\-9]{3}$" \-p
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH CREATE
-.sp
-The \fBcreate\fP argument is used to create a new cookbook directory on the local machine, including the following directories and files:
-.INDENT 0.0
-.INDENT 3.5
-.INDENT 0.0
-.IP \(bu 2
-cookbook/attributes
-.IP \(bu 2
-cookbook/CHANGELOG.md
-.IP \(bu 2
-cookbook/definitions
-.IP \(bu 2
-cookbook/files/default
-.IP \(bu 2
-cookbook/libraries
-.IP \(bu 2
-cookbook/metadata.rb
-.IP \(bu 2
-cookbook/providers
-.IP \(bu 2
-cookbook/README.md (or .rdoc)
-.IP \(bu 2
-cookbook/recipes/default.rb
-.IP \(bu 2
-cookbook/resources
-.IP \(bu 2
-cookbook/templates/default
-.UNINDENT
-.UNINDENT
-.UNINDENT
-.sp
-After the cookbook is created, it can be uploaded to the Chef server using the \fBknife upload\fP argument.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook create COOKBOOK_NAME (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-C COPYRIGHT_HOLDER\fP, \fB\-\-copyright COPYRIGHT_HOLDER\fP
-The name of the copyright holder. This option will place a copyright notice that contains the name of the copyright holder in each of the pre\-created files. If this option is not specified, a copyright name of "your_company_name" will be used instead; it can be easily modified later.
-.TP
-.B \fB\-I LICENSE\fP, \fB\-\-license LICENSE\fP
-The type of license under which a cookbook is distributed: \fBapachev2\fP, \fBgplv2\fP, \fBgplv3\fP, \fBmit\fP, or \fBnone\fP (default). This option will place the appropriate license notice in the pre\-created files: \fBApache v2.0\fP (for \fBapachev2\fP), \fBGPL v2\fP (for \fBgplv2\fP), \fBGPL v3\fP (for \fBgplv3\fP), \fBMIT\fP (for \fBmit\fP), or \fBlicense \(aqProprietary \- All Rights Reserved\fP (for \fBnone\fP). Be aware of the licenses for files inside of a cookbook and be sure to follow any restrictions they describe.
-.TP
-.B \fB\-m EMAIL\fP, \fB\-\-email EMAIL\fP
-The email address for the individual who maintains the cookbook. This option will place an email address in each of the pre\-created files. If this option is not specified, an email name of "your_email" will be used instead; it can be easily modified later.
-.TP
-.B \fB\-o PATH\fP, \fB\-\-cookbook\-path PATH\fP
-The directory in which cookbooks are created. This can be a colon\-separated path.
-.TP
-.B \fB\-r FORMAT\fP, \fB\-\-readme\-format FORMAT\fP
-The document format of the readme file: \fBmd\fP (markdown) and \fBrdoc\fP (Ruby docs).
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To create a cookbook named "my_cookbook" with copyright, email, license, and readme format options specified, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook create my_cookbook \-C "My Name" \-m "my@email.com" \-I apachev2 \-r md
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-** Creating cookbook my_cookbook
-** Creating README for cookbook: my_cookbook
-** Creating metadata for cookbook: my_cookbook
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH DELETE
-.sp
-The \fBdelete\fP argument is used to delete a specified cookbook or cookbook version on the Chef server (and not locally).
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook delete COOKBOOK_NAME [COOKBOOK_VERSION] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a\fP, \fB\-\-all\fP
-Use to delete all cookbooks (and cookbook versions).
-.TP
-.B \fBCOOKBOOK_VERSION\fP
-The version of a cookbook to be deleted. If a cookbook has only one version, this option does not need to be specified. If a cookbook has more than one version and this option is not specified, knife will prompt for a version.
-.TP
-.B \fB\-p\fP, \fB\-\-purge\fP
-Use to entirely remove a cookbook (or cookbook version) from the Chef server\&. This action should be used carefully because only one copy of any single file is stored on the Chef server\&. Consequently, purging a cookbook will disable any other cookbook that references one or more files from a cookbook that has been purged.
-.UNINDENT
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook delete cookbook_name version
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-For example:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook delete smartmon 0.8
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Type \fBY\fP to confirm a deletion.
-.SH DOWNLOAD
-.sp
-The \fBdownload\fP argument is used to download a cookbook from the Chef server to the current working directory.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook download COOKBOOK_NAME [COOKBOOK_VERSION] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-d DOWNLOAD_DIRECTORY\fP, \fB\-\-dir DOWNLOAD_DIRECTORY\fP
-The directory in which cookbooks are located.
-.TP
-.B \fB\-f\fP, \fB\-\-force\fP
-Use to overwrite an existing directory.
-.TP
-.B \fB\-N\fP, \fB\-\-latest\fP
-Use to download the most recent version of a cookbook.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To download a cookbook named "smartmon", enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook download smartmon
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH LIST
-.sp
-The \fBlist\fP argument is used to view a list of cookbooks that are currently available on the Chef server\&. The list will contain only the most recent version for each cookbook by default.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook list (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a\fP, \fB\-\-all\fP
-Use to return all available versions for every cookbook.
-.TP
-.B \fB\-w\fP, \fB\-\-with\-uri\fP
-Use to show the corresponding URIs.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To view a list of cookbooks:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook list
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH METADATA
-.sp
-The \fBmetadata\fP argument is used to generate the metadata for one or more cookbooks.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook metadata (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a\fP, \fB\-\-all\fP
-Use to generate metadata for all cookbooks.
-.TP
-.B \fB\-o PATH:PATH\fP, \fB\-\-cookbook\-path PATH:PATH\fP
-The directory in which cookbooks are created. This can be a colon\-separated path.
-.UNINDENT
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook metadata \-a
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH METADATA FROM FILE
-.sp
-The \fBmetadata from file\fP argument is used to load the metadata for a cookbook from a file.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook metadata from file FILE
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook metadata from file /path/to/file
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH SHOW
-.sp
-The \fBshow\fP argument is used to view information about a cookbook, parts of a cookbook (attributes, definitions, files, libraries, providers, recipes, resources, and templates), or a file that is associated with a cookbook (including attributes such as checksum or specificity).
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook show COOKBOOK_NAME [COOKBOOK_VERSION] [PART...] [FILE_NAME] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fBCOOKBOOK_VERSION\fP
-The version of a cookbook to be shown. If a cookbook has only one version, this option does not need to be specified. If a cookbook has more than one version and this option is not specified, a list of cookbook versions will be returned.
-.TP
-.B \fB\-f FQDN\fP, \fB\-\-fqdn FQDN\fP
-The FQDN of the host.
-.TP
-.B \fBFILE_NAME\fP
-The name of a file that is associated with a cookbook.
-.TP
-.B \fB\-p PLATFORM\fP, \fB\-\-platform PLATFORM\fP
-The platform for which a cookbook is designed.
-.TP
-.B \fBPART\fP
-The part of the cookbook to show: \fBattributes\fP, \fBdefinitions\fP, \fBfiles\fP, \fBlibraries\fP, \fBproviders\fP, \fBrecipes\fP, \fBresources\fP, or \fBtemplates\fP\&. More than one part can be specified.
-.TP
-.B \fB\-V PLATFORM_VERSION\fP, \fB\-\-platform\-version PLATFORM_VERSION\fP
-The version of the platform.
-.TP
-.B \fB\-w\fP, \fB\-\-with\-uri\fP
-Use to show the corresponding URIs.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To get the list of available versions of a cookbook named "getting\-started", enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook show getting\-started
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-getting\-started 0.3.0 0.2.0
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To show a list of data about a cookbook using the name of the cookbook and the version, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook show getting\-started 0.3.0
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-attributes:
- checksum: fa0fc4abf3f6787aeb5c3c5c35de667c
- name: default.rb
- path: attributes/default.rb
- specificity: default
- url: https://somelongurlhere.com
-chef_type: cookbook_version
-cookbook_name: getting\-started
-definitions: []
-files: []
-frozen?: false
-json_class: Chef::CookbookVersion
-libraries: []
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To only view data about "templates", enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook show getting\-started 0.3.0 templates
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-checksum: a29d6f254577b830091f140c3a78b1fe
-name: chef\-getting\-started.txt.erb
-path: templates/default/chef\-getting\-started.txt.erb
-specificity: default
-url: https://someurlhere.com
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To view information in JSON format, use the \fB\-F\fP common option as part of the command like this:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role show devops \-F json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&.
-.SH TEST
-.sp
-The \fBtest\fP argument is used to test a cookbook for syntax errors. This argument uses Ruby syntax checking to verify every file in a cookbook that ends in .rb and Embedded Ruby (ERB)\&. This argument will respect \&.chefignore files when determining which cookbooks to test for syntax errors.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook test COOKBOOK_NAME (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a\fP, \fB\-\-all\fP
-Use to test all cookbooks.
-.TP
-.B \fB\-o PATH:PATH\fP, \fB\-\-cookbook\-path PATH:PATH\fP
-The directory in which cookbooks are created. This can be a colon\-separated path.
-.UNINDENT
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook test cookbook_name
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH UPLOAD
-.sp
-The \fBupload\fP argument is used to upload one or more cookbooks (and any files that are associated with those cookbooks) from a local repository to the Chef server\&. Only files that do not already exist on the Chef server will be uploaded.
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-Use a \&.chefignore file to prevent the upload of specific files and file types, such as temporary files or files placed in folders by version control systems. The \&.chefignore file must be located in the root of the cookbook repository and must use rules similar to filename globbing (as defined by the Ruby \fBFile.fnmatch\fP syntax).
-.UNINDENT
-.UNINDENT
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-Empty directories are not uploaded to the Chef server\&. To upload an empty directory, create a "dot" file\-\-\-e.g. \fB\&.keep\fP\-\-\-in that directory to ensure that the directory itself is not empty.
-.UNINDENT
-.UNINDENT
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook upload [COOKBOOK_NAME...] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a\fP, \fB\-\-all\fP
-Use to upload all cookbooks.
-.TP
-.B \fB\-\-concurrency\fP
-The number of allowed concurrent connections. Default: \fB10\fP\&.
-.TP
-.B \fB\-d\fP, \fB\-\-include\-dependencies\fP
-Use to ensure that when a cookbook has a dependency on one (or more) cookbooks, those cookbooks will also be uploaded.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-Use to set the environment version dependency to the cookbook version being uploaded.
-.TP
-.B \fB\-\-force\fP
-Use to update a cookbook even if the \fB\-\-freeze\fP flag has been set.
-.TP
-.B \fB\-\-freeze\fP
-Use to require changes to a cookbook be included as a new version. Only the \fB\-\-force\fP option can override this setting.
-.TP
-.B \fB\-o PATH:PATH\fP, \fB\-\-cookbook\-path PATH:PATH\fP
-The directory in which cookbooks are created. This can be a colon\-separated path.
-.UNINDENT
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook upload cookbook_name
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To upload a cookbook, and then prevent other users from being able to make changes to it, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife cookbook upload redis \-\-freeze
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-Uploading redis...
-Upload completed
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-If a cookbook is frozen and the \fB\-\-force\fP option is not specified, knife will return an error message similar to the following:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-Uploading redis...
-ERROR: Version 0.1.6 of cookbook redis is frozen. Use \-\-force to override.
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-data-bag.1 b/distro/common/man/man1/knife-data-bag.1
deleted file mode 100644
index a4d45ce60e..0000000000
--- a/distro/common/man/man1/knife-data-bag.1
+++ /dev/null
@@ -1,617 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-DATA-BAG" "1" "Chef 12.0" "" "knife data bag"
-.SH NAME
-knife-data-bag \- The man page for the knife data bag subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-A data bag is a global variable that is stored as JSON data and is accessible from a Chef server\&. A data bag is indexed for searching and can be loaded by a recipe or accessed during a search.
-.sp
-A data bag item may be encrypted using \fI\%shared secret encryption\fP\&. This allows each data bag item to store confidential information (such as a database password) or to be managed in a source control system (without plain\-text data appearing in revision history). Each data bag item may be encrypted individually; if a data bag contains multiple encrypted data bag items, these data bag items are not required to share the same encryption keys.
-.sp
-The \fBknife data bag\fP subcommand is used to manage arbitrary stores of globally available JSON data.
-.SH COMMON OPTIONS
-.sp
-The following options may be used with any of the arguments available to the \fBknife data bag\fP subcommand:
-.INDENT 0.0
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.SH CREATE
-.sp
-The \fBcreate\fP argument is used to add a data bag to the Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag create DATA_BAG_NAME [DATA_BAG_ITEM] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fBDATA_BAG_ITEM\fP
-The name of a specific item within a data bag.
-.TP
-.B \fB\-\-secret SECRET\fP
-The encryption key that is used for values contained within a data bag item. If \fBsecret\fP is not specified, the chef\-client will look for a secret at the path specified by the \fBencrypted_data_bag_secret\fP setting in the client.rb file.
-.TP
-.B \fB\-\-secret\-file FILE\fP
-The path to the file that contains the encryption key.
-.UNINDENT
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-For encrypted data bag items, use \fIeither\fP \fB\-\-secret\fP or \fB\-\-secret\-file\fP, not both.
-.UNINDENT
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To create a data bag named "admins", enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag create admins
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-Created data_bag[admins]
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH DELETE
-.sp
-The \fBdelete\fP argument is used to delete a data bag or a data bag item from a Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag delete DATA_BAG_NAME [DATA_BAG_ITEM] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fBDATA_BAG_ITEM\fP
-The name of a specific item within a data bag.
-.UNINDENT
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag delete data_bag_name
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To delete an item named "charlie", enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag delete admins charlie
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Type \fBY\fP to confirm a deletion.
-.SH EDIT
-.sp
-The \fBedit\fP argument is used to edit the data contained in a data bag. If encryption is being used, the data bag will be decrypted, the data will be made available in the $EDITOR, and then encrypted again before saving it to the Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag edit DATA_BAG_NAME [DATA_BAG_ITEM] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fBDATA_BAG_ITEM\fP
-The name of a specific item within a data bag.
-.TP
-.B \fB\-\-secret SECRET\fP
-The encryption key that is used for values contained within a data bag item. If \fBsecret\fP is not specified, the chef\-client will look for a secret at the path specified by the \fBencrypted_data_bag_secret\fP setting in the client.rb file.
-.TP
-.B \fB\-\-secret\-file FILE\fP
-The path to the file that contains the encryption key.
-.UNINDENT
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-For encrypted data bag items, use \fIeither\fP \fB\-\-secret\fP or \fB\-\-secret\-file\fP, not both.
-.UNINDENT
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To edit the contents of a data bag, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag edit dogs tibetanspaniel
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-where \fBdogs\fP is the name of the data bag and \fBtibetanspaniel\fP is the name of the data bag item. This will return something similar to the following in the knife editor:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-{
- "name":"data_bag_item_dogs_tibetanspaniel",
- "json_class":"Chef::DataBagItem",
- "chef_type":"data_bag_item",
- "data_bag":"dogs",
- "raw_data":
- {
- "description":"small dog that likes to sit in windows",
- "id":"tibetanspaniel"
- }
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Make the necessary changes to the key\-value pairs under \fBraw_data\fP and save them.
-.sp
-To edit an item named "charlie" that is contained in a data bag named "admins", enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag edit admins charlie
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to open the $EDITOR\&. Once opened, you can update the data before saving it to the Chef server\&. For example, by changing:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-{
- "id": "charlie"
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-{
- "id": "charlie",
- "uid": 1005,
- "gid": "ops",
- "shell": "/bin/zsh",
- "comment": "Crazy Charlie"
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH FROM FILE
-.sp
-The \fBfrom file\fP argument is used to:
-.INDENT 0.0
-.IP \(bu 2
-Add a data bag item to a data bag
-.IP \(bu 2
-Update the contents of an existing data bag item
-.UNINDENT
-.sp
-The data bag itself must already exist on the Chef server and must be specified as part of the command. The contents of the data bag item are specified using a JSON file. This JSON file may be located at a relative or absolute path; its location must be specified as part of the command. The JSON file that defines the contents of the data bag item must at least contain the name of the data bag item\-\-\-\fB"id": "name"\fP\&.
-.sp
-\fBWARNING:\fP
-.INDENT 0.0
-.INDENT 3.5
-A chef\-client must be version 11.6 (or higher) when using the \fBknife data bag from file\fP argument with the Enterprise Chef or Open Source Chef version 11 servers.
-.UNINDENT
-.UNINDENT
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag from file DATA_BAG_NAME_or_PATH
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a\fP, \fB\-\-all\fP
-Use to upload all data bags found at the specified path.
-.TP
-.B \fB\-\-secret SECRET\fP
-The encryption key that is used for values contained within a data bag item. If \fBsecret\fP is not specified, the chef\-client will look for a secret at the path specified by the \fBencrypted_data_bag_secret\fP setting in the client.rb file.
-.TP
-.B \fB\-\-secret\-file FILE\fP
-The path to the file that contains the encryption key.
-.UNINDENT
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-For encrypted data bag items, use \fIeither\fP \fB\-\-secret\fP or \fB\-\-secret\-file\fP, not both.
-.UNINDENT
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To create a data bag on the Chef server from a file:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag from file "path to JSON file"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To create a data bag named "devops_data" that contains encrypted data, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag from file devops_data \-\-secret\-file "path to decryption file"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH LIST
-.sp
-The \fBlist\fP argument is used to view a list of data bags that are currently available on the Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag list
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-w\fP, \fB\-\-with\-uri\fP
-Use to show the corresponding URIs.
-.UNINDENT
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag list
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH SHOW
-.sp
-The \fBshow\fP argument is used to view the contents of a data bag.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag show DATA_BAG_NAME (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fBDATA_BAG_ITEM\fP
-The name of a specific item within a data bag.
-.TP
-.B \fB\-\-secret SECRET\fP
-The encryption key that is used for values contained within a data bag item. If \fBsecret\fP is not specified, the chef\-client will look for a secret at the path specified by the \fBencrypted_data_bag_secret\fP setting in the client.rb file.
-.TP
-.B \fB\-\-secret\-file FILE\fP
-The path to the file that contains the encryption key.
-.UNINDENT
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-For encrypted data bag items, use \fIeither\fP \fB\-\-secret\fP or \fB\-\-secret\-file\fP, not both.
-.UNINDENT
-.UNINDENT
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag show admins
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-charlie
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To show the contents of a specific item within data bag, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag show admins charlie
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-comment: Crazy Charlie
-gid: ops
-id: charlie
-shell: /bin/zsh
-uid: 1005
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To show the contents of a data bag named \fBpasswords\fP with an item that contains encrypted data named \fBmysql\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag show passwords mysql
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-## sample:
-{
- "id": "mysql",
- "pass": "trywgFA6R70NO28PNhMpGhEvKBZuxouemnbnAUQsUyo=\en",
- "user": "e/p+8WJYVHY9fHcEgAAReg==\en"
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To show the decrypted contents of the same data bag, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag show \-\-secret\-file /path/to/decryption/file passwords mysql
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-## sample:
-{
- "id": "mysql",
- "pass": "thesecret123",
- "user": "fred"
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To view information in JSON format, use the \fB\-F\fP common option as part of the command like this:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag show admins \-F json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&.
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-delete.1 b/distro/common/man/man1/knife-delete.1
deleted file mode 100644
index fcbf52a65a..0000000000
--- a/distro/common/man/man1/knife-delete.1
+++ /dev/null
@@ -1,127 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-DELETE" "1" "Chef 12.0" "" "knife delete"
-.SH NAME
-knife-delete \- The man page for the knife delete subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife delete\fP subcommand is used to delete an object from a Chef server\&. This subcommand works similar to \fBknife cookbook delete\fP, \fBknife data bag delete\fP, \fBknife environment delete\fP, \fBknife node delete\fP, and \fBknife role delete\fP, but with a single verb (and a single action).
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife delete [PATTERN...] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-\-both\fP
-Use to delete both local and remote copies of an object. Default: \fBfalse\fP\&.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-\-chef\-repo\-path PATH\fP
-The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-concurrency\fP
-The number of allowed concurrent connections. Default: \fB10\fP\&.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-local\fP
-Use to delete only the local copy of an object. (A remote copy will not be deleted.) Default: \fBfalse\fP\&.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-r\fP, \fB\-\-[no\-]recurse\fP
-Use \fB\-\-recurse\fP to delete directories recursively. Default: \fB\-\-no\-recurse\fP\&.
-.TP
-.B \fB\-\-repo\-mode MODE\fP
-The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-deps.1 b/distro/common/man/man1/knife-deps.1
deleted file mode 100644
index afa384c1a9..0000000000
--- a/distro/common/man/man1/knife-deps.1
+++ /dev/null
@@ -1,246 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-DEPS" "1" "Chef 12.0" "" "knife deps"
-.SH NAME
-knife-deps \- The man page for the knife deps subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife deps\fP subcommand is used to identify dependencies for a node, role, or cookbook.
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife deps (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-\-chef\-repo\-path PATH\fP
-The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-concurrency\fP
-The number of allowed concurrent connections. Default: \fB10\fP\&.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-\-[no\-]recurse\fP
-Use \fB\-\-recurse\fP to list dependencies recursively. This option can only be used when \fB\-\-tree\fP is set to \fBtrue\fP\&. Default: \fB\-\-no\-recurse\fP\&.
-.TP
-.B \fB\-\-remote\fP
-Use to determine dependencies from objects located on the Chef server instead of in the local chef\-repo\&. Default: \fBfalse\fP\&.
-.TP
-.B \fB\-\-repo\-mode MODE\fP
-The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-\-tree\fP
-Use to show dependencies in a visual tree structure (including duplicates, if they exist). Default: \fBfalse\fP\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife deps nodes/node_name.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife deps roles/role_name.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife deps cookbooks/cookbook_name.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife deps environments/environment_name.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To find the dependencies for a combination of nodes, cookbooks, roles, and/or environments:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife deps cookbooks/git.json cookbooks/github.json roles/base.json environments/desert.json nodes/mynode.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-A wildcard can be used to return all of the child nodes. For example, all of the environments:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife deps environments/*.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Use the \fB\-\-tree\fP option to view the results with structure:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife deps roles/webserver.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-roles/webserver.json
- roles/base.json
- cookbooks/github
- cookbooks/git
- cookbooks/users
- cookbooks/apache2
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-The output of \fBknife deps\fP can be passed to \fBknife upload\fP:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife upload \(gaknife deps nodes/*.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-The output of \fBknife deps\fP can be passed to \fBknife xargs\fP:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife deps nodes/*.json | xargs knife upload
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-diff.1 b/distro/common/man/man1/knife-diff.1
deleted file mode 100644
index 8bf19ef609..0000000000
--- a/distro/common/man/man1/knife-diff.1
+++ /dev/null
@@ -1,226 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-DIFF" "1" "Chef 12.0" "" "knife diff"
-.SH NAME
-knife-diff \- The man page for the knife diff subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife diff\fP subcommand is used to compare the differences between files and directories on the Chef server and in the chef\-repo\&. For example, to compare files on the Chef server prior to an uploading or downloading files using the \fBknife download\fP and \fBknife upload\fP subcommands, or to ensure that certain files in multiple production environments are the same. This subcommand is similar to the \fBgit diff\fP command that can be used to diff what is in the chef\-repo with what is synced to a git repository.
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife diff [PATTERN...] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-\-chef\-repo\-path PATH\fP
-The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-cookbook\-version VERSION\fP
-The version of a cookbook to be downloaded.
-.TP
-.B \fB\-\-concurrency\fP
-The number of allowed concurrent connections. Default: \fB10\fP\&.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-\-diff\-filter=[(A|D|M|T)...[*]]\fP
-Use to select only files that have been added (\fBA\fP), deleted (\fBD\fP), modified (\fBM\fP), and/or have had their type changed (\fBT\fP). Any combination of filter characters may be used, including no filter characters. Use \fB*\fP to select all paths if a file matches other criteria in the comparison. Default value: \fBnil\fP\&.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-name\-only\fP
-Use to show only the names of modified files.
-.TP
-.B \fB\-\-name\-status\fP
-Use to show only the names of files with a status of \fBAdded\fP, \fBDeleted\fP, \fBModified\fP, or \fBType Changed\fP\&.
-.TP
-.B \fB\-\-no\-recurse\fP
-Use \fB\-\-no\-recurse\fP to disable listing a directory recursively. Default: \fB\-\-recurse\fP\&.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-\-repo\-mode MODE\fP
-The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.sp
-\fBknife.rb File Settings\fP
-.sp
-In addition to the default settings in a knife.rb file, there are other subcommand\-specific settings that can be added. When a subcommand is run, knife will use:
-.INDENT 0.0
-.IP 1. 3
-A value passed via the command\-line
-.IP 2. 3
-A value contained in the knife.rb file
-.IP 3. 3
-The default value
-.UNINDENT
-.sp
-A value passed via the command line will override a value in the knife.rb file; a value in a knife.rb file will override a default value.
-.sp
-The following \fBknife diff\fP settings can be added to the knife.rb file:
-.INDENT 0.0
-.TP
-.B \fBknife[:chef_repo_path]\fP
-Use to add the \fB\-\-chef\-repo\-path\fP option.
-.TP
-.B \fBknife[:concurrency]\fP
-Use to add the \fB\-\-concurrency\fP option.
-.TP
-.B \fBknife[:name_only]\fP
-Use to add the \fB\-\-name\-only\fP option.
-.TP
-.B \fBknife[:name_status]\fP
-Use to add the \fB\-\-name\-status\fP option.
-.TP
-.B \fBknife[:recurse]\fP
-Use to add the \fB\-\-recurse\fP option.
-.TP
-.B \fBknife[:repo_mode]\fP
-Use to add the \fB\-\-repo\-mode\fP option.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To compare the \fBbase.json\fP role to a \fBwebserver.json\fP role, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife diff roles/base.json roles/webserver.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To compare the differences between the local chef\-repo and the files that are on the Chef server, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife diff
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To diff a node named \fBnode\-lb\fP and then only return files that have been added, deleted, modified, or changed, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife diff \-\-name\-status node\-lb
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-node\-lb/recipes/eip.rb
-node\-lb/recipes/heartbeat\-int.rb
-node\-lb/templates/default/corpsite.conf.erb
-node\-lb/files/default/wildcard.node.com.crt
-node\-lb/files/default/wildcard.node.com.crt\-2009
-node\-lb/files/default/wildcard.node.com.key
-node\-lb/.gitignore
-node\-lb/Rakefile
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-download.1 b/distro/common/man/man1/knife-download.1
deleted file mode 100644
index ab232fe613..0000000000
--- a/distro/common/man/man1/knife-download.1
+++ /dev/null
@@ -1,258 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-DOWNLOAD" "1" "Chef 12.0" "" "knife download"
-.SH NAME
-knife-download \- The man page for the knife download subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife download\fP subcommand is used to download roles, cookbooks, environments, nodes, and data bags from the Chef server to the current working directory. It can be used to back up data on the Chef server, inspect the state of one or more files, or to extract out\-of\-process changes users may have made to files on the Chef server, such as if a user made a change that bypassed version source control. This subcommand is often used in conjunction with \fBknife diff\fP, which can be used to see exactly what changes will be downloaded, and then \fBknife upload\fP, which does the opposite of \fBknife download\fP\&.
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife download [PATTERN...] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-\-chef\-repo\-path PATH\fP
-The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-concurrency\fP
-The number of allowed concurrent connections. Default: \fB10\fP\&.
-.TP
-.B \fB\-\-cookbook\-version VERSION\fP
-The version of a cookbook to be downloaded.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-\-[no\-]diff\fP
-Use to download only new and modified files. Set to \fBfalse\fP to download all files. Default: \fB\-\-diff\fP\&.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-\-[no\-]force\fP
-Use \fB\-\-force\fP to download files even when the file on the hard drive is identical to the object on the server (role, cookbook, etc.). By default, files are compared to see if they have equivalent content, and local files are only overwritten if they are different. Default: \fB\-\-no\-force\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-n\fP, \fB\-\-dry\-run\fP
-Use to take no action and only print out results. Default: \fBfalse\fP\&.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-\-[no\-]purge\fP
-Use \fB\-\-purge\fP to delete local files and directories that do not exist on the Chef server\&. By default, if a role, cookbook, etc. does not exist on the Chef server, the local file for said role will be left alone and NOT deleted. Default: \fB\-\-no\-purge\fP\&.
-.TP
-.B \fB\-\-[no\-]recurse\fP
-Use \fB\-\-no\-recurse\fP to disable downloading a directory recursively. Default: \fB\-\-recurse\fP\&.
-.TP
-.B \fB\-\-repo\-mode MODE\fP
-The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To download the entire chef\-repo from the Chef server, browse to the top level of the chef\-repo and enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife download /
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To download the \fBcookbooks/\fP directory from the Chef server, browse to the top level of the chef\-repo and enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife download cookbooks
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or from anywhere in the chef\-repo, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife download /cookbooks
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To download the \fBenvironments/\fP directory from the Chef server, browse to the top level of the chef\-repo and enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife download environments
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or from anywhere in the chef\-repo, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife download /environments
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To download an environment named "production" from the Chef server, browse to the top level of the chef\-repo and enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife download environments/production.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or from the \fBenvironments/\fP directory, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife download production.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To download the \fBroles/\fP directory from the Chef server, browse to the top level of the chef\-repo and enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife download roles
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or from anywhere in the chef\-repo, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife download /roles
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To download all cookbooks that start with "apache" and belong to the "webserver" role, browse to the top level of the chef\-repo and enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife download cookbooks/apache\e* roles/webserver.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-edit.1 b/distro/common/man/man1/knife-edit.1
deleted file mode 100644
index bc159e6444..0000000000
--- a/distro/common/man/man1/knife-edit.1
+++ /dev/null
@@ -1,121 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-EDIT" "1" "Chef 12.0" "" "knife edit"
-.SH NAME
-knife-edit \- The man page for the knife edit subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife edit\fP subcommand is used to edit objects on the Chef server\&. This subcommand works similar to \fBknife cookbook edit\fP, \fBknife data bag edit\fP, \fBknife environment edit\fP, \fBknife node edit\fP, and \fBknife role edit\fP, but with a single verb (and a single action).
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife edit (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-\-chef\-repo\-path PATH\fP
-The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-concurrency\fP
-The number of allowed concurrent connections. Default: \fB10\fP\&.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-local\fP
-Use to show files in the local chef\-repo instead of a remote location. Default: \fBfalse\fP\&.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-\-repo\-mode MODE\fP
-The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-environment.1 b/distro/common/man/man1/knife-environment.1
deleted file mode 100644
index d583abe7fa..0000000000
--- a/distro/common/man/man1/knife-environment.1
+++ /dev/null
@@ -1,508 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-ENVIRONMENT" "1" "Chef 12.0" "" "knife environment"
-.SH NAME
-knife-environment \- The man page for the knife environment subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-An environment is a way to map an organization\(aqs real\-life workflow to what can be configured and managed when using Chef server\&. Every organization begins with a single environment called the \fB_default\fP environment, which cannot be modified (or deleted). Additional environments can be created to reflect each organization\(aqs patterns and workflow. For example, creating \fBproduction\fP, \fBstaging\fP, \fBtesting\fP, and \fBdevelopment\fP environments. Generally, an environment is also associated with one (or more) cookbook versions.
-.sp
-The \fBknife environment\fP subcommand is used to manage environments within a single organization on the Chef server\&.
-.SH COMMON OPTIONS
-.sp
-The following options may be used with any of the arguments available to the \fBknife environment\fP subcommand:
-.INDENT 0.0
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.SH COMPARE
-.sp
-The \fBcompare\fP argument is used to compare the cookbook version constraints that are set on one (or more) environments.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment compare [ENVIRONMENT_NAME...] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a\fP, \fB\-\-all\fP
-Use to upload all environments found at the specified path.
-.TP
-.B \fB\-m\fP, \fB\-\-mismatch\fP
-Use to show only matching versions.
-.UNINDENT
-.sp
-\fBExample\fP
-.sp
-To compare cookbook versions for a single environment:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment compare development
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something similar to:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
- development
-apache 2.3.1
-windows 4.1.2
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To compare cookbook versions for multiple environments:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment compare development staging
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something similar to:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
- development staging
-apache 2.3.1 1.2.2
-windows 4.1.2 1.0.0
-postgresql 1.0.0 1.0.0
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To compare all cookbook versions for all environments:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment compare \-\-all
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something similar to:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
- staging development
-ulimit latest latest
-redisio latest latest
-journly latest latest
-aws latest latest
-test latest latest
-unicorn latest latest
-sensu latest latest
-runit latest latest
-templater latest latest
-powershell latest latest
-openssl latest latest
-rbenv latest latest
-rabbitmq latest latest
-postgresql latest latest
-mysql latest latest
-ohai latest latest
-git latest latest
-erlang latest latest
-ssh_known_hosts latest latest
-nginx latest latest
-database latest latest
-yum latest latest
-xfs latest latest
-apt latest latest
-dmg latest latest
-chef_handler latest latest
-windows 1.0.0 4.1.2
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH CREATE
-.sp
-The \fBcreate\fP argument is used to add an environment object to the Chef server\&. When this argument is run, knife will open $EDITOR to enable editing of the \fBENVIRONMENT\fP description field (unless a description is specified as part of the command). When finished, knife will add the environment to the Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment create ENVIRONMENT_NAME \-d DESCRIPTION
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-d DESCRIPTION\fP, \fB\-\-description DESCRIPTION\fP
-The description of the environment. This value will populate the description field for the environment on the Chef server\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To create an environment named \fBdev\fP with a description of \fBThe development environment.\fP:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment create dev \-d "The development environment."
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH DELETE
-.sp
-The \fBdelete\fP argument is used to delete an environment from a Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment delete ENVIRONMENT_NAME
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-To delete an environment named \fBdev\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment delete dev
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Type \fBY\fP to confirm a deletion.
-.SH EDIT
-.sp
-The \fBedit\fP argument is used to edit the attributes of an environment. When this argument is run, knife will open $EDITOR to enable editing of \fBENVIRONMENT\fP attributes. When finished, knife will update the Chef server with those changes.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment edit ENVIRONMENT_NAME
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-To edit an environment named \fBdevops\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment edit devops
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH FROM FILE
-.sp
-The \fBfrom file\fP argument is used to add or update an environment using a JSON or Ruby DSL description. It must be run with the \fBcreate\fP or \fBedit\fP arguments.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment [create | edit] from file FILE (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a\fP, \fB\-\-all\fP
-Use to upload all environments found at the specified path.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To add an environment using data contained in a JSON file:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment create devops from file "path to JSON file"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment edit devops from file "path to JSON file"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH LIST
-.sp
-The \fBlist\fP argument is used to list all of the environments that are currently available on the Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment list \-w
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-w\fP, \fB\-\-with\-uri\fP
-Use to show the corresponding URIs.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To view a list of environments:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment list \-w
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH SHOW
-.sp
-The \fBshow\fP argument is used to display information about the specified environment.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment show ENVIRONMENT_NAME
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-To view information about the \fBdev\fP environment enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife environment show dev
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-% knife environment show dev
-chef_type: environment
-cookbook_versions:
-default_attributes:
-description:
-json_class: Chef::Environment
-name: dev
-override_attributes:
-
-\e\e
-\e\e
-\e\e
-\e\e
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To view information in JSON format, use the \fB\-F\fP common option as part of the command like this:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role show devops \-F json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&.
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-exec.1 b/distro/common/man/man1/knife-exec.1
deleted file mode 100644
index b63491185f..0000000000
--- a/distro/common/man/man1/knife-exec.1
+++ /dev/null
@@ -1,362 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-EXEC" "1" "Chef 12.0" "" "knife exec"
-.SH NAME
-knife-exec \- The man page for the knife exec subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife exec\fP subcommand uses the knife configuration file to execute Ruby scripts in the context of a fully configured chef\-client\&. This subcommand is most often used to run scripts that will only access Chef server one time (or otherwise very infrequently). Use this subcommand any time that an operation does not warrant full usage of the knife subcommand library.
-.sp
-\fBAuthenticated API Requests\fP
-.sp
-The \fBknife exec\fP subcommand can be used to make authenticated API requests to the Chef server using the following methods:
-.TS
-center;
-|l|l|.
-_
-T{
-Method
-T} T{
-Description
-T}
-_
-T{
-\fBapi.delete\fP
-T} T{
-Use to delete an object from the Chef server\&.
-T}
-_
-T{
-\fBapi.get\fP
-T} T{
-Use to get the details of an object on the Chef server\&.
-T}
-_
-T{
-\fBapi.post\fP
-T} T{
-Use to add an object to the Chef server\&.
-T}
-_
-T{
-\fBapi.put\fP
-T} T{
-Use to update an object on the Chef server\&.
-T}
-_
-.TE
-.sp
-These methods are used with the \fB\-E\fP option, which executes that string locally on the workstation using chef\-shell\&. These methods have the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife exec \-E \(aqapi.method(/endpoint)\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-where:
-.INDENT 0.0
-.IP \(bu 2
-\fBapi.method\fP is the corresponding authentication method \-\-\- \fBapi.delete\fP, \fBapi.get\fP, \fBapi.post\fP, or \fBapi.put\fP
-.IP \(bu 2
-\fB/endpoint\fP is an endpoint in the Chef server API
-.UNINDENT
-.sp
-For example, to get the data for a node named "Example_Node":
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife exec \-E \(aqputs api.get("/nodes/Example_Node")\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-and to ensure that the output is visible in the console, add the \fBputs\fP in front of the API authorization request:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife exec \-E \(aqputs api.get("/nodes/Example_Node")\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-where \fBputs\fP is the shorter version of the \fB$stdout.puts\fP predefined variable in Ruby\&.
-.sp
-The following example shows how to add a client named "IBM305RAMAC" and the \fB/clients\fP endpoint, and then return the private key for that user in the console:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ client_desc = {
- "name" => "IBM305RAMAC",
- "admin" => false
- }
-
- new_client = api.post("/clients", client_desc)
- puts new_client["private_key"]
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife exec SCRIPT (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-E CODE\fP, \fB\-\-exec CODE\fP
-A string of code that will be executed.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-p PATH:PATH\fP, \fB\-\-script\-path PATH:PATH\fP
-A colon\-separated path at which Ruby scripts are located.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-There are three ways to use \fBknife exec\fP to run Ruby script files. For example:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife exec /path/to/script_file
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife exec \-E \(aqRUBY CODE\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife exec
-RUBY CODE
-^D
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To check the status of knife using a Ruby script named \fBstatus.rb\fP (which looks like):
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-printf "%\-5s %\-12s %\-8s %s\en", "Check In", "Name", "Ruby", "Recipes"
-nodes.all do |n|
- checkin = Time.at(n[\(aqohai_time\(aq]).strftime("%F %R")
- rubyver = n[\(aqlanguages\(aq][\(aqruby\(aq][\(aqversion\(aq]
- recipes = n.run_list.expand(_default).recipes.join(", ")
- printf "%\-20s %\-12s %\-8s %s\en", checkin, n.name, rubyver, recipes
-end
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-and is located in a directory named \fBscripts/\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife exec scripts/status.rb
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To show the available free memory for all nodes, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife exec \-E \(aqnodes.all {|n| puts "#{n.name} has #{n.memory.total} free memory"}\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To list all of the available search indexes, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife exec \-E \(aqputs api.get("search").keys\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To query a node for multiple attributes using a Ruby script named \fBsearch_attributes.rb\fP (which looks like):
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-% cat scripts/search_attributes.rb
-query = ARGV[2]
-attributes = ARGV[3].split(",")
-puts "Your query: #{query}"
-puts "Your attributes: #{attributes.join(" ")}"
-results = {}
-search(:node, query) do |n|
- results[n.name] = {}
- attributes.each {|a| results[n.name][a] = n[a]}
-end
-
-puts results
-exit 0
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-% knife exec scripts/search_attributes.rb "hostname:test_system" ipaddress,fqdn
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-Your query: hostname:test_system
-Your attributes: ipaddress fqdn
-{"test_system.example.com"=>{"ipaddress"=>"10.1.1.200", "fqdn"=>"test_system.example.com"}}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-index-rebuild.1 b/distro/common/man/man1/knife-index-rebuild.1
deleted file mode 100644
index 3fe2c52f8c..0000000000
--- a/distro/common/man/man1/knife-index-rebuild.1
+++ /dev/null
@@ -1,63 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-INDEX-REBUILD" "1" "Chef 12.0" "" "knife index rebuild"
-.SH NAME
-knife-index-rebuild \- The man page for the knife index rebuild subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife index rebuild\fP subcommand is used to rebuild the search indexes for the open source Chef server\&. This operation is destructive and may take some time.
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-This subcommand ONLY works when run against the open source Chef server version 10.x. This subcommand will NOT run against open source Chef server 11, Enterprise Chef (including hosted Enterprise Chef), or Private Chef\&.
-.UNINDENT
-.UNINDENT
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife index rebuild
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-list.1 b/distro/common/man/man1/knife-list.1
deleted file mode 100644
index c9349fa822..0000000000
--- a/distro/common/man/man1/knife-list.1
+++ /dev/null
@@ -1,174 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-LIST" "1" "Chef 12.0" "" "knife list"
-.SH NAME
-knife-list \- The man page for the knife list subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife list\fP subcommand is used to view a list of objects on the Chef server\&. This subcommand works similar to \fBknife cookbook list\fP, \fBknife data bag list\fP, \fBknife environment list\fP, \fBknife node list\fP, and \fBknife role list\fP, but with a single verb (and a single action).
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife list [PATTERN...] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-1\fP
-Use to show only one column of results. Default: \fBfalse\fP\&.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-\-chef\-repo\-path PATH\fP
-The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-concurrency\fP
-The number of allowed concurrent connections. Default: \fB10\fP\&.
-.TP
-.B \fB\-d\fP
-Use to prevent a directory\(aqs children from showing when a directory matches a pattern. Default value: \fBfalse\fP\&.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-f\fP, \fB\-\-flat\fP
-Use to show a list of file names. Set to \fBfalse\fP to view ls\-like output. Default: \fBfalse\fP\&.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-local\fP
-Use to return only the contents of the local directory. Default: \fBfalse\fP\&.
-.TP
-.B \fB\-p\fP
-Use to show directories with trailing slashes (/). Default: \fBfalse\fP\&.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-R\fP
-Use to list directories recursively. Default: \fBfalse\fP\&.
-.TP
-.B \fB\-\-repo\-mode MODE\fP
-The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-For example, to view a list of roles on the Chef server:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife list roles/
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To view a list of roles and environments on the Chef server:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife list roles/ environments/
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To view a list of absolutely everything on the Chef server:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife list \-R /
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-node.1 b/distro/common/man/man1/knife-node.1
deleted file mode 100644
index af7eab9317..0000000000
--- a/distro/common/man/man1/knife-node.1
+++ /dev/null
@@ -1,716 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-NODE" "1" "Chef 12.0" "" "knife node"
-.SH NAME
-knife-node \- The man page for the knife node subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-A node is any physical, virtual, or cloud machine that is configured to be maintained by a chef\-client\&.
-.sp
-The \fBknife node\fP subcommand is used to manage the nodes that exist on a Chef server\&.
-.SH COMMON OPTIONS
-.sp
-The following options may be used with any of the arguments available to the \fBknife node\fP subcommand:
-.INDENT 0.0
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.SH BULK DELETE
-.sp
-The \fBbulk delete\fP argument is used to delete one or more nodes that match a pattern defined by a regular expression. The regular expression must be within quotes and not be surrounded by forward slashes (/).
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node bulk delete REGEX
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-Use a regular expression to define the pattern used to bulk delete nodes:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node bulk delete "^[0\-9]{3}$"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Type \fBY\fP to confirm a deletion.
-.SH CREATE
-.sp
-The \fBcreate\fP argument is used to add a node to the Chef server\&. Node data is stored as JSON on the Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node create NODE_NAME
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-To add a node, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node create node1
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-In the $EDITOR enter the node data in JSON:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-## sample:
-{
- "normal": {
- },
- "name": "foobar",
- "override": {
- },
- "default": {
- },
- "json_class": "Chef::Node",
- "automatic": {
- },
- "run_list": [
- "recipe[zsh]",
- "role[webserver]"
- ],
- "chef_type": "node"
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-When finished, save it.
-.SH DELETE
-.sp
-The \fBdelete\fP argument is used to delete a node from the Chef server\&.
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-Deleting a node will not delete any corresponding API clients.
-.UNINDENT
-.UNINDENT
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node delete NODE_NAME
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node delete node_name
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH EDIT
-.sp
-The \fBedit\fP argument is used to edit the details of a node on a Chef server\&. Node data is stored as JSON on the Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node edit NODE_NAME (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a\fP, \fB\-\-all\fP
-Displays a node in the $EDITOR\&. By default, attributes that are default, override, or automatic are not shown.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To edit the data for a node named \fBnode1\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node edit node1 \-a
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Update the role data in JSON:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-## sample:
-{
- "normal": {
- },
- "name": "node1",
- "override": {
- },
- "default": {
- },
- "json_class": "Chef::Node",
- "automatic": {
- },
- "run_list": [
- "recipe[devops]",
- "role[webserver]"
- ],
- "chef_type": "node"
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-When finished, save it.
-.SH FROM FILE
-.sp
-The \fBfrom file\fP argument is used to create a node using existing node data as a template.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node from file FILE
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-To add a node using data contained in a JSON file:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node from file "path to JSON file"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH LIST
-.sp
-The \fBlist\fP argument is used to view all of the nodes that exist on a Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node list (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-w\fP, \fB\-\-with\-uri\fP
-Use to show the corresponding URIs.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To verify the list of nodes that are registered with the Chef server, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node list
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something similar to:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-i\-12345678
-rs\-123456
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH RUN_LIST ADD
-.sp
-The \fBrun_list add\fP argument is used to add run\-list items (roles or recipes) to a node.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node run_list add NODE_NAME RUN_LIST_ITEM (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a ITEM\fP, \fB\-\-after ITEM\fP
-Use this to add the run list item after the specified run list item.
-.TP
-.B \fB\-b ITEM\fP, \fB\-\-before ITEM\fP
-Use this to add the run list item before the specified run list item.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To add a role to a run\-list, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node run_list add node \(aqrole[ROLE_NAME]\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To add roles and recipes to a run\-list, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node run_list add node \(aqrecipe[COOKBOOK::RECIPE_NAME],recipe[COOKBOOK::RECIPE_NAME],role[ROLE_NAME]\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To add a recipe to a run\-list using the fully qualified format, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node run_list add node \(aqrecipe[COOKBOOK::RECIPE_NAME]\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To add a recipe to a run\-list using the cookbook format, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node run_list add node \(aqCOOKBOOK::RECIPE_NAME\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To add the default recipe of a cookbook to a run\-list, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node run_list add node \(aqCOOKBOOK\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH RUN_LIST REMOVE
-.sp
-The \fBrun_list remove\fP argument is used to remove run\-list items (roles or recipes) from a node. A recipe must be in one of the following formats: fully qualified, cookbook, or default. Both roles and recipes must be in quotes, for example: \fB\(aqrole[ROLE_NAME]\(aq\fP or \fB\(aqrecipe[COOKBOOK::RECIPE_NAME]\(aq\fP\&. Use a comma to separate roles and recipes when removing more than one, like this: \fB\(aqrecipe[COOKBOOK::RECIPE_NAME],COOKBOOK::RECIPE_NAME,role[ROLE_NAME]\(aq\fP\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node run_list remove NODE_NAME RUN_LIST_ITEM
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-To remove a role from a run\-list, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node run_list remove node \(aqrole[ROLE_NAME]\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To remove a recipe from a run\-list using the fully qualified format, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node run_list remove node \(aqrecipe[COOKBOOK::RECIPE_NAME]\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH SHOW
-.sp
-The \fBshow\fP argument is used to display information about a node.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node show NODE_NAME (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a ATTR\fP, \fB\-\-attribute ATTR\fP
-The attribute (or attributes) to show.
-.TP
-.B \fB\-l\fP, \fB\-\-long\fP
-Use to display all attributes in the output and to show the output as JSON\&.
-.TP
-.B \fB\-m\fP, \fB\-\-medium\fP
-Use to display normal attributes in the output and to show the output as JSON\&.
-.TP
-.B \fB\-r\fP, \fB\-\-run\-list\fP
-Use to show only the run\-list.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To view all data for a node named \fBbuild\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node show build
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-Node Name: build
-Environment: _default
-FQDN:
-IP:
-Run List:
-Roles:
-Recipes:
-Platform:
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To show basic information about a node, truncated and nicely formatted:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-knife node show <node_name>
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To show all information about a node, nicely formatted:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-knife node show \-l <node_name>
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To list a single node attribute:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-knife node show <node_name> \-a <attribute_name>
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-where \fB<attribute_name>\fP is something like kernel or platform. (This doesn\(aqt work for nested attributes like \fBnode[kernel][machine]\fP because \fBknife node show\fP doesn\(aqt understand nested attributes.)
-.sp
-To view the FQDN for a node named \fBi\-12345678\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node show i\-12345678 \-a fqdn
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-fqdn: ip\-10\-251\-75\-20.ec2.internal
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To view the run list for a node named \fBdev\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife node show dev \-r
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To view information in JSON format, use the \fB\-F\fP common option as part of the command like this:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role show devops \-F json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&.
-.sp
-To view node information in raw JSON, use the \fB\-l\fP or \fB\-\-long\fP option:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-knife node show \-l \-F json <node_name>
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-and/or:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-knife node show \-l \-\-format=json <node_name>
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-raw.1 b/distro/common/man/man1/knife-raw.1
deleted file mode 100644
index a484db25bb..0000000000
--- a/distro/common/man/man1/knife-raw.1
+++ /dev/null
@@ -1,172 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-RAW" "1" "Chef 12.0" "" "knife raw"
-.SH NAME
-knife-raw \- The man page for the knife raw subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife raw\fP subcommand is used to send a REST request to an endpoint in the Chef server API\&.
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife raw REQUEST_PATH (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-i FILE\fP, \fB\-\-input FILE\fP
-The name of a file to be used with the \fBPUT\fP or a \fBPOST\fP request.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-m METHOD\fP, \fB\-\-method METHOD\fP
-The request method: \fBDELETE\fP, \fBGET\fP, \fBPOST\fP, or \fBPUT\fP\&. Default value: \fBGET\fP\&.
-.TP
-.B \fB\-\-[no\-]pretty\fP
-Use \fB\-\-no\-pretty\fP to disable pretty\-print output for JSON\&. Default: \fB\-\-pretty\fP\&.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To view information about a client:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-knife raw /clients/<client_name>
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To view information about a node:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-knife raw /nodes/<node_name>
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To delete a data bag, enter a command similar to:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife raw \-m DELETE /data/foo
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something similar to:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-{
- "name":"foo",
- "json_class":"Chef::DataBag",
- "chef_type":"data_bag"
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-recipe-list.1 b/distro/common/man/man1/knife-recipe-list.1
deleted file mode 100644
index 3bd5a4c3aa..0000000000
--- a/distro/common/man/man1/knife-recipe-list.1
+++ /dev/null
@@ -1,85 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-RECIPE-LIST" "1" "Chef 12.0" "" "knife recipe list"
-.SH NAME
-knife-recipe-list \- The man page for the knife recipe list subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife recipe list\fP subcommand is used to view all of the recipes that are on a Chef server\&. A regular expression can be used to limit the results to recipes that match a specific pattern. The regular expression must be within quotes and not be surrounded by forward slashes (/).
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife recipe list REGEX
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-To view a list of recipes:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife recipe list \(aqcouchdb::*\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-couchdb::main_monitors
-couchdb::master
-couchdb::default
-couchdb::org_cleanu
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-role.1 b/distro/common/man/man1/knife-role.1
deleted file mode 100644
index d5b5616d31..0000000000
--- a/distro/common/man/man1/knife-role.1
+++ /dev/null
@@ -1,426 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-ROLE" "1" "Chef 12.0" "" "knife role"
-.SH NAME
-knife-role \- The man page for the knife role subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-A role is a way to define certain patterns and processes that exist across nodes in an organization as belonging to a single job function. Each role consists of zero (or more) attributes and a run\-list. Each node can have zero (or more) roles assigned to it. When a role is run against a node, the configuration details of that node are compared against the attributes of the role, and then the contents of that role\(aqs run\-list are applied to the node\(aqs configuration details. When a chef\-client runs, it merges its own attributes and run\-lists with those contained within each assigned role.
-.sp
-The \fBknife role\fP subcommand is used to manage the roles that are associated with one or more nodes on a Chef server\&.
-.SH COMMON OPTIONS
-.sp
-The following options may be used with any of the arguments available to the \fBknife role\fP subcommand:
-.INDENT 0.0
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.SH BULK DELETE
-.sp
-The \fBbulk delete\fP argument is used to delete one or more roles that match a pattern defined by a regular expression. The regular expression must be within quotes and not be surrounded by forward slashes (/).
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role bulk delete REGEX
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-Use a regular expression to define the pattern used to bulk delete roles:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role bulk delete "^[0\-9]{3}$"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH CREATE
-.sp
-The \fBcreate\fP argument is used to add a role to the Chef server\&. Role data is saved as JSON on the Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role create ROLE_NAME (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-d DESCRIPTION\fP, \fB\-\-description DESCRIPTION\fP
-The description of the role. This value will populate the description field for the role on the Chef server\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To add a role named \fBrole1\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role create role1
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-In the $EDITOR enter the role data in JSON:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-## sample:
-{
- "name": "role1",
- "default_attributes": {
- },
- "json_class": "Chef::Role",
- "run_list": [\(aqrecipe[cookbook_name::recipe_name],
- role[role_name]\(aq
- ],
- "description": "",
- "chef_type": "role",
- "override_attributes": {
- }
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-When finished, save it.
-.SH DELETE
-.sp
-The \fBdelete\fP argument is used to delete a role from the Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role delete ROLE_NAME
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role delete devops
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Type \fBY\fP to confirm a deletion.
-.SH EDIT
-.sp
-The \fBedit\fP argument is used to edit role details on the Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role edit ROLE_NAME
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-To edit the data for a role named \fBrole1\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role edit role1
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Update the role data in JSON:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-## sample:
-{
- "name": "role1",
- "default_attributes": {
- },
- "json_class": "Chef::Role",
- "run_list": [\(aqrecipe[cookbook_name::recipe_name],
- role[role_name]\(aq
- ],
- "description": "This is the description for the role1 role.",
- "chef_type": "role",
- "override_attributes": {
- }
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-When finished, save it.
-.SH FROM FILE
-.sp
-The \fBfrom file\fP argument is used to create a role using existing JSON data as a template.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role from file FILE
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-To view role details based on the values contained in a JSON file:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role from file "path to JSON file"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH LIST
-.sp
-The \fBlist\fP argument is used to view a list of roles that are currently available on the Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role list
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-w\fP, \fB\-\-with\-uri\fP
-Use to show the corresponding URIs.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To view a list of roles on the Chef server and display the URI for each role returned, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role list \-w
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH SHOW
-.sp
-The \fBshow\fP argument is used to view the details of a role.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role show ROLE_NAME
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a ATTR\fP, \fB\-\-attribute ATTR\fP
-The attribute (or attributes) to show.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To view information in JSON format, use the \fB\-F\fP common option as part of the command like this:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role show devops \-F json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&.
-.sp
-To view information in JSON format, use the \fB\-F\fP common option as part of the command like this:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife role show devops \-F json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&.
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-search.1 b/distro/common/man/man1/knife-search.1
deleted file mode 100644
index 3b81898530..0000000000
--- a/distro/common/man/man1/knife-search.1
+++ /dev/null
@@ -1,359 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-SEARCH" "1" "Chef 12.0" "" "knife search"
-.SH NAME
-knife-search \- The man page for the knife search subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-Search indexes allow queries to be made for any type of data that is indexed by the Chef server, including data bags (and data bag items), environments, nodes, and roles. A defined query syntax is used to support search patterns like exact, wildcard, range, and fuzzy. A search is a full\-text query that can be done from several locations, including from within a recipe, by using the \fBsearch\fP subcommand in knife, the \fBsearch\fP method in the Recipe DSL, and by using the \fB/search\fP or \fB/search/INDEX\fP endpoints in the Chef server API\&. The search engine is based on Apache Solr and is run from the Chef server\&.
-.sp
-The \fBknife search\fP subcommand is used run a search query for information that is indexed on a Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife search INDEX SEARCH_QUERY
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-where \fBINDEX\fP is one of \fBclient\fP, \fBenvironment\fP, \fBnode\fP, \fBrole\fP, or the name of a data bag and \fBSEARCH_QUERY\fP is the search query syntax for the query that will be executed.
-.sp
-\fBINDEX\fP is implied if omitted, and will default to \fBnode\fP\&. For example:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife search \(aq*:*\(aq \-i
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-will return something similar to:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-8 items found
-
-centos\-62\-dev
-opensuse\-1203
-ubuntu\-1304\-dev
-ubuntu\-1304\-orgtest
-ubuntu\-1204\-ohai\-test
-ubuntu\-1304\-ifcfg\-test
-ohai\-test
-win2k8\-dev
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-and is the same search as:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife search node \(aq*:*" \-i
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-If the \fBSEARCH_QUERY\fP does not contain a colon character (\fB:\fP), then the default query pattern is \fBtags:*#{@query}* OR roles:*#{@query}* OR fqdn:*#{@query}* OR addresses:*#{@query}*\fP, which means the following two search queries are effectively the same:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife search ubuntu
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife search node "tags:*ubuntu* OR roles:*ubuntu* OR fqdn:*ubuntu* (etc.)"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This sub\-command has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a ATTR\fP, \fB\-\-attribute ATTR\fP
-The attribute (or attributes) to show.
-.TP
-.B \fB\-b ROW\fP, \fB\-\-start ROW\fP
-The row at which return results will begin.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-i\fP, \fB\-\-id\-only\fP
-Use to show only matching object IDs.
-.TP
-.B \fBINDEX\fP
-The name of the index to be queried: \fBclient\fP, \fBenvironment\fP, \fBnode\fP, \fBrole\fP, or \fBDATA_BAG_NAME\fP\&. Default index: \fBnode\fP\&.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-l\fP, \fB\-\-long\fP
-Use to display all attributes in the output and to show the output as JSON\&.
-.TP
-.B \fB\-m\fP, \fB\-\-medium\fP
-Use to display normal attributes in the output and to show the output as JSON\&.
-.TP
-.B \fB\-o SORT\fP, \fB\-\-sort SORT\fP
-The order in which search results will be sorted.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-q SEARCH_QUERY\fP, \fB\-\-query SEARCH_QUERY\fP
-Use to protect search queries that start with a hyphen (\-). A \fB\-q\fP query may be specified as an argument or an option, but not both.
-.TP
-.B \fB\-r\fP, \fB\-\-run\-list\fP
-Use to show only the run\-list.
-.TP
-.B \fB\-R INT\fP, \fB\-\-rows INT\fP
-The number of rows to be returned.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fBSEARCH_QUERY\fP
-The search query used to identify a a list of items on a Chef server\&. This option uses the same syntax as the \fBsearch\fP sub\-command.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To search for the IDs of all nodes running on the Amazon EC2 platform, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife search node \(aqec2:*\(aq \-i
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-4 items found
-
-ip\-0A7CA19F.ec2.internal
-
-ip\-0A58CF8E.ec2.internal
-
-ip\-0A58E134.ec2.internal
-
-ip\-0A7CFFD5.ec2.internal
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To search for the instance type (flavor) of all nodes running on the Amazon EC2 platform, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife search node \(aqec2:*\(aq \-a ec2.instance_type
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-4 items found
-
-ec2.instance_type: m1.large
-id: ip\-0A7CA19F.ec2.internal
-
-ec2.instance_type: m1.large
-id: ip\-0A58CF8E.ec2.internal
-
-ec2.instance_type: m1.large
-id: ip\-0A58E134.ec2.internal
-
-ec2.instance_type: m1.large
-id: ip\-0A7CFFD5.ec2.internal
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To search for all nodes running Ubuntu, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife search node \(aqplatform:ubuntu\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To search for all nodes running CentOS in the production environment, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife search node \(aqchef_environment:production AND platform:centos\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To find a nested attribute, use a pattern similar to the following:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife search node <query_to_run> \-a <main_attribute>.<nested_attribute>
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To build a search query to use more than one attribute, use an underscore (\fB_\fP) to separate each attribute. For example, the following query will search for all nodes running a specific version of Ruby:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife search node "languages_ruby_version:1.9.3"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To build a search query that can find a nested attribute:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife search node name:<node_name> \-a kernel.machine
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To test a search query that will be used in a \fBknife ssh\fP command:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife search node "role:web NOT name:web03"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-where the query in the previous example will search all servers that have the \fBweb\fP role, but not on the server named \fBweb03\fP\&.
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-serve.1 b/distro/common/man/man1/knife-serve.1
deleted file mode 100644
index 8760559f32..0000000000
--- a/distro/common/man/man1/knife-serve.1
+++ /dev/null
@@ -1,109 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-SERVE" "1" "Chef 12.0" "" "knife serve"
-.SH NAME
-knife-serve \- The man page for the knife serve subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife serve\fP subcommand is used to run a persistent chef\-zero against the local chef\-repo\&. (chef\-zero is a lightweight Chef server that runs in\-memory on the local machine.) This is the same as running the chef\-client executable with the \fB\-\-local\-mode\fP option. The \fBchef_repo_path\fP is located automatically and the Chef server will bind to the first available port between \fB8889\fP and \fB9999\fP\&. \fBknife serve\fP will print the URL for the local Chef server, so that it may be added to the knife.rb file.
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife serve (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-show.1 b/distro/common/man/man1/knife-show.1
deleted file mode 100644
index 94f295afb7..0000000000
--- a/distro/common/man/man1/knife-show.1
+++ /dev/null
@@ -1,160 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-SHOW" "1" "Chef 12.0" "" "knife show"
-.SH NAME
-knife-show \- The man page for the knife show subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife show\fP subcommand is used to view the details of one (or more) objects on the Chef server\&. This subcommand works similar to \fBknife cookbook show\fP, \fBknife data bag show\fP, \fBknife environment show\fP, \fBknife node show\fP, and \fBknife role show\fP, but with a single verb (and a single action).
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife show [PATTERN...] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a ATTR\fP, \fB\-\-attribute ATTR\fP
-The attribute (or attributes) to show.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-\-chef\-repo\-path PATH\fP
-The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-concurrency\fP
-The number of allowed concurrent connections. Default: \fB10\fP\&.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-local\fP
-Use to show local files instead of remote files.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-\-repo\-mode MODE\fP
-The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To show all cookbooks in the \fBcookbooks/\fP directory:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife show cookbooks/
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or, (if already in the \fBcookbooks/\fP directory in the local chef\-repo):
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife show
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife show roles/ environments/
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-ssh.1 b/distro/common/man/man1/knife-ssh.1
deleted file mode 100644
index 78555b7293..0000000000
--- a/distro/common/man/man1/knife-ssh.1
+++ /dev/null
@@ -1,284 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-SSH" "1" "Chef 12.0" "" "knife ssh"
-.SH NAME
-knife-ssh \- The man page for the knife ssh subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife ssh\fP subcommand is used to invoke SSH commands (in parallel) on a subset of nodes within an organization, based on the results of a \fI\%search query\fP made to the Chef server\&.
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssh SEARCH_QUERY SSH_COMMAND (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a SSH_ATTR\fP, \fB\-\-attribute SSH_ATTR\fP
-The attribute that is used when opening the SSH connection. The default attribute is the FQDN of the host. Other possible values include a public IP address, a private IP address, or a hostname.
-.TP
-.B \fB\-A\fP, \fB\-\-forward\-agent\fP
-Use to enable SSH agent forwarding.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-C NUM\fP, \fB\-\-concurrency NUM\fP
-The number of allowed concurrent connections.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-G GATEWAY\fP, \fB\-\-ssh\-gateway GATEWAY\fP
-The SSH tunnel or gateway that is used to run a bootstrap action on a machine that is not accessible from the workstation.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-i IDENTITY_FILE\fP, \fB\-\-identity\-file IDENTIFY_FILE\fP
-The SSH identity file used for authentication. Key\-based authentication is recommended.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-m\fP, \fB\-\-manual\-list\fP
-Use to define a search query as a space\-separated list of servers. If there is more than one item in the list, put quotes around the entire list. For example: \fB\-\-manual\-list "server01 server 02 server 03"\fP
-.TP
-.B \fB\-\-[no\-]host\-key\-verify\fP
-Use \fB\-\-no\-host\-key\-verify\fP to disable host key verification. Default setting: \fB\-\-host\-key\-verify\fP\&.
-.TP
-.B \fBOTHER\fP
-The shell type. Possible values: \fBinteractive\fP, \fBscreen\fP, \fBtmux\fP, \fBmacterm\fP, or \fBcssh\fP\&. (\fBcsshx\fP is deprecated in favor of \fBcssh\fP\&.)
-.TP
-.B \fB\-p PORT\fP, \fB\-\-ssh\-port PORT\fP
-The SSH port.
-.TP
-.B \fB\-P PASSWORD\fP, \fB\-\-ssh\-password PASSWORD\fP
-The SSH password. This can be used to pass the password directly on the command line. If this option is not specified (and a password is required) knife will prompt for the password.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fBSEARCH_QUERY\fP
-The search query used to return a list of servers to be accessed using SSH and the specified \fBSSH_COMMAND\fP\&. This option uses the same syntax as the search sub\-command.
-.TP
-.B \fBSSH_COMMAND\fP
-The command that will be run against the results of a search query.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-x USER_NAME\fP, \fB\-\-ssh\-user USER_NAME\fP
-The SSH user name.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To find the uptime of all of web servers running Ubuntu on the Amazon EC2 platform, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssh "role:web" "uptime" \-x ubuntu \-a ec2.public_hostname
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-ec2\-174\-129\-127\-206.compute\-1.amazonaws.com 13:50:47 up 1 day, 23:26, 1 user, load average: 0.25, 0.18, 0.11
-ec2\-67\-202\-63\-102.compute\-1.amazonaws.com 13:50:47 up 1 day, 23:33, 1 user, load average: 0.12, 0.13, 0.10
-ec2\-184\-73\-9\-250.compute\-1.amazonaws.com 13:50:48 up 16:45, 1 user, load average: 0.30, 0.22, 0.13
-ec2\-75\-101\-240\-230.compute\-1.amazonaws.com 13:50:48 up 1 day, 22:59, 1 user, load average: 0.24, 0.17, 0.11
-ec2\-184\-73\-60\-141.compute\-1.amazonaws.com 13:50:48 up 1 day, 23:30, 1 user, load average: 0.32, 0.17, 0.15
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssh \(aqname:*\(aq \(aqsudo chef\-client\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To force a chef\-client run on all of the web servers running Ubuntu on the Amazon EC2 platform, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssh "role:web" "sudo chef\-client" \-x ubuntu \-a ec2.public_hostname
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-ec2\-67\-202\-63\-102.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:37 +0000] INFO: Starting Chef Run (Version 0.9.10)
-ec2\-174\-129\-127\-206.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:37 +0000] INFO: Starting Chef Run (Version 0.9.10)
-ec2\-184\-73\-9\-250.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:38 +0000] INFO: Starting Chef Run (Version 0.9.10)
-ec2\-75\-101\-240\-230.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:38 +0000] INFO: Starting Chef Run (Version 0.9.10)
-ec2\-184\-73\-60\-141.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:38 +0000] INFO: Starting Chef Run (Version 0.9.10)
-ec2\-174\-129\-127\-206.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: Chef Run complete in 1.419243 seconds
-ec2\-174\-129\-127\-206.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: cleaning the checksum cache
-ec2\-174\-129\-127\-206.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: Running report handlers
-ec2\-174\-129\-127\-206.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: Report handlers complete
-ec2\-67\-202\-63\-102.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: Chef Run complete in 1.578265 seconds
-ec2\-67\-202\-63\-102.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: cleaning the checksum cache
-ec2\-67\-202\-63\-102.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: Running report handlers
-ec2\-67\-202\-63\-102.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: Report handlers complete
-ec2\-184\-73\-9\-250.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Chef Run complete in 1.638884 seconds
-ec2\-184\-73\-9\-250.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: cleaning the checksum cache
-ec2\-184\-73\-9\-250.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Running report handlers
-ec2\-184\-73\-9\-250.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Report handlers complete
-ec2\-75\-101\-240\-230.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Chef Run complete in 1.540257 seconds
-ec2\-75\-101\-240\-230.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: cleaning the checksum cache
-ec2\-75\-101\-240\-230.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Running report handlers
-ec2\-75\-101\-240\-230.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Report handlers complete
-ec2\-184\-73\-60\-141.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Chef Run complete in 1.502489 seconds
-ec2\-184\-73\-60\-141.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: cleaning the checksum cache
-ec2\-184\-73\-60\-141.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Running report handlers
-ec2\-184\-73\-60\-141.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Report handlers complete
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To query for all nodes that have the \fBwebserver\fP role and then use SSH to run the command \fBsudo chef\-client\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssh "role:webserver" "sudo chef\-client"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssh name:* "sudo aptitude upgrade \-y"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To specify the shell type used on the nodes returned by a search query:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssh roles:opscode\-omnitruck macterm
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-where \fBscreen\fP is one of the following values: \fBcssh\fP, \fBinteractive\fP, \fBmacterm\fP, \fBscreen\fP, or \fBtmux\fP\&. If the node does not have the shell type installed, knife will return an error similar to the following:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-you need the rb\-appscript gem to use knife ssh macterm.
-\(ga(sudo) gem install rb\-appscript\(ga to install
-ERROR: LoadError: cannot load such file \-\- appscript
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-ssl-check.1 b/distro/common/man/man1/knife-ssl-check.1
deleted file mode 100644
index 23b945468d..0000000000
--- a/distro/common/man/man1/knife-ssl-check.1
+++ /dev/null
@@ -1,207 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-SSL-CHECK" "1" "Chef 12.0" "" "knife ssl check"
-.SH NAME
-knife-ssl-check \- The man page for the knife ssl check subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife ssl check\fP subcommand is used to verify the SSL configuration for the Enterprise Chef and/or Open Source Chef servers, or at another location specified by a URL or URI.
-.sp
-\fBWARNING:\fP
-.INDENT 0.0
-.INDENT 3.5
-When verification of a remote server\(aqs SSL certificate is disabled, the chef\-client will issue a warning similar to "SSL validation of HTTPS requests is disabled. HTTPS connections are still encrypted, but the chef\-client is not able to detect forged replies or man\-in\-the\-middle attacks." To configure SSL for the chef\-client, set \fBssl_verify_mode\fP to \fB:verify_peer\fP (recommended) \fBor\fP \fBverify_api_cert\fP to \fBtrue\fP in the client.rb file.
-.UNINDENT
-.UNINDENT
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssl check URI
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a SSH_ATTR\fP, \fB\-\-attribute SSH_ATTR\fP
-The attribute that is used when opening the SSH connection. The default attribute is the FQDN of the host. Other possible values include a public IP address, a private IP address, or a hostname.
-.TP
-.B \fB\-A\fP, \fB\-\-forward\-agent\fP
-Use to enable SSH agent forwarding.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-C NUM\fP, \fB\-\-concurrency NUM\fP
-The number of allowed concurrent connections.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-G GATEWAY\fP, \fB\-\-ssh\-gateway GATEWAY\fP
-The SSH tunnel or gateway that is used to run a bootstrap action on a machine that is not accessible from the workstation.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-i IDENTITY_FILE\fP, \fB\-\-identity\-file IDENTIFY_FILE\fP
-The SSH identity file used for authentication. Key\-based authentication is recommended.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-m\fP, \fB\-\-manual\-list\fP
-Use to define a search query as a space\-separated list of servers. If there is more than one item in the list, put quotes around the entire list. For example: \fB\-\-manual\-list "server01 server 02 server 03"\fP
-.TP
-.B \fB\-\-[no\-]host\-key\-verify\fP
-Use \fB\-\-no\-host\-key\-verify\fP to disable host key verification. Default setting: \fB\-\-host\-key\-verify\fP\&.
-.TP
-.B \fBOTHER\fP
-The shell type. Possible values: \fBinteractive\fP, \fBscreen\fP, \fBtmux\fP, \fBmacterm\fP, or \fBcssh\fP\&. (\fBcsshx\fP is deprecated in favor of \fBcssh\fP\&.)
-.TP
-.B \fB\-p PORT\fP, \fB\-\-ssh\-port PORT\fP
-The SSH port.
-.TP
-.B \fB\-P PASSWORD\fP, \fB\-\-ssh\-password PASSWORD\fP
-The SSH password. This can be used to pass the password directly on the command line. If this option is not specified (and a password is required) knife will prompt for the password.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fBSEARCH_QUERY\fP
-The search query used to return a list of servers to be accessed using SSH and the specified \fBSSH_COMMAND\fP\&. This option uses the same syntax as the search sub\-command.
-.TP
-.B \fBSSH_COMMAND\fP
-The command that will be run against the results of a search query.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-x USER_NAME\fP, \fB\-\-ssh\-user USER_NAME\fP
-The SSH user name.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-The following examples show how to use this knife subcommand:
-.sp
-\fBVerify the SSL configuration for the Chef server\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssl check
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBVerify the SSL configuration for the chef\-client\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssl check \-c /etc/chef/client.rb
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBVerify an external server\(aqs SSL certificate\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssl check URL_or_URI
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-for example:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssl check https://www.getchef.com
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-ssl-fetch.1 b/distro/common/man/man1/knife-ssl-fetch.1
deleted file mode 100644
index 88b0ad1ebf..0000000000
--- a/distro/common/man/man1/knife-ssl-fetch.1
+++ /dev/null
@@ -1,207 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-SSL-FETCH" "1" "Chef 12.0" "" "knife ssl fetch"
-.SH NAME
-knife-ssl-fetch \- The man page for the knife ssl fetch subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife ssl fetch\fP subcommand is used to copy SSL certificates from an HTTPS server to the \fBtrusted_certs_dir\fP directory that is used by knife and the chef\-client to store trusted SSL certificates. When these certificates match the hostname of the remote server, running \fBknife ssl fetch\fP is the only step required to verify a remote server that is accessed by either knife or the chef\-client\&.
-.sp
-\fBWARNING:\fP
-.INDENT 0.0
-.INDENT 3.5
-It is the user\(aqs responsibility to verify the authenticity of every SSL certificate before downloading it to the \fBtrusted_certs_dir\fP directory. knife will use any certificate in that directory as if it is a 100% trusted and authentic SSL certificate. knife will not be able to determine if any certificate in this directory has been tampered with, is forged, malicious, or otherwise harmful. Therefore it is essential that users take the proper steps before downloading certificates into this directory.
-.UNINDENT
-.UNINDENT
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssl fetch URI_FOR_HTTPS_SERVER
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a SSH_ATTR\fP, \fB\-\-attribute SSH_ATTR\fP
-The attribute that is used when opening the SSH connection. The default attribute is the FQDN of the host. Other possible values include a public IP address, a private IP address, or a hostname.
-.TP
-.B \fB\-A\fP, \fB\-\-forward\-agent\fP
-Use to enable SSH agent forwarding.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-C NUM\fP, \fB\-\-concurrency NUM\fP
-The number of allowed concurrent connections.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-G GATEWAY\fP, \fB\-\-ssh\-gateway GATEWAY\fP
-The SSH tunnel or gateway that is used to run a bootstrap action on a machine that is not accessible from the workstation.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-i IDENTITY_FILE\fP, \fB\-\-identity\-file IDENTIFY_FILE\fP
-The SSH identity file used for authentication. Key\-based authentication is recommended.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-m\fP, \fB\-\-manual\-list\fP
-Use to define a search query as a space\-separated list of servers. If there is more than one item in the list, put quotes around the entire list. For example: \fB\-\-manual\-list "server01 server 02 server 03"\fP
-.TP
-.B \fB\-\-[no\-]host\-key\-verify\fP
-Use \fB\-\-no\-host\-key\-verify\fP to disable host key verification. Default setting: \fB\-\-host\-key\-verify\fP\&.
-.TP
-.B \fBOTHER\fP
-The shell type. Possible values: \fBinteractive\fP, \fBscreen\fP, \fBtmux\fP, \fBmacterm\fP, or \fBcssh\fP\&. (\fBcsshx\fP is deprecated in favor of \fBcssh\fP\&.)
-.TP
-.B \fB\-p PORT\fP, \fB\-\-ssh\-port PORT\fP
-The SSH port.
-.TP
-.B \fB\-P PASSWORD\fP, \fB\-\-ssh\-password PASSWORD\fP
-The SSH password. This can be used to pass the password directly on the command line. If this option is not specified (and a password is required) knife will prompt for the password.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fBSEARCH_QUERY\fP
-The search query used to return a list of servers to be accessed using SSH and the specified \fBSSH_COMMAND\fP\&. This option uses the same syntax as the search sub\-command.
-.TP
-.B \fBSSH_COMMAND\fP
-The command that will be run against the results of a search query.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-x USER_NAME\fP, \fB\-\-ssh\-user USER_NAME\fP
-The SSH user name.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-The following examples show how to use this knife subcommand:
-.sp
-\fBFetch the SSL certificates used by Knife from the Chef server\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssl fetch
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBFetch the SSL certificates used by the chef\-client from the Chef server\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssl fetch \-c /etc/chef/client.rb
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBFetch SSL certificates from a URL or URI\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssl fetch URL_or_URI
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-for example:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife ssl fetch https://www.getchef.com
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-status.1 b/distro/common/man/man1/knife-status.1
deleted file mode 100644
index 038cf3a0f0..0000000000
--- a/distro/common/man/man1/knife-status.1
+++ /dev/null
@@ -1,234 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-STATUS" "1" "Chef 12.0" "" "knife status"
-.SH NAME
-knife-status \- The man page for the knife status subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife status\fP subcommand is used to display a brief summary of the nodes on a Chef server, including the time of the most recent successful chef\-client run.
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife status (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-H\fP, \fB\-\-hide\-healthy\fP
-Use to hide nodes on which a chef\-client run has occurred within the previous hour.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fBSEARCH_QUERY\fP
-The search query used to identify a a list of items on a Chef server\&. This option uses the same syntax as the \fBsearch\fP sub\-command.
-.TP
-.B \fB\-r RUN_LIST\fP, \fB\-\-run\-list RUN_LIST\fP
-A comma\-separated list of roles and/or recipes to be applied.
-.TP
-.B \fB\-s\fP, \fB\-\-sort\-reverse\fP
-Use to sort a list by last run time, descending.
-.TP
-.B \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To include run lists in the status, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife status \-\-run\-list
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-20 hours ago, dev\-vm.chisamore.com, ubuntu 10.04, dev\-vm.chisamore.com, 10.66.44.126, role[lb].
-3 hours ago, i\-225f954f, ubuntu 10.04, ec2\-67\-202\-63\-102.compute\-1.amazonaws.com, 67.202.63.102, role[web].
-3 hours ago, i\-a45298c9, ubuntu 10.04, ec2\-174\-129\-127\-206.compute\-1.amazonaws.com, 174.129.127.206, role[web].
-3 hours ago, i\-5272a43f, ubuntu 10.04, ec2\-184\-73\-9\-250.compute\-1.amazonaws.com, 184.73.9.250, role[web].
-3 hours ago, i\-226ca64f, ubuntu 10.04, ec2\-75\-101\-240\-230.compute\-1.amazonaws.com, 75.101.240.230, role[web].
-3 hours ago, i\-f65c969b, ubuntu 10.04, ec2\-184\-73\-60\-141.compute\-1.amazonaws.com, 184.73.60.141, role[web].
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To show the status for nodes on which the chef\-client did not run successfully within the past hour, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife status \-\-hide\-healthy
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-1 hour ago, i\-256f884f, ubuntu 12.04, ec2\-67\-202\-63\-102.compute\-1.amazonaws.com, 67.202.63.102, role[web].
-1 hour ago, i\-a47823c9, ubuntu 10.04, ec2\-174\-129\-127\-206.compute\-1.amazonaws.com, 184.129.143.111, role[lb].
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To show the status of a subset of nodes that are returned by a specific query, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife status "role:web" \-\-run\-list
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-3 hours ago, i\-225f954f, ubuntu 10.04, ec2\-67\-202\-63\-102.compute\-1.amazonaws.com, 67.202.63.102, role[web].
-3 hours ago, i\-a45298c9, ubuntu 10.04, ec2\-174\-129\-127\-206.compute\-1.amazonaws.com, 174.129.127.206, role[web].
-3 hours ago, i\-5272a43f, ubuntu 10.04, ec2\-184\-73\-9\-250.compute\-1.amazonaws.com, 184.73.9.250, role[web].
-3 hours ago, i\-226ca64f, ubuntu 10.04, ec2\-75\-101\-240\-230.compute\-1.amazonaws.com, 75.101.240.230, role[web].
-3 hours ago, i\-f65c969b, ubuntu 10.04, ec2\-184\-73\-60\-141.compute\-1.amazonaws.com, 184.73.60.141, role[web].
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To view the status of all nodes in the organization, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife status
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-20 hours ago, dev\-vm.chisamore.com, ubuntu 10.04, dev\-vm.chisamore.com, 10.66.44.126
-3 hours ago, i\-225f954f, ubuntu 10.04, ec2\-67\-202\-63\-102.compute\-1.amazonaws.com, 67.202.63.102
-3 hours ago, i\-a45298c9, ubuntu 10.04, ec2\-174\-129\-127\-206.compute\-1.amazonaws.com, 174.129.127.206
-3 hours ago, i\-5272a43f, ubuntu 10.04, ec2\-184\-73\-9\-250.compute\-1.amazonaws.com, 184.73.9.250
-3 hours ago, i\-226ca64f, ubuntu 10.04, ec2\-75\-101\-240\-230.compute\-1.amazonaws.com, 75.101.240.230
-3 hours ago, i\-f65c969b, ubuntu 10.04, ec2\-184\-73\-60\-141.compute\-1.amazonaws.com, 184.73.60.141
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-tag.1 b/distro/common/man/man1/knife-tag.1
deleted file mode 100644
index 3149f520f1..0000000000
--- a/distro/common/man/man1/knife-tag.1
+++ /dev/null
@@ -1,189 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-TAG" "1" "Chef 12.0" "" "knife tag"
-.SH NAME
-knife-tag \- The man page for the knife tag subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-A tag is a custom description that is applied to a node. A tag, once applied, can be helpful when managing nodes using knife or when building recipes by providing alternate methods of grouping similar types of information.
-.sp
-The \fBknife tag\fP subcommand is used to apply tags to nodes on a Chef server\&.
-.SH COMMON OPTIONS
-.sp
-The following options may be used with any of the arguments available to the \fBknife tag\fP subcommand:
-.INDENT 0.0
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.SH CREATE
-.sp
-The \fBcreate\fP argument is used to add one or more tags to a node.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife tag create NODE_NAME [TAG...]
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-To create tags named \fBseattle\fP, \fBportland\fP, and \fBvancouver\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife tag create node seattle portland vancouver
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH DELETE
-.sp
-The \fBdelete\fP argument is used to delete one or more tags from a node.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife tag delete NODE_NAME [TAG...]
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-To delete tags named \fBdenver\fP and \fBphoenix\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife tag delete node denver phoenix
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Type \fBY\fP to confirm a deletion.
-.SH LIST
-.sp
-The \fBlist\fP argument is used to list all of the tags that have been applied to a node.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife tag list [NODE_NAME...]
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-upload.1 b/distro/common/man/man1/knife-upload.1
deleted file mode 100644
index dfc20e0270..0000000000
--- a/distro/common/man/man1/knife-upload.1
+++ /dev/null
@@ -1,280 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-UPLOAD" "1" "Chef 12.0" "" "knife upload"
-.SH NAME
-knife-upload \- The man page for the knife upload subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife upload\fP subcommand is used to upload roles, cookbooks, environments, and data bags to the Chef server from the current working directory in the chef\-repo\&. This subcommand is often used in conjunction with \fBknife diff\fP, which can be used to see exactly what changes will be uploaded, and then \fBknife download\fP, which does the opposite of \fBknife upload\fP\&.
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife upload [PATTERN...] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-\-chef\-repo\-path PATH\fP
-The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-concurrency\fP
-The number of allowed concurrent connections. Default: \fB10\fP\&.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-\-[no\-]diff\fP
-Use to upload only new and modified files. Set to \fBfalse\fP to upload all files. Default: \fBtrue\fP\&.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-\-[no\-]force\fP
-Use \fB\-\-force\fP to upload roles, cookbooks, etc. even if the file in the directory is identical (by default, no \fBPOST\fP or \fBPUT\fP is performed unless an actual change would be made). Default: \fB\-\-no\-force\fP\&.
-.TP
-.B \fB\-\-[no\-]freeze\fP
-Use to require changes to a cookbook be included as a new version. Only the \fB\-\-force\fP option can override this setting. Default: \fBfalse\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-n\fP, \fB\-\-dry\-run\fP
-Use to take no action and only print out results. Default: \fBfalse\fP\&.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-\-[no\-]purge\fP
-Use \fB\-\-purge\fP to delete roles, cookbooks, etc. from the Chef server if their corresponding files do not exist in the chef\-repo\&. By default, such objects are left alone and NOT purged. Default: \fB\-\-no\-purge\fP\&.
-.TP
-.B \fB\-\-[no\-]recurse\fP
-Use \fB\-\-no\-recurse\fP to disable uploading a directory recursively. Default: \fB\-\-recurse\fP\&.
-.TP
-.B \fB\-\-repo\-mode MODE\fP
-The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-Browse to the top level of the chef\-repo and enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife upload
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or from anywhere in the chef\-repo, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife upload /
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Browse to the top level of the chef\-repo and enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife upload cookbooks
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or from anywhere in the chef\-repo, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife upload /cookbooks
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Browse to the top level of the chef\-repo and enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife upload environments
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or from anywhere in the chef\-repo, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife upload /environments
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Browse to the top level of the chef\-repo and enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife upload environments/production.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or from the \fBenvironments/\fP directory, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife upload production.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Browse to the top level of the chef\-repo and enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife upload roles
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or from anywhere in the chef\-repo, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife upload /roles
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Browse to the top level of the chef\-repo and enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife upload cookbooks/apache\e* roles/webserver.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife upload \(gaknife deps nodes/*.json\(ga
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-user.1 b/distro/common/man/man1/knife-user.1
deleted file mode 100644
index cdd15b9d14..0000000000
--- a/distro/common/man/man1/knife-user.1
+++ /dev/null
@@ -1,356 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-USER" "1" "Chef 12.0" "" "knife user"
-.SH NAME
-knife-user \- The man page for the knife user subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife user\fP subcommand is used to manage the list of users and their associated RSA public key\-pairs.
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-This subcommand ONLY works when run against the open source Chef server version 10.x. This subcommand will NOT run against open source Chef server 11, Enterprise Chef (including hosted Enterprise Chef), or Private Chef\&.
-.UNINDENT
-.UNINDENT
-.SH COMMON OPTIONS
-.sp
-The following options may be used with any of the arguments available to the \fBknife user\fP subcommand:
-.INDENT 0.0
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.SH CREATE
-.sp
-The \fBcreate\fP argument is used to create a user. This process will generate an RSA key pair for the named user. The public key will be stored on the Chef server and the private key will be displayed on \fBSTDOUT\fP or written to a named file.
-.INDENT 0.0
-.IP \(bu 2
-For the user, the private key should be copied to the system as \fB/etc/chef/client.pem\fP\&.
-.IP \(bu 2
-For knife, the private key is typically copied to \fB~/.chef/client_name.pem\fP and referenced in the knife.rb configuration file.
-.UNINDENT
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife user create USER_NAME (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a\fP, \fB\-\-admin\fP
-Use to create a client as an admin client. This is required for any user to access Open Source Chef as an administrator. This option only works when used with the open source Chef server and will have no effect when used with Enterprise Chef\&.
-.TP
-.B \fB\-f FILE_NAME\fP, \fB\-\-file FILE_NAME\fP
-Use to save a private key to the specified file name.
-.TP
-.B \fB\-p PASSWORD\fP, \fB\-\-password PASSWORD\fP
-The user password.
-.TP
-.B \fB\-\-user\-key FILE_NAME\fP
-All users are assigned a public key. Use to write the public key to a file.
-.UNINDENT
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife user create "Radio Birdman" \-f /keys/user_name
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH DELETE
-.sp
-The \fBdelete\fP argument is used to delete a registered user.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife user delete USER_NAME
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife user delete "Steve Danno"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH EDIT
-.sp
-The \fBedit\fP argument is used to edit the details of a user. When this argument is run, knife will open $EDITOR\&. When finished, knife will update the Chef server with those changes.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife user edit USER_NAME
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This command does not have any specific options.
-.sp
-\fBExamples\fP
-.sp
-None.
-.SH LIST
-.sp
-The \fBlist\fP argument is used to view a list of registered users.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife user list (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-w\fP, \fB\-\-with\-uri\fP
-Use to show the corresponding URIs.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-None.
-.SH REREGISTER
-.sp
-The \fBreregister\fP argument is used to regenerate an RSA key pair for a user. The public key will be stored on the Chef server and the private key will be displayed on \fBSTDOUT\fP or written to a named file.
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-Running this argument will invalidate the previous RSA key pair, making it unusable during authentication to the Chef server\&.
-.UNINDENT
-.UNINDENT
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife user reregister USER_NAME (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-f FILE_NAME\fP, \fB\-\-file FILE_NAME\fP
-Use to save a private key to the specified file name.
-.UNINDENT
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife user reregister "Robert Younger"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH SHOW
-.sp
-The \fBshow\fP argument is used to show the details of a user.
-.sp
-\fBSyntax\fP
-.sp
-This argument has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife user show USER_NAME (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This argument has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-a ATTR\fP, \fB\-\-attribute ATTR\fP
-The attribute (or attributes) to show.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-To view a user named \fBDennis Teck\fP, enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife user show "Dennis Teck"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-chef_type: user
-json_class: Chef::User
-name: Dennis Teck
-public_key:
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-To view information in JSON format, use the \fB\-F\fP common option as part of the command like this:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife user show "Dennis Teck" \-F json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-(Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP, e.g. \fB\-F yaml\fP for YAML\&.)
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife-xargs.1 b/distro/common/man/man1/knife-xargs.1
deleted file mode 100644
index 1fcc3d9f4a..0000000000
--- a/distro/common/man/man1/knife-xargs.1
+++ /dev/null
@@ -1,189 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE-XARGS" "1" "Chef 12.0" "" "knife xargs"
-.SH NAME
-knife-xargs \- The man page for the knife xargs subcommand.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-The \fBknife xargs\fP subcommand is used to take patterns from standard input, download as JSON, run a command against the downloaded JSON, and then upload any changes.
-.sp
-\fBSyntax\fP
-.sp
-This subcommand has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife xargs [PATTERN...] (options)
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBOptions\fP
-.sp
-This subcommand has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-0\fP
-Use to show a \fBNULL\fP character (\fB\e0\fP) instead of white space as the separator. Default: \fBfalse\fP\&.
-.TP
-.B \fB\-\-chef\-repo\-path PATH\fP
-The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb.
-.TP
-.B \fB\-\-concurrency\fP
-The number of allowed concurrent connections. Default: \fB10\fP\&.
-.TP
-.B \fB\-\-[no\-]diff\fP
-Use to show a diff when a file changes. Default: \fB\-\-diff\fP\&.
-.TP
-.B \fB\-\-dry\-run\fP
-Use to prevent changes from being uploaded to the Chef server\&. Default: \fBfalse\fP\&.
-.TP
-.B \fB\-\-[no\-]force\fP
-Use to force the upload of files even if they haven\(aqt been changed. Default: \fB\-\-no\-force\fP\&.
-.TP
-.B \fB\-I REPLACE_STRING\fP, \fB\-\-replace REPLACE_STRING\fP
-Use to define a string that will be used to replace all occurrences of a file name. Default: \fBnil\fP\&.
-.TP
-.B \fB\-J REPLACE_STRING\fP, \fB\-\-replace\-first REPLACE_STRING\fP
-Use to define a string that will be used to replace the first occurrence of a file name. Default: \fBnil\fP\&.
-.TP
-.B \fB\-\-local\fP
-Use to build or execute a command line against a local file. Set to \fBfalse\fP to build or execute against a remote file. Default: \fBfalse\fP\&.
-.TP
-.B \fB\-n MAX_ARGS\fP, \fB\-\-max\-args MAX_ARGS\fP
-The maximum number of arguments per command line. Default: \fBnil\fP\&.
-.TP
-.B \fB\-p [PATTERN...]\fP, \fB\-\-pattern [PATTERN...]\fP
-One (or more) patterns for a command line. If this option is not specified, a list of patterns may be expected on standard input. Default: \fBnil\fP\&.
-.TP
-.B \fB\-\-repo\-mode MODE\fP
-The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default value: \fBdefault\fP\&.
-.TP
-.B \fB\-s LENGTH\fP, \fB\-\-max\-chars LENGTH\fP
-The maximum size (in characters) for a command line. Default: \fBnil\fP\&.
-.TP
-.B \fB\-t\fP
-Use to run the print command on the command line. Default: \fBnil\fP\&.
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.sp
-\fBExamples\fP
-.sp
-The following examples show various ways of listing all nodes on the server, and then using Perl to replace \fBgrantmc\fP with \fBgmc\fP:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife list \(aqnodes/*\(aq | knife xargs "perl \-i \-pe \(aqs/grantmc/gmc\(aq"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or without quotes and the backslash escaped:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife list /nodes/\e* | knife xargs "perl \-i \-pe \(aqs/grantmc/gmc\(aq"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-or by using the \fB\-\-pattern\fP option:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife xargs \-\-pattern \(aq/nodes.*\(aq "perl \-i \-pe \(aqs/grantmc/gmc\(aq"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man1/knife.1 b/distro/common/man/man1/knife.1
deleted file mode 100644
index 9259da8477..0000000000
--- a/distro/common/man/man1/knife.1
+++ /dev/null
@@ -1,332 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "KNIFE" "1" "Chef 12.0" "" "knife"
-.SH NAME
-knife \- The man page for the knife command line tool.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-knife is a command\-line tool that provides an interface between a local chef\-repo and the Chef server\&. knife helps users to manage:
-.INDENT 0.0
-.IP \(bu 2
-Nodes
-.IP \(bu 2
-Cookbooks and recipes
-.IP \(bu 2
-Roles
-.IP \(bu 2
-Stores of JSON data (data bags), including encrypted data
-.IP \(bu 2
-Environments
-.IP \(bu 2
-Cloud resources, including provisioning
-.IP \(bu 2
-The installation of the chef\-client on management workstations
-.IP \(bu 2
-Searching of indexed data on the Chef server
-.UNINDENT
-.sp
-Knife subcommands:
-.INDENT 0.0
-.IP \(bu 2
-knife bootstrap
-.IP \(bu 2
-knife client
-.IP \(bu 2
-knife configure
-.IP \(bu 2
-knife cookbook
-.IP \(bu 2
-knife cookbook site
-.IP \(bu 2
-knife data bag
-.IP \(bu 2
-knife delete
-.IP \(bu 2
-knife deps
-.IP \(bu 2
-knife diff
-.IP \(bu 2
-knife download
-.IP \(bu 2
-knife edit
-.IP \(bu 2
-knife environment
-.IP \(bu 2
-knife exec
-.IP \(bu 2
-knife list
-.IP \(bu 2
-knife node
-.IP \(bu 2
-knife raw
-.IP \(bu 2
-knife recipe list
-.IP \(bu 2
-knife role
-.IP \(bu 2
-knife search
-.IP \(bu 2
-knife show
-.IP \(bu 2
-knife ssh
-.IP \(bu 2
-knife status
-.IP \(bu 2
-knife tag
-.IP \(bu 2
-knife upload
-.IP \(bu 2
-knife user
-.IP \(bu 2
-knife xargs
-.UNINDENT
-.SH WORKING WITH KNIFE
-.sp
-knife runs from a management workstation and sits in\-between a Chef server and an organization\(aqs infrastructure. knife interacts with a Chef server by using the same REST API that is used by a chef\-client\&. Role\-based authentication controls (RBAC) can be used to authorize changes when knife is run with Enterprise Chef\&. knife is configured during workstation setup, but subsequent modifications can be made using the knife.rb configuration file.
-.SS Common Options
-.sp
-The following options can be run with all knife sub\-commands and plug\-ins:
-.INDENT 0.0
-.TP
-.B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP
-The configuration file to use. For example, when knife is run from a node that is configured to be managed by the Chef server, this option is used to allow knife to use the same credentials as the chef\-client when communicating with the Chef server\&.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen.
-.TP
-.B \fB\-d\fP, \fB\-\-disable\-editing\fP
-Use to prevent the $EDITOR from being opened and to accept data as\-is.
-.TP
-.B \fB\-\-defaults\fP
-Use to have knife use the default value instead of asking a user to provide one.
-.TP
-.B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP
-The $EDITOR that is used for all interactive commands.
-.TP
-.B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP
-The name of the environment. When this option is added to a command, the command will run only against the named environment. This option is ignored during search queries made using the \fBknife search\fP subcommand.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-k KEY\fP, \fB\-\-key KEY\fP
-The private key that knife will use to sign requests made by the API client to the Chef server\&.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output.
-.TP
-.B \fB\-\-print\-after\fP
-Use to show data after a destructive operation.
-.TP
-.B \fB\-s URL\fP, \fB\-\-server\-url URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-V\fP, \fB\-\-verbose\fP
-Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity.
-.TP
-.B \fB\-y\fP, \fB\-\-yes\fP
-Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.SS JSON Data Format
-.sp
-Most data is entered using a text editor in JSON format, unless the \fB\-\-disable\-editing\fP option is entered as part of a command. (Encrypted data bags use YAML, which is a superset of JSON\&.) JSON is a common, language\-independent data format that provides a simple text representation of arbitrary data structures. For more information about JSON, see \fI\%http://www.json.org/\fP or \fI\%http://en.wikipedia.org/wiki/JSON\fP\&.
-.SS Set the Text Editor
-.sp
-Some knife commands, such as \fBknife data bag edit\fP, require that information be edited as JSON data using a text editor. For example, the following command:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ knife data bag edit admins admin_name
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-will open up the text editor with data similar to:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-{
- "id": "admin_name"
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-Changes to that file can then be made:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-{
- "id": "Justin C."
- "description": "I am passing the time by letting time pass over me ..."
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-The type of text editor that is used by knife can be configured by adding an entry to the knife.rb file or by setting an \fBEDITOR\fP environment variable. For example, to configure the text editor to always open with vim, add the following to the knife.rb file:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-knife[:editor] = "/usr/bin/vim"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-When a Microsoft Windows file path is enclosed in a double\-quoted string (" "), the same backslash character (\fB\e\fP) that is used to define the file path separator is also used in Ruby to define an escape character. The knife.rb file is a Ruby file; therefore, file path separators must be escaped. In addition, spaces in the file path must be replaced with \fB~1\fP so that the length of each section within the file path is not more than 8 characters. For example, if EditPad Pro is the text editor of choice and is located at the following path:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-C:\e\eProgram Files (x86)\eEditPad Pro\eEditPad.exe
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-the setting in the knife.rb file would be similar to:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-knife[:editor] = "C:\e\eProgra~1\e\eEditPa~1\e\eEditPad.exe"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-One approach to working around the double\- vs. single\-quote issue is to put the single\-quotes outside of the double\-quotes. For example, for Notepad++:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-knife[:editor] = \(aq"C:\eProgram Files (x86)\eNotepad++\enotepad++.exe \-nosession \-multiInst"\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-for Sublime Text:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-knife[:editor] = \(aq"C:\eProgram Files\eSublime Text 2\esublime_text.exe \-\-wait"\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-for TextPad:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-knife[:editor] = \(aq"C:\eProgram Files (x86)\eTextPad 7\eTextPad.exe"\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-and for vim:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-knife[:editor] = \(aq"C:\eProgram Files (x86)\evim\evim74\egvim.exe"\(aq
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SS Using Quotes
-.sp
-Values can be entered with double quotes (" ") or single quotes (\(aq \(aq), but this should be done consistently.
-.SS Sub\-commands
-.sp
-knife comes with a collection of built in subcommands that work together to provide all of the functionality required to take specific actions against any object in an organization, including cookbooks, nodes, roles, data bags, environments, and users. A knife plugin extends the functionality beyond built\-in subcommands.
-.sp
-knife has the following subcommands: \fBbootstrap\fP, \fBclient\fP, \fBconfigure\fP, \fBcookbook\fP, \fBcookbook site\fP, \fBdata bag\fP, \fBdelete\fP, \fBdeps\fP, \fBdiff\fP, \fBdownload\fP, \fBedit\fP, \fBenvironment\fP, \fBexec\fP, \fBindex rebuild\fP, \fBlist\fP, \fBnode\fP, \fBrecipe list\fP, \fBrole\fP, \fBsearch\fP, \fBshow\fP, \fBssh\fP, \fBstatus\fP, \fBtag\fP, \fBupload\fP, \fBuser\fP, and \fBxargs\fP\&.
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-The following subcommands run only against the open source Chef server: \fBindex rebuild\fP and \fBuser\fP\&.
-.UNINDENT
-.UNINDENT
-.SS Syntax
-.sp
-All knife subcommands have the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-knife subcommand [ARGUMENT] (options)
-.UNINDENT
-.UNINDENT
-.sp
-Each subcommand has its own set of arguments and options.
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-All syntax examples in this document show variables in ALL_CAPS. For example \fB\-u PORT_LIST\fP (where PORT_LIST is a comma\-separated list of local and public UDP ports) or \fB\-F FORMAT\fP (where FORMAT determines the output format, either \fBsummary\fP, \fBtext\fP, \fBjson\fP, \fByaml\fP, or \fBpp\fP). These variables often require specific values that are unique to each organization.
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man8/chef-apply.8 b/distro/common/man/man8/chef-apply.8
deleted file mode 100644
index b12f01e886..0000000000
--- a/distro/common/man/man8/chef-apply.8
+++ /dev/null
@@ -1,86 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "CHEF-APPLY" "8" "Chef 12.0" "" "chef-client"
-.SH NAME
-chef-apply \- The man page for the chef-apply command line tool.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-chef\-apply allows a single recipe to be run from the command line.
-.SH OPTIONS
-.sp
-This command has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-chef\-apply name_of_recipe.rb
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-This tool has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-e RECIPE_TEXT\fP, \fB\-\-execute RECIPE_TEXT\fP
-Use to execute a resource using a string.
-.TP
-.B \fB\-l LEVEL\fP, \fB\-\-log_level LEVEL\fP
-The level of logging that will be stored in a log file.
-.TP
-.B \fB\-s\fP, \fB\-\-stdin\fP
-Use to execute a resource using standard input.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-W\fP, \fB\-\-why\-run\fP
-Use to run the executable in why\-run mode, which is a type of chef\-client run that does everything except modify the system. Use why\-run mode to understand why the chef\-client makes the decisions that it makes and to learn more about the current and proposed state of the system.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.UNINDENT
-.SH EXAMPLES
-.sp
-To use chef\-apply to run a recipe named \fBmachinations.rb\fP, enter the following:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ chef\-apply machinations.rb
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man8/chef-client.8 b/distro/common/man/man8/chef-client.8
deleted file mode 100644
index 4c8a1f7b8f..0000000000
--- a/distro/common/man/man8/chef-client.8
+++ /dev/null
@@ -1,398 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "CHEF-CLIENT" "8" "Chef 12.0" "" "chef-client"
-.SH NAME
-chef-client \- The man page for the chef-client command line tool.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-A chef\-client is an agent that runs locally on every node that is under management by Chef\&. When a chef\-client is run, it will perform all of the steps that are required to bring the node into the expected state, including:
-.INDENT 0.0
-.IP \(bu 2
-Registering and authenticating the node with the Chef server
-.IP \(bu 2
-Building the node object
-.IP \(bu 2
-Synchronizing cookbooks
-.IP \(bu 2
-Compiling the resource collection by loading each of the required cookbooks, including recipes, attributes, and all other dependencies
-.IP \(bu 2
-Taking the appropriate and required actions to configure the node
-.IP \(bu 2
-Looking for exceptions and notifications, handling each as required
-.UNINDENT
-.sp
-The chef\-client executable is run as a command\-line tool.
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-A client.rb file is used to specify the configuration details for the chef\-client\&.
-.INDENT 0.0
-.IP \(bu 2
-This file is loaded every time this executable is run
-.IP \(bu 2
-On UNIX\- and Linux\-based machines, the default location for this file is \fB/etc/chef/client.rb\fP; on Microsoft Windows machines, the default location for this file is \fBC:\echef\eclient.rb\fP; use the \fB\-\-config\fP option from the command line to change this location
-.IP \(bu 2
-This file is not created by default
-.IP \(bu 2
-When a client.rb file is present in this directory, the settings contained within that file will override the default configuration settings
-.UNINDENT
-.UNINDENT
-.UNINDENT
-.SH OPTIONS
-.sp
-This command has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-chef\-client OPTION VALUE OPTION VALUE ...
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-This command has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-A\fP, \fB\-\-fatal\-windows\-admin\-check\fP
-Use to cause a chef\-client run to fail when the chef\-client does not have administrator privileges in Microsoft Windows\&.
-.TP
-.B \fB\-\-chef\-zero\-port PORT\fP
-The port on which chef\-zero will listen. If a port is not specified\-\-\-individually, as range of ports, or from the \fBchef_zero.port\fP setting in the client.rb file\-\-\-the chef\-client will scan for ports between 8889\-9999 and will pick the first port that is available.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBdoc\fP (default) or \fBmin\fP\&.
-.sp
-Use \fBdoc\fP to print the progress of the chef\-client run using full strings that display a summary of updates as they occur.
-.sp
-Use \fBmin\fP to print the progress of the chef\-client run using single characters. A summary of updates is printed at the end of the chef\-client run. A dot (\fB\&.\fP) is printed for events that do not have meaningful status information, such as loading a file or synchronizing a cookbook. For resources, a dot (\fB\&.\fP) is printed when the resource is up to date, an \fBS\fP is printed when the resource is skipped by \fBnot_if\fP or \fBonly_if\fP, and a \fBU\fP is printed when the resource is updated.
-.sp
-Other formatting options are available when those formatters are configured in the client.rb file using the \fBadd_formatter\fP option.
-.TP
-.B \fB\-\-force\-formatter\fP
-Use to show formatter output instead of logger output.
-.TP
-.B \fB\-\-force\-logger\fP
-Use to show logger output instead of formatter output.
-.TP
-.B \fB\-g GROUP\fP, \fB\-\-group GROUP\fP
-The name of the group that owns a process. This is required when starting any executable as a daemon.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-i SECONDS\fP, \fB\-\-interval SECONDS\fP
-The frequency (in seconds) at which the chef\-client runs. When the chef\-client is run at intervals, \fB\-\-splay\fP and \fB\-\-interval\fP values are applied before the chef\-client run. Default value: \fB1800\fP\&.
-.TP
-.B \fB\-j PATH\fP, \fB\-\-json\-attributes PATH\fP
-The path to a file that contains JSON data.
-.sp
-Use this option to define a \fBrun_list\fP object. For example, a JSON file similar to:
-.INDENT 7.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-"run_list": [
- "recipe[base]",
- "recipe[foo]",
- "recipe[bar]",
- "role[webserver]"
-],
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-may be used by running \fBchef\-client \-j path/to/file.json\fP\&.
-.sp
-In certain situations this option may be used to update \fBnormal\fP attributes.
-.sp
-\fBWARNING:\fP
-.INDENT 7.0
-.INDENT 3.5
-Any other attribute type that is contained in this JSON file will be treated as a \fBnormal\fP attribute. For example, attempting to update \fBoverride\fP attributes using the \fB\-j\fP option:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-{
- "name": "dev\-99",
- "description": "Install some stuff",
- "override_attributes": {
- "apptastic": {
- "enable_apptastic": "false",
- "apptastic_tier_name": "dev\-99.bomb.com"
- }
- }
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-will result in a node object similar to:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-{
- "name": "maybe\-dev\-99",
- "normal": {
- "name": "dev\-99",
- "description": "Install some stuff",
- "override_attributes": {
- "apptastic": {
- "enable_apptastic": "false",
- "apptastic_tier_name": "dev\-99.bomb.com"
- }
- }
- }
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.UNINDENT
-.UNINDENT
-.TP
-.B \fB\-k KEY_FILE\fP, \fB\-\-client_key KEY_FILE\fP
-The location of the file which contains the client key. Default value: \fB/etc/chef/client.pem\fP\&.
-.TP
-.B \fB\-K KEY_FILE\fP, \fB\-\-validation_key KEY_FILE\fP
-The location of the file which contains the key used when a chef\-client is registered with a Chef server\&. A validation key is signed using the \fBvalidation_client_name\fP for authentication. Default value: \fB/etc/chef/validation.pem\fP\&.
-.TP
-.B \fB\-l LEVEL\fP, \fB\-\-log_level LEVEL\fP
-The level of logging that will be stored in a log file.
-.TP
-.B \fB\-L LOGLOCATION\fP, \fB\-\-logfile c\fP
-The location in which log file output files will be saved. If this location is set to something other than \fBSTDOUT\fP, standard output logging will still be performed (otherwise there would be no output other than to a file). This is recommended when starting any executable as a daemon. Default value: \fBSTDOUT\fP\&.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output. Default setting: \fB\-\-color\fP\&.
-.TP
-.B \fB\-N NODE_NAME\fP, \fB\-\-node\-name NODE_NAME\fP
-The name of the node.
-.TP
-.B \fB\-o RUN_LIST_ITEM\fP, \fB\-\-override\-runlist RUN_LIST_ITEM\fP
-Replace the current run list with the specified items. This option will not clear the list of cookbooks (and related files) that is cached on the node.
-.TP
-.B \fB\-\-once\fP
-Use to run the chef\-client only once and to cancel \fBinterval\fP and \fBsplay\fP options.
-.TP
-.B \fB\-P PID_FILE\fP, \fB\-\-pid PID_FILE\fP
-The location in which a process identification number (pid) is saved. An executable, when started as a daemon, will write the pid to the specified file. Default value: \fB/tmp/name\-of\-executable.pid\fP\&.
-.TP
-.B \fB\-r RUN_LIST_ITEM\fP, \fB\-\-runlist RUN_LIST_ITEM\fP
-Use to permanently replace the current run\-list with the specified run\-list items.
-.TP
-.B \fB\-R\fP, \fB\-\-enable\-reporting\fP
-Use to enable Chef reporting, which performs data collection during a chef\-client run.
-.TP
-.B \fBRECIPE_FILE\fP
-The path to a recipe. For example, if a recipe file is in the current directory, use \fBrecipe_file.rb\fP\&. This is typically used with the \fB\-\-local\-mode\fP option.
-.TP
-.B \fB\-\-run\-lock\-timeout SECONDS\fP
-The amount of time (in seconds) to wait for a chef\-client run to finish. Default value: not set (indefinite). Set to \fB0\fP to cause a second chef\-client to exit immediately.
-.TP
-.B \fB\-s SECONDS\fP, \fB\-\-splay SECONDS\fP
-A number (in seconds) to add to the \fBinterval\fP that is used to determine the frequency of chef\-client runs. This number can help prevent server load when there are many clients running at the same time. When the chef\-client is run at intervals, \fB\-\-splay\fP and \fB\-\-interval\fP values are applied before the chef\-client run.
-.TP
-.B \fB\-S CHEF_SERVER_URL\fP, \fB\-\-server CHEF_SERVER_URL\fP
-The URL for the Chef server\&.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user that owns a process. This is required when starting any executable as a daemon.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-W\fP, \fB\-\-why\-run\fP
-Use to run the executable in why\-run mode, which is a type of chef\-client run that does everything except modify the system. Use why\-run mode to understand why the chef\-client makes the decisions that it makes and to learn more about the current and proposed state of the system.
-.TP
-.B \fB\-z\fP, \fB\-\-local\-mode\fP
-Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&.
-.UNINDENT
-.SH RUN WITH ELEVATED PRIVILEGES
-.sp
-The chef\-client may need to be run with elevated privileges in order to get a recipe to converge correctly. On UNIX and UNIX\-like operating systems this can be done by running the command as root. On Microsoft Windows this can be done by running the command prompt as an administrator.
-.SS Linux
-.sp
-On Linux, the following error sometimes occurs when the permissions used to run the chef\-client are incorrect:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ chef\-client
-[Tue, 29 Nov 2011 19:46:17 \-0800] INFO: *** Chef 10.X.X ***
-[Tue, 29 Nov 2011 19:46:18 \-0800] WARN: Failed to read the private key /etc/chef/client.pem: #<Errno::EACCES: Permission denied \- /etc/chef/client.pem>
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-This can be resolved by running the command as root. There are a few ways this can be done:
-.INDENT 0.0
-.IP \(bu 2
-Log in as root and then run the chef\-client
-.IP \(bu 2
-Use \fBsu\fP to become the root user, and then run the chef\-client\&. For example:
-.INDENT 2.0
-.INDENT 3.5
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ su
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-and then:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ chef\-client
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.UNINDENT
-.UNINDENT
-.IP \(bu 2
-Use the sudo utility
-.INDENT 2.0
-.INDENT 3.5
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ sudo chef\-client
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.UNINDENT
-.UNINDENT
-.IP \(bu 2
-Give a user access to read \fB/etc/chef\fP and also the files accessed by the chef\-client\&. This requires super user privileges and, as such, is not a recommended approach
-.UNINDENT
-.SS Windows
-.sp
-On Microsoft Windows, running without elevated privileges (when they are necessary) is an issue that fails silently. It will appear that the chef\-client completed its run successfully, but the changes will not have been made. When this occurs, do one of the following to run the chef\-client as the administrator:
-.INDENT 0.0
-.IP \(bu 2
-Log in to the administrator account. (This is not the same as an account in the administrator\(aqs security group.)
-.IP \(bu 2
-Run the chef\-client process from the administrator account while being logged into another account. Run the following command:
-.INDENT 2.0
-.INDENT 3.5
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ runas /user:Administrator "cmd /C chef\-client"
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-This will prompt for the administrator account password.
-.UNINDENT
-.UNINDENT
-.IP \(bu 2
-Open a command prompt by right\-clicking on the command prompt application, and then selecting \fBRun as administrator\fP\&. After the command window opens, the chef\-client can be run as the administrator
-.UNINDENT
-.SH EXAMPLES
-.sp
-\fBStart a Chef run when the chef\-client is running as a daemon\fP
-.sp
-A chef\-client that is running as a daemon can be woken up and started by sending the process a \fBSIGUSR1\fP\&. For example, to trigger a chef\-client run on a machine running Linux:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ sudo killall \-USR1 chef\-client
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-\fBStart a Chef run manually\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ ps auxw|grep chef\-client
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-to return something like:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-root 66066 0.9 0.0 2488880 264 s001 S+ 10:26AM 0:03.05
-/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby /usr/bin/chef\-client \-i 3600 \-s 20
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-and then enter:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ sudo kill \-USR1 66066
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/man/man8/chef-solo.8 b/distro/common/man/man8/chef-solo.8
deleted file mode 100644
index d4bbc7507f..0000000000
--- a/distro/common/man/man8/chef-solo.8
+++ /dev/null
@@ -1,260 +0,0 @@
-.\" Man page generated from reStructuredText.
-.
-.TH "CHEF-SOLO" "8" "Chef 12.0" "" "chef-solo"
-.SH NAME
-chef-solo \- The man page for the chef-solo command line tool.
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.sp
-chef\-solo is an open source version of the chef\-client that allows using cookbooks with nodes without requiring access to a Chef server\&. chef\-solo runs locally and requires that a cookbook (and any of its dependencies) be on the same physical disk as the node. chef\-solo is a limited\-functionality version of the chef\-client and \fBdoes not support\fP the following:
-.INDENT 0.0
-.IP \(bu 2
-Node data storage
-.IP \(bu 2
-Search indexes
-.IP \(bu 2
-Centralized distribution of cookbooks
-.IP \(bu 2
-A centralized API that interacts with and integrates infrastructure components
-.IP \(bu 2
-Authentication or authorization
-.IP \(bu 2
-Persistent attributes
-.UNINDENT
-.sp
-\fBNOTE:\fP
-.INDENT 0.0
-.INDENT 3.5
-chef\-solo can be run as a daemon.
-.UNINDENT
-.UNINDENT
-.sp
-The chef\-solo executable is run as a command\-line tool.
-.sp
-\fBOptions\fP
-.sp
-This command has the following syntax:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-chef\-solo OPTION VALUE OPTION VALUE ...
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-This command has the following options:
-.INDENT 0.0
-.TP
-.B \fB\-c CONFIG\fP, \fB\-\-config CONFIG\fP
-The configuration file to use.
-.TP
-.B \fB\-d\fP, \fB\-\-daemonize\fP
-Use to run the executable as a daemon. This option may not be used in the same command with the \fB\-\-[no\-]fork\fP option.
-.sp
-This option is only available on machines that run in UNIX or Linux environments. For machines that are running Microsoft Windows that require similar functionality, use the \fBchef\-client::service\fP recipe in the \fBchef\-client\fP cookbook: \fI\%http://community.opscode.com/cookbooks/chef\-client\fP\&. This will install a chef\-client service under Microsoft Windows using the Windows Service Wrapper\&.
-.TP
-.B \fB\-E ENVIRONMENT_NAME\fP, \fB\-\-environment ENVIRONMENT_NAME\fP
-The name of the environment.
-.TP
-.B \fB\-f\fP, \fB\-\-[no\-]fork\fP
-Use to contain the chef\-client run in a secondary process with dedicated RAM. When the chef\-client run is complete the RAM will be returned to the master process. This option helps ensure that a chef\-client will use a steady amount of RAM over time because the master process will not run recipes. This option will also help prevent memory leaks (such as those that can be introduced by the code contained within a poorly designed cookbook). Use \fB\-\-no\-fork\fP to disable running the chef\-client in fork node. Default value: \fB\-\-fork\fP\&. This option may not be used in the same command with the \fB\-\-daemonize\fP and \fB\-\-interval\fP options.
-.TP
-.B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP
-The output format: \fBdoc\fP (default) or \fBmin\fP\&.
-.sp
-Use \fBdoc\fP to print the progress of the chef\-client run using full strings that display a summary of updates as they occur.
-.sp
-Use \fBmin\fP to print the progress of the chef\-client run using single characters. A summary of updates is printed at the end of the chef\-client run. A dot (\fB\&.\fP) is printed for events that do not have meaningful status information, such as loading a file or synchronizing a cookbook. For resources, a dot (\fB\&.\fP) is printed when the resource is up to date, an \fBS\fP is printed when the resource is skipped by \fBnot_if\fP or \fBonly_if\fP, and a \fBU\fP is printed when the resource is updated.
-.sp
-Other formatting options are available when those formatters are configured in the client.rb file using the \fBadd_formatter\fP option.
-.TP
-.B \fB\-\-force\-formatter\fP
-Use to show formatter output instead of logger output.
-.TP
-.B \fB\-\-force\-logger\fP
-Use to show logger output instead of formatter output.
-.TP
-.B \fB\-g GROUP\fP, \fB\-\-group GROUP\fP
-The name of the group that owns a process. This is required when starting any executable as a daemon.
-.TP
-.B \fB\-h\fP, \fB\-\-help\fP
-Shows help for the command.
-.TP
-.B \fB\-i SECONDS\fP, \fB\-\-interval SECONDS\fP
-The frequency (in seconds) at which the chef\-client runs. When the chef\-client is run at intervals, \fB\-\-splay\fP and \fB\-\-interval\fP values are applied before the chef\-client run. This option may not be used in the same command with the \fB\-\-[no\-]fork\fP option.
-.TP
-.B \fB\-j PATH\fP, \fB\-\-json\-attributes PATH\fP
-The path to a file that contains JSON data.
-.sp
-Use this option to define a \fBrun_list\fP object. For example, a JSON file similar to:
-.INDENT 7.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-"run_list": [
- "recipe[base]",
- "recipe[foo]",
- "recipe[bar]",
- "role[webserver]"
-],
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-may be used by running \fBchef\-client \-j path/to/file.json\fP\&.
-.sp
-In certain situations this option may be used to update \fBnormal\fP attributes.
-.sp
-\fBWARNING:\fP
-.INDENT 7.0
-.INDENT 3.5
-Any other attribute type that is contained in this JSON file will be treated as a \fBnormal\fP attribute. For example, attempting to update \fBoverride\fP attributes using the \fB\-j\fP option:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-{
- "name": "dev\-99",
- "description": "Install some stuff",
- "override_attributes": {
- "apptastic": {
- "enable_apptastic": "false",
- "apptastic_tier_name": "dev\-99.bomb.com"
- }
- }
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-will result in a node object similar to:
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-{
- "name": "maybe\-dev\-99",
- "normal": {
- "name": "dev\-99",
- "description": "Install some stuff",
- "override_attributes": {
- "apptastic": {
- "enable_apptastic": "false",
- "apptastic_tier_name": "dev\-99.bomb.com"
- }
- }
- }
-}
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.UNINDENT
-.UNINDENT
-.TP
-.B \fB\-l LEVEL\fP, \fB\-\-log_level LEVEL\fP
-The level of logging that will be stored in a log file.
-.TP
-.B \fB\-L LOGLOCATION\fP, \fB\-\-logfile c\fP
-The location in which log file output files will be saved. If this location is set to something other than \fBSTDOUT\fP, standard output logging will still be performed (otherwise there would be no output other than to a file). This is recommended when starting any executable as a daemon.
-.TP
-.B \fB\-\-[no\-]color\fP
-Use to view colored output. Default setting: \fB\-\-color\fP\&.
-.TP
-.B \fB\-N NODE_NAME\fP, \fB\-\-node\-name NODE_NAME\fP
-The name of the node.
-.TP
-.B \fB\-o RUN_LIST_ITEM\fP, \fB\-\-override\-runlist RUN_LIST_ITEM\fP
-Replace the current run list with the specified items.
-.TP
-.B \fB\-r RECIPE_URL\fP, \fB\-\-recipe\-url RECIPE_URL\fP
-The URL location from which a remote cookbook tar.gz will be downloaded.
-.TP
-.B \fB\-\-run\-lock\-timeout SECONDS\fP
-The amount of time (in seconds) to wait for a chef\-client run to finish. Default value: not set (indefinite). Set to \fB0\fP to cause a second chef\-client to exit immediately.
-.TP
-.B \fB\-s SECONDS\fP, \fB\-\-splay SECONDS\fP
-A number (in seconds) to add to the \fBinterval\fP that is used to determine the frequency of chef\-client runs. This number can help prevent server load when there are many clients running at the same time. When the chef\-client is run at intervals, \fB\-\-splay\fP and \fB\-\-interval\fP values are applied before the chef\-client run.
-.TP
-.B \fB\-u USER\fP, \fB\-\-user USER\fP
-The user that owns a process. This is required when starting any executable as a daemon.
-.TP
-.B \fB\-v\fP, \fB\-\-version\fP
-The version of the chef\-client\&.
-.TP
-.B \fB\-W\fP, \fB\-\-why\-run\fP
-Use to run the executable in why\-run mode, which is a type of chef\-client run that does everything except modify the system. Use why\-run mode to understand why the chef\-client makes the decisions that it makes and to learn more about the current and proposed state of the system.
-.UNINDENT
-.sp
-\fBExamples\fP
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ chef\-solo \-c ~/solo.rb \-j ~/node.json \-r http://www.example.com/chef\-solo.tar.gz
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-The tar.gz archived into the \fBfile_cache_path\fP, and then extracted to \fBcookbooks_path\fP\&.
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ chef\-solo \-c ~/solo.rb \-j ~/node.json
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.sp
-chef\-solo will look in the solo.rb file to determine the directory in which cookbooks are located.
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ chef\-solo \-c ~/solo.rb \-j http://www.example.com/node.json \-r http://www.example.com/chef\-solo.tar.gz
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Chef
-.\" Generated by docutils manpage writer.
-.
diff --git a/distro/common/markdown/README b/distro/common/markdown/README
deleted file mode 100644
index 0f35814baf..0000000000
--- a/distro/common/markdown/README
+++ /dev/null
@@ -1,3 +0,0 @@
-This directory contains markdown documentation that is used in other places.
-For example, markdown (.mkd) documents that are generated as man pages
-with ronn.
diff --git a/distro/common/markdown/man1/chef-shell.mkd b/distro/common/markdown/man1/chef-shell.mkd
deleted file mode 100644
index 216dc73d41..0000000000
--- a/distro/common/markdown/man1/chef-shell.mkd
+++ /dev/null
@@ -1,195 +0,0 @@
-chef-shell(1) -- Interactive Chef Console
-=========================================
-
-## SYNOPSIS
-
-__chef-shell__ [_named configuration_] _(options)_
-
- * `-S`, `--server CHEF_SERVER_URL`:
- The chef server URL
- * `-z`, `--client`:
- chef-client mode
- * `-c`, `--config CONFIG`:
- The configuration file to use
- * `-j`, `--json-attributes JSON_ATTRIBS`:
- Load attributes from a JSON file or URL
- * `-l`, `--log-level LOG_LEVEL`:
- Set the logging level
- * `-s`, `--solo`:
- chef-solo session
- * `-a`, `--standalone`:
- standalone session
- * `-v`, `--version`:
- Show chef version
- * `-h`, `--help`:
- Show command options
-
-When no --config option is specified, chef-shell attempts to load a
-default configuration file:
-
-* If a _named configuration_ is given, chef-shell will load ~/.chef/_named
- configuration_/chef_shell.rb
-* If no _named configuration_ is given chef-shell will load
- ~/.chef/chef_shell.rb if it exists
-* chef-shell falls back to loading /etc/chef/client.rb or
-/etc/chef/solo.rb if -z or -s options are given and no chef_shell.rb
-can be found.
-* The --config option takes precedence over implicit configuration
- paths.
-
-## DESCRIPTION
-
-`chef-shell` is an irb(1) (interactive ruby) session customized for Chef.
-`chef-shell` serves two primary functions: it provides a means to
-interact with a Chef Server interactively using a convenient DSL; it
-allows you to define and run Chef recipes interactively.
-
-## SYNTAX
-
-chef-shell uses irb's subsession feature to provide multiple modes of
-interaction. In addition to the primary mode which is entered on start,
-`recipe` and `attributes` modes are available.
-
-## PRIMARY MODE
-The following commands are available in the primary
-session:
-
- * `help`:
- Prints a list of available commands
- * `version`:
- Prints the Chef version
- * `recipe`:
- Switches to `recipe` mode
- * `attributes`:
- Switches to `attributes` mode
- * `run_chef`:
- Initiates a chef run
- * `reset`:
- reinitializes chef-shell session
- * `echo :on|:off`:
- Turns irb's echo function on or off. Echo is _on_ by default.
- * `tracing :on|:off`:
- Turns irb's function tracing feature on or off. Tracing is extremely
- verbose and expected to be of interest primarily to developers.
- * `node`:
- Returns the _node_ object for the current host. See knife-node(1)
- for more information about nodes.
- * `ohai`:
- Prints the attributes of _node_
-
-In addition to these commands, chef-shell provides a DSL for accessing
-data on the Chef Server. When working with remote data in chef-shell, you
-chain method calls in the form _object type_._operation_, where
-_object type_ is in plural form. The following object types are
-available:
-
- * `nodes`
- * `roles`
- * `data_bags`
- * `clients`
- * `cookbooks`
-
-For each _object type_ the following operations are available:
-
- * _object type_.all(_&block_):
- Loads all items from the server. If the optional code _block_ is
- given, each item will be passed to the block and the results
- returned, similar to ruby's `Enumerable#map` method.
- * _object type_.show(_object name_):
- Aliased as _object type_.load
-
- Loads the singular item identified by _object name_.
- * _object type_.search(_query_, _&block_):
- Aliased as _object type_.find
-
- Runs a search against the server and returns the matching items. If
- the optional code _block_ is given each item will be passed to the
- block and the results returned.
-
- The _query_ may be a Solr/Lucene format query given as a String, or
- a Hash of conditions. If a Hash is given, the options will be ANDed
- together. To join conditions with OR, use negative queries, or any
- advanced search syntax, you must provide give the query in String
- form.
- * _object type_.transform(:all|_query_, _&block_):
- Aliased as _object type_.bulk_edit
-
- Bulk edit objects by processing them with the (required) code _block_.
- You can edit all objects of the given type by passing the Symbol
- `:all` as the argument, or only a subset by passing a _query_ as the
- argument. The _query_ is evaluated in the same way as with
- __search__.
-
- The return value of the code _block_ is used to alter the behavior
- of `transform`. If the value returned from the block is `nil` or
- `false`, the object will not be saved. Otherwise, the object is
- saved after being passed to the block. This behavior can be
- exploited to create a dry run to test a data transformation.
-
-## RECIPE MODE
-Recipe mode implements Chef's recipe DSL. Exhaustively documenting this
-DSL is outside the scope of this document. See the following pages in
-the Chef documentation for more information:
-
- * <http://docs.chef.io/resources.html>
- * <http://docs.chef.io/recipes.html>
-
-Once you have defined resources in the recipe, you can trigger a
-convergence run via `run_chef`
-
-## EXAMPLES
-
-* A "Hello World" interactive recipe
-
- chef > recipe
- chef:recipe > echo :off
- chef:recipe > file "/tmp/hello\_world"
- chef:recipe > run\_chef
- [Sat, 09 Apr 2011 08:56:56 -0700] INFO: Processing file[/tmp/hello\_world] action create ((irb#1) line 2)
- [Sat, 09 Apr 2011 08:56:56 -0700] INFO: file[/tmp/hello\_world] created file /tmp/hello\_world
- chef:recipe > pp ls '/tmp'
- [".",
- "..",
- "hello\_world"]
-
-* Search for _nodes_ by role, and print their IP addresses
-
- chef > nodes.find(:roles => 'monitoring-server') {|n| n[:ipaddress] }
- => ["10.254.199.5"]
-
-* Remove the role _obsolete_ from every node in the system
-
- chef > nodes.transform(:all) {|n| n.run\_list.delete('role[obsolete]') }
- => [node[chef098b2.opschef.com], node[ree-woot], node[graphite-dev], node[fluke.localdomain], node[ghost.local], node[kallistec]]
-
-
-## BUGS
-
-`chef-shell` often does not perfectly replicate the context in which
-chef-client(8) configures a host, which may lead to discrepancies in
-observed behavior.
-
-`chef-shell` has to duplicate much code from chef-client's internal
-libraries and may become out of sync with the behavior of those
-libraries.
-
-## SEE ALSO
-
- chef-client(8) knife(1)
- <http://docs.chef.io/ctl_chef_shell.html>
-
-## AUTHOR
-
- Chef was written by Adam Jacob <adam@opscode.com> with many
- contributions from the community. chef-shell was written by Daniel
- DeLeo.
-
-## DOCUMENTATION
-
- This manual page was written by Daniel DeLeo <dan@opscode.com>.
- Permission is granted to copy, distribute and / or modify this
- document under the terms of the Apache 2.0 License.
-
-## CHEF
-
- chef-shell is distributed with Chef. <http://docs.chef.io>
diff --git a/distro/common/markdown/man1/knife-bootstrap.mkd b/distro/common/markdown/man1/knife-bootstrap.mkd
deleted file mode 100644
index a1a2d3460c..0000000000
--- a/distro/common/markdown/man1/knife-bootstrap.mkd
+++ /dev/null
@@ -1,141 +0,0 @@
-knife-bootstrap(1) -- Install Chef Client on a remote host
-========================================
-
-## SYNOPSIS
-
-__knife__ __bootstrap__ _(options)_
-
- * `-i`, `--identity-file IDENTITY_FILE`:
- The SSH identity file used for authentication
- * `-N`, `--node-name NAME`:
- The Chef node name for your new node
- * `-P`, `--ssh-password PASSWORD`:
- The ssh password
- * `-x`, `--ssh-user USERNAME`:
- The ssh username
- * `-p`, `--ssh-port PORT`:
- The ssh port
- * `--bootstrap-version VERSION`:
- The version of Chef to install
- * `--bootstrap-proxy PROXY_URL`:
- `The proxy server for the node being bootstrapped`
- * `--prerelease`:
- Install pre-release Chef gems
- * `-r`, `--run-list RUN_LIST`:
- Comma separated list of roles/recipes to apply
- * `--template-file TEMPLATE`:
- Full path to location of template to use
- * `--sudo`:
- Execute the bootstrap via sudo
- * `-d`, `--distro DISTRO`:
- Bootstrap a distro using a template
- * `--[no-]host-key-verify`:
- Enable host key verification, which is the default behavior.
- * `--hint HINT_NAME[=HINT_FILE]`:
- Provide the name of a hint (with option JSON file) to set for use by
- Ohai plugins.
-
-## DESCRIPTION
-
-Performs a Chef Bootstrap on the target node. The goal of the bootstrap
-is to get Chef installed on the target system so it can run Chef Client
-with a Chef Server. The main assumption is a baseline OS installation
-exists. This sub-command is used internally by some cloud computing
-plugins.
-
-The bootstrap sub-command supports supplying a template to perform the
-bootstrap steps. If the distro is not specified (via `-d` or `--distro`
-option), an Ubuntu 10.04 host bootstrapped with RubyGems is assumed. The
-__DISTRO__ value corresponds to the base filename of the template, in
-other words `DISTRO`.erb. A template file can be specified with the
-`--template-file` option in which case the __DISTRO__ is not used. The
-sub-command looks in the following locations for the template to use:
-
-* `bootstrap` directory in the installed Chef Knife library.
-* `bootstrap` directory in the `$PWD/.chef`.
-* `bootstrap` directory in the users `$HOME/.chef`.
-
-The default bootstrap templates are scripts that get copied to the
-target node (FQDN). The following distros are supported:
-
-* centos5-gems
-* fedora13-gems
-* ubuntu10.04-gems
-* ubuntu10.04-apt
-
-The gems installations will use RubyGems 1.3.6 and Chef installed as a
-gem. The apt installation will use the Opscode APT repository.
-
-In addition to handling the software installation, these bootstrap
-templates do the following:
-
- - Write the validation.pem per the local knife configuration.
- - Write a default config file for Chef (`/etc/chef/client.rb`) using values from the `knife.rb`.
- - Create a JSON attributes file containing the specified run list and run Chef.
-
-In the case of the RubyGems, the `client.rb` will be written from
-scratch with a minimal set of values; see __EXAMPLES__. In the case of
-APT Package installation, `client.rb` will have the
-`validation_client_name` appended if it is not set to `chef-validator`
-(default config value), and the `node_name` will be added if
-`chef_node_name` option is specified.
-
-When this is complete, the bootstrapped node will have:
-
- - Latest Chef version installed from RubyGems or APT Packages from Opscode. This may be a later version than the local system.
- - Be validated with the configured Chef Server.
- - Have run Chef with its default run list if one is specified.
-
-Additional custom bootstrap templates can be created and stored in
-`.chef/bootstrap/DISTRO.erb`, replacing __DISTRO__ with the value passed
-with the `-d` or `--distro` option. See __EXAMPLES__ for more
-information.
-
-## EXAMPLES
-Setting up a custom bootstrap is fairly straightforward. Create a
-`.chef/bootstrap` directory in your Chef Repository or in
-`$HOME/.chef/bootstrap`. Then create the ERB template file.
-
- mkdir ~/.chef/bootstrap
- vi ~/.chef/bootstrap/debian5.0-apt.erb
-
-For example, to create a new bootstrap template that should be used when
-setting up a new Debian node. Edit the template to run the commands, set
-up the validation certificate and the client configuration file, and
-finally to run chef-client on completion. The bootstrap template can be
-called with:
-
- knife bootstrap mynode.example.com --template-file ~/.chef/bootstrap/debian5.0-apt.erb
-
-Or,
-
- knife bootstrap mynode.example.com --distro debian5.0-apt
-
-The `--distro` parameter will automatically look in the
-`~/.chef/bootstrap` directory for a file named `debian5.0-apt.erb`.
-
-Templates provided by the Chef installation are located in
-`BASEDIR/lib/chef/knife/bootstrap/*.erb`, where _BASEDIR_ is the
-location where the package or Gem installed the Chef client libraries.
-
-## BUGS
-`knife bootstrap` is not capable of bootstrapping multiple hosts in
-parallel.
-
-The bootstrap script is passed as an argument to sh(1) on the remote
-system, so sensitive information contained in the script will be visible
-to other users via the process list using tools such as ps(1).
-
-## SEE ALSO
- __knife-ssh__(1)
-
-## AUTHOR
- Chef was written by Adam Jacob <adam@opscode.com> with many contributions from the community.
-
-## DOCUMENTATION
- This manual page was written by Joshua Timberman <joshua@opscode.com>.
- Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.
-
-
-## CHEF
- Knife is distributed with Chef. <http://docs.chef.io>
diff --git a/distro/common/markdown/man1/knife-client.mkd b/distro/common/markdown/man1/knife-client.mkd
deleted file mode 100644
index b95a578391..0000000000
--- a/distro/common/markdown/man1/knife-client.mkd
+++ /dev/null
@@ -1,103 +0,0 @@
-knife-client(1) -- Manage Chef API Clients
-========================================
-
-## SYNOPSIS
-
-__knife__ __client__ _sub-command_ _(options)_
-
-## SUB-COMMANDS
-Client subcommands follow a basic create, read, update, delete (CRUD)
-pattern. The Following subcommands are available:
-
-## BULK DELETE
-__knife client bulk delete__ _regex_ _(options)_
-
-Delete clients where the client name matches the regular expression
-_regex_ on the Chef Server. The regular expression should be given as a
-quoted string, and not surrounded by forward slashes.
-
-## CREATE
-__knife client create__ _client name_ _(options)_
-
- * `-a`, `--admin `:
- Create the client as an admin
- * `-f`, `--file FILE`:
- Write the key to a file
-
-Create a new client. This generates an RSA keypair. The private key will
-be displayed on _STDOUT_ or written to the named file. The public half
-will be stored on the Server. For _chef-client_ systems, the private key
-should be copied to the system as `/etc/chef/client.pem`.
-
-Admin clients should be created for users that will use _knife_ to
-access the API as an administrator. The private key will generally be
-copied to `~/.chef/client\_name.pem` and referenced in the `knife.rb`
-configuration file.
-
-## DELETE
-__knife client delete__ _client name_ _(options)_
-
-Deletes a registered client.
-
-## EDIT
-__client edit__ _client name_ _(options)_
-
-Edit a registered client.
-
-## LIST
-__client list__ _(options)_
-
- * `-w`, `--with-uri`:
- Show corresponding URIs
-
-List all registered clients.
-
-## REREGISTER
-__client reregister__ _client name_ _(options)_
-
- * `-f`, `--file FILE`:
- Write the key to a file
-
-Regenerate the RSA keypair for a client. The public half will be stored
-on the server and the private key displayed on _STDOUT_ or written to
-the named file. This operation will invalidate the previous keypair used
-by the client, preventing it from authenticating with the Chef Server.
-Use care when reregistering the validator client.
-
-## SHOW
-__client show__ _client name_ _(options)_
-
- * `-a`, `--attribute ATTR`:
- Show only one attribute
-
-Show a client. Output format is determined by the --format option.
-
-## DESCRIPTION
-Clients are identities used for communication with the Chef Server API,
-roughly equivalent to user accounts on the Chef Server, except that
-clients only communicate with the Chef Server API and are authenticated
-via request signatures.
-
-In the typical case, there will be one client object on the server for
-each node, and the corresponding client and node will have identical
-names.
-
-In the Chef authorization model, there is one special client, the
-"validator", which is authorized to create new non-administrative
-clients but has minimal privileges otherwise. This identity is used as a
-sort of "guest account" to create a client identity when initially
-setting up a host for management with Chef.
-
-## SEE ALSO
- __knife-node__(1)
-
-## AUTHOR
- Chef was written by Adam Jacob <adam@opscode.com> with many contributions from the community.
-
-## DOCUMENTATION
- This manual page was written by Joshua Timberman <joshua@opscode.com>.
- Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.
-
-## CHEF
- Knife is distributed with Chef. <http://docs.chef.io>
-
diff --git a/distro/common/markdown/man1/knife-configure.mkd b/distro/common/markdown/man1/knife-configure.mkd
deleted file mode 100644
index f3a4ef02bb..0000000000
--- a/distro/common/markdown/man1/knife-configure.mkd
+++ /dev/null
@@ -1,70 +0,0 @@
-knife-configure(1) -- Generate configuration files for knife or Chef Client
-========================================
-
-## SYNOPSIS
-
-__knife__ __configure__ [client] _(options)_
-
-## DESCRIPTION
-Generates a knife.rb configuration file interactively. When given the
---initial option, also creates a new administrative user.
-
-## CONFIGURE SUBCOMMANDS ##
-
-__knife configure__ _(options)_
-
- * `-i`, `--initial`:
- Create an initial API Client
- * `-r`, `--repository REPO`:
- The path to your chef-repo
-
-Create a configuration file for knife. This will prompt for values to
-enter into the file. Default values are listed in square brackets if no
-other entry is typed. See __knife__(1) for a description of
-configuration options.
-
-__knife configure client__ _directory_
-
-Read the _knife.rb_ config file and generate a config file suitable for
-use in _/etc/chef/client.rb_ and copy the validation certificate into
-the specified _directory_.
-
-## EXAMPLES
- * On a freshly installed Chef Server, use _knife configure -i_ to
- create an administrator and knife configuration file. Leave the
- field blank to accept the default value. On most systems, the
- default values are acceptable.
-
- user@host$ knife configure -i
- Please enter the chef server URL: [http://localhost:4000]
- Please enter a clientname for the new client: [username]
- Please enter the existing admin clientname: [chef-webui]
- Please enter the location of the existing admin client's private key: [/etc/chef/webui.pem]
- Please enter the validation clientname: [chef-validator]
- Please enter the location of the validation key: [/etc/chef/validation.pem]
- Please enter the path to a chef repository (or leave blank):
- Creating initial API user...
- Created (or updated) client[username]
- Configuration file written to /home/username/.chef/knife.rb
-
- This creates a new administrator client named _username_, writes
- a configuration file to _/home/username/.chef/knife.rb_, and the
- private key to _/home/username/.chef/username.pem_. The
- configuration file and private key may be copied to another system
- to facilitate administration of the Chef Server from a remote
- system. Depending on the value given for the Chef Server URL, you
- may need to modify that setting after copying to a remote host.
-
-## SEE ALSO
- __knife__(1) __knife-client__(1)
-
-## AUTHOR
- Chef was written by Adam Jacob <adam@opscode.com> with many contributions from the community.
-
-## DOCUMENTATION
- This manual page was written by Joshua Timberman <joshua@opscode.com>.
- Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.
-
-## CHEF
- Knife is distributed with Chef. <http://docs.chef.io>
-
diff --git a/distro/common/markdown/man1/knife-cookbook-site.mkd b/distro/common/markdown/man1/knife-cookbook-site.mkd
deleted file mode 100644
index 68bc8433df..0000000000
--- a/distro/common/markdown/man1/knife-cookbook-site.mkd
+++ /dev/null
@@ -1,123 +0,0 @@
-knife-cookbook-site(1) -- Install and update open source cookbooks
-========================================
-
-## SYNOPSIS
-
-__knife__ __cookbook site__ _sub-command_ _(options)_
-
-## COOKBOOK SITE SUB-COMMANDS
-`knife cookbook site` provides the following subcommands:
-
-## INSTALL
-__cookbook site install COOKBOOK [VERSION]__ _(options)_
-
- * `-D`, `--skip-dependencies `:
- Skip automatic installation of dependencies.
- * `-o`, `--cookbook-path PATH`:
- Install cookbooks to PATH
- * `-B`, `--branch BRANCH`:
- Default branch to work with [defaults to master]
-
-Uses git(1) version control in conjunction with the cookbook site to
-install community contributed cookbooks to your local cookbook
-repository. Running `knife cookbook site install` does the following:
-
-1. A new "pristine copy" branch is created in git for tracking the
- upstream;
-2. All existing cookbooks are removed from the branch;
-3. The cookbook is downloaded from the cookbook site in tarball form;
-4. The downloaded cookbook is untarred, and its contents committed via git;
-5. The pristine copy branch is merged into the master branch.
-
-By installing cookbook with this process, you can locally modify the
-upstream cookbook in your master branch and let git maintain your
-changes as a separate patch. When an updated upstream version becomes
-available, you will be able to merge the upstream changes while
-maintaining your local modifications.
-
-Unless _--skip-dependencies_ is specified, the process is applied recursively to all the
-cookbooks _COOKBOOK_ depends on (via metadata _dependencies_).
-
-## DOWNLOAD
-__knife cookbook site download COOKBOOK [VERSION]__ _(options)_
-
- * `-f`, `--file FILE`:
- The filename to write to
- * `--force`:
- Force download deprecated cookbook
-
-Downloads a specific cookbook from the Community site, optionally
-specifying a certain version.
-
-## LIST
-__knife cookbook site list__ _(options)_
-
- * `-w`, `--with-uri`:
- Show corresponding URIs
-
-Lists available cookbooks from the Community site.
-
-## SEARCH
-__knife cookbook site search QUERY__ _(options)_
-
-Searches for available cookbooks matching the specified query.
-
-## SHARE
-__knife cookbook site share COOKBOOK CATEGORY__ _(options)_
-
- * `-k`, `--key KEY`:
- API Client Key
- * `-u`, `--user USER`:
- API Client Username
- * `-o`, `--cookbook-path PATH:PATH`:
- A colon-separated path to look for cookbooks in
-
-Uploads the specified cookbook using the given category to the Opscode
-cookbooks site. Requires a login user and certificate for the Opscode
-Cookbooks site. By default, knife will use the username and API key
-you've configured in your configuration file; otherwise you must
-explicitly set these values on the command line or use an alternate
-configuration file.
-
-## UNSHARE
-__knife cookbook site unshare COOKBOOK__
-
-Stops sharing the specified cookbook on the Opscode cookbooks site.
-
-## SHOW
-__knife cookbook site show COOKBOOK [VERSION]__ _(options)_
-
-Shows information from the site about a particular cookbook.
-
-## DESCRIPTION
-The cookbook site, <http://community.opscode.com/>, is a cookbook
-distribution service operated by Opscode. This service provides users
-with a central location to publish cookbooks for sharing with other
-community members.
-
-`knife cookbook site` commands provide an interface to the cookbook
-site's HTTP API. For commands that read data from the API, no account is
-required. In order to upload cookbooks using the `knife cookbook site
-share` command, you must create an account on the cookbook site and
-configure your credentials via command line option or in your knife
-configuration file.
-
-## EXAMPLES
-Uploading cookbooks to the Opscode cookbooks site:
-
- knife cookbook site share example Other -k ~/.chef/USERNAME.pem -u USERNAME
-
-## SEE ALSO
- __knife-cookbook(1)__
- <http://community.opscode.com/cookbooks>
-
-## AUTHOR
- Chef was written by Adam Jacob <adam@opscode.com> with many contributions from the community.
-
-## DOCUMENTATION
- This manual page was written by Joshua Timberman <joshua@opscode.com>.
- Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.
-
-## CHEF
- Knife is distributed with Chef. <http://docs.chef.io>
-
diff --git a/distro/common/markdown/man1/knife-cookbook.mkd b/distro/common/markdown/man1/knife-cookbook.mkd
deleted file mode 100644
index 6a56059e80..0000000000
--- a/distro/common/markdown/man1/knife-cookbook.mkd
+++ /dev/null
@@ -1,263 +0,0 @@
-knife-cookbook(1) -- upload and manage chef cookbooks
-========================================
-
-## SYNOPSIS
-
-__knife__ __cookbook__ _sub-command_ _(options)_
-
-## SUB-COMMANDS
-`knife cookbook` supports the following sub commands:
-
-## LIST
-__knife cookbook list__ _(options)_
-
- * `-a`, `--all`:
- show all versions of a cookbook instead of just the most recent
- * `-w`, `--with-uri`:
- show corresponding uris
-
-Lists the cookbooks available on the Chef server.
-
-## SHOW
-__knife cookbook show cookbook [version] [part] [filename]__ _(options)_
-
- * `-f`, `--fqdn fqdn `:
- the fqdn of the host to see the file for
- * `-p`, `--platform platform `:
- the platform to see the file for
- * `-v`, `--platform-version version`:
- the platform version to see the file for
- * `-w`, `--with-uri`:
- Show corresponding URIs
-
-show a particular part of a _cookbook_ for the specified _version_. _part_ can be one of:
-
- * _attributes_
- * _definitions_
- * _files_
- * _libraries_
- * _providers_
- * _recipes_
- * _resources_
- * _templates_
-
-## UPLOAD
-__knife cookbook upload [cookbooks...]__ _(options)_
-
- * `-a`, `--all`:
- upload all cookbooks, rather than just a single cookbook
- * `-o`, `--cookbook-path path:path`:
- a colon-separated path to look for cookbooks in
- * `-d`, `--upload-dependencies`:
- Uploads additional cookbooks that this cookbook lists in as
- dependencies in its metadata.
- * `-E`, `--environment ENVIRONMENT`:
- An _ENVIRONMENT_ to apply the uploaded cookbooks to. Specifying this
- option will cause knife to edit the _ENVIRONMENT_ to place a strict
- version constraint on the cookbook version(s) uploaded.
- * `--freeze`:
- Sets the frozen flag on the uploaded cookbook(s) Any future attempt
- to modify the cookbook without changing the version number will
- return an error unless --force is specified.
- * `--force`:
- Overrides the frozen flag on a cookbook, allowing you to overwrite a
- cookbook version that has previously been uploaded with the --freeze
- option.
-
-Uploads one or more cookbooks from your local cookbook repository(ies)
-to the Chef Server. Only files that don't yet exist on the server will
-be uploaded.
-
-As the command parses the name args as 1..n cookbook names:
- `knife cookbook upload COOKBOOK COOKBOOK ...`
-works for one to many cookbooks.
-
-## DOWNLOAD
-__knife cookbook download cookbook [version]__ _(options)_
-
- * `-d`, `--dir download_directory`:
- the directory to download the cookbook into
- * `-f`, `--force`:
- overwrite an existing directory with the download
- * `-n`, `--latest`:
- download the latest version of the cookbook
-
-download a cookbook from the chef server. if no version is specified and
-only one version exists on the server, that version will be downloaded.
-if no version is specified and multiple versions are available on the
-server, you will be prompted for a version to download.
-
-## DELETE
-__knife cookbook delete cookbook [version]__ _(options)_
-
- * `-a`, `--all`:
- delete all versions
- * `-p`, `--purge`:
- purge files from backing store. this will disable any cookbook that contains any of the same files as the cookbook being purged.
-
-delete the specified _version_ of the named _cookbook_. if no version is
-specified, and only one version exists on the server, that version will
-be deleted. if multiple versions are available on the server, you will
-be prompted for a version to delete.
-
-## BULK DELETE
-__knife cookbook bulk delete regex__ _(options)_
-
- * `-p`, `--purge`:
- purge files from backing store. this will disable any cookbook that
- contains any of the same files as the cookbook being purged.
-
-delete cookbooks on the chef server based on a regular expression. the
-regular expression (_regex_) should be in quotes, not in //'s.
-
-## COOKBOOK CREATE
-__knife cookbook create cookbook__ _(options)_
-
- * `-o`, `--cookbook-path path`:
- the directory where the cookbook will be created
- * `-r`, `--readme-format format`:
- format of the readme file md, mkd, txt, rdoc
- * `-C`, `--copyright copyright`:
- name of copyright holder
- * `-i`, `--license license`:
- license for cookbook, apachev2 or none
- * `-m`, `--email email`:
- email address of cookbook maintainer
-
-this is a helper command that creates a new cookbook directory in the
-`cookbook_path`. the following directories and files are created for the
-named cookbook.
-
-* cookbook/attributes
-* cookbook/definitions
-* cookbook/files/default
-* cookbook/libraries
-* cookbook/metadata.rb
-* cookbook/providers
-* cookbook/readme.md
-* cookbook/recipes/default.rb
-* cookbook/resources
-* cookbook/templates/default
-
-supported readme formats are 'md' (default), 'mkd', 'txt', 'rdoc'. the
-readme file will be written with the specified extension and a set of
-helpful starting headers.
-
-specify `-C` or `--copyright` with the name of the copyright holder as
-your name or your company/organization name in a quoted string. if this
-value is not specified an all-caps string `your_company_name` is used
-which can be easily changed with find/replace.
-
-specify `-i` or `--license` with the license that the cookbook is
-distributed under for sharing with other people or posting to the
-opscode cookbooks site. be aware of the licenses of files you put inside
-the cookbook and follow any restrictions they describe. when using
-`none` (default) or `apachev2`, comment header text and metadata file
-are pre-filled. the `none` license will be treated as
-non-redistributable.
-
-specify `-m` or `--email` with the email address of the cookbook's
-maintainer. if this value is not specified, an all-caps string
-`your_email` is used which can easily be changed with find/replace.
-
-the cookbook copyright, license, email and readme_format settings can be filled in the
-`knife.rb`, for example with default values:
-
- cookbook_copyright "your_company_name"
- cookbook_license "none"
- cookbook_email "your_email"
- readme_format "md"
-
-
-## METADATA
-__knife cookbook metadata cookbook__ _(options)_
-
- * `-a`, `--all`:
- generate metadata for all cookbooks, rather than just a single cookbook
- * `-o`, `--cookbook-path path:path`:
- a colon-separated path to look for cookbooks in
-
-generate cookbook metadata for the named _cookbook_. the _path_ used here specifies where the cookbooks directory is located and corresponds to the `cookbook_path` configuration option.
-
-## METADATA FROM FILE
-__knife cookbook metadata from file__ _(options)_
-
-load the cookbook metadata from a specified file.
-
-## TEST
-__knife cookbook test [cookbooks...]__ _(options)_
-
- * `-a`, `--all`:
- test all cookbooks, rather than just a single cookbook
- * `-o`, `--cookbook-path path:path`:
- a colon-separated path to look for cookbooks in
-
-test the specified cookbooks for syntax errors. this uses the built-in
-ruby syntax checking option for files in the cookbook ending in `.rb`,
-and the erb syntax check for files ending in `.erb` (templates).
-
-## RECIPE LIST
-__knife recipe list [PATTERN]__
-
-List available recipes from the server. Specify _PATTERN_ as a regular
-expression to limit the results.
-
-## DESCRIPTION
-Cookbooks are the fundamental unit of distribution in Chef. They
-encapsulate all recipes of resources and assets used to configure a
-particular aspect of the infrastructure. The following sub-commands can
-be used to manipulate the cookbooks stored on the Chef Server.
-
-On disk, cookbooks are directories with a defined structure. The
-following directories may appear within a cookbook:
-
- * COOKBOOK/attributes/:
- Ruby files that define default parameters to be used in recipes
- * COOKBOOK/definitions/:
- Ruby files that contain _resource definitions_
- * COOKBOOK/files/SPECIFICITY:
- Files of arbitrary type. These files may be downloaded by
- chef-client(8) when configuring a host.
- * COOKBOOK/libraries/:
- Ruby files that contain library code needed for recipes
- * COOKBOOK/providers/:
- Ruby files that contain Lightweight Provider definitions
- * COOKBOOK/recipes/:
- Ruby files that use Chef's recipe DSL to describe the desired
- configuration of a system
- * COOKBOOK/resources/:
- Ruby files that contain Lightweight Resource definitions
- * COOKBOOK/templates/SPECIFICITY:
- ERuby (ERb) template files. These are referenced by _recipes_ and
- evaluated to dynamically generate configuration files.
-
-__SPECIFICITY__ is a feature of _files_ and _templates_ that allow you
-to specify alternate files to be used on a specific OS platform or host.
-The default specificity setting is _default_, that is files in
-`COOKBOOK/files/default` will be used when a more specific copy is not
-available. Further documentation for this feature is available on the
-Chef wiki: <https://docs.chef.io/resource_cookbook_file.html#file-specificity>
-
-Cookbooks also contain a metadata file that defines various properties
-of the cookbook. The most important of these are the _version_ and the
-_dependencies_. The _version_ is used in combination with environments
-to select which copy of a given cookbook is distributed to a node. The
-_dependencies_ are used by the server to determine which additional
-cookbooks must be distributed to a given host when it requires a
-cookbook.
-
-## SEE ALSO
- __knife-environment(1)__ __knife-cookbook-site(1)__
- <http://docs.chef.io/cookbooks.html>
- <http://docs.chef.io/cookbook_repo.html>
-
-## AUTHOR
- Chef was written by Adam Jacob <adam@opscode.com> with many contributions from the community.
-
-## DOCUMENTATION
- This manual page was written by Joshua Timberman <joshua@opscode.com>.
- Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.
-
-
-## CHEF
- Knife is distributed with Chef. <http://docs.chef.io>
diff --git a/distro/common/markdown/man1/knife-data-bag.mkd b/distro/common/markdown/man1/knife-data-bag.mkd
deleted file mode 100644
index cab28a2f7f..0000000000
--- a/distro/common/markdown/man1/knife-data-bag.mkd
+++ /dev/null
@@ -1,121 +0,0 @@
-knife-data-bag(1) -- Store arbitrary data on a Chef Server
-========================================
-
-## SYNOPSIS
-
-__knife__ __data bag__ _sub-command_ _(options)_
-
-## DESCRIPTION
-Data bags are stores of arbitrary JSON data. Each data bag is a
-collection that may contain many items. Data Bag Items are indexed by
-the Chef Server and can be searched via __knife-search__(1).
-
-Data bags are available to all nodes configured by __chef-client__(8),
-and are therefore a convenient mechanism to store global information,
-such as lists of administrative accounts that should be configured on
-all hosts.
-
-## DATA BAG SUB-COMMANDS
-
-## CREATE
-__knife data bag create__ _bag name_ [item id] _(options)_
-
- * `-s`, `--secret SECRET`:
- A secret key used to encrypt the data bag item. See __encryption support__ below.
- * `--secret-file SECRET_FILE`:
- The path to a file containing the secret key to be used to encrypt
- the data bag item.
-
-If _item id_ is given, creates a new, empty data bag item and opens it for
-editing in your editor. The data bag will be created if it does not
-exist.
-
-If _item id_ is not given, the data bag will be created.
-
-## DELETE
-__knife data bag delete__ _bag name_ [item id] _(options)_
-
-Delete a data bag, or an item from a data bag.
-
-## EDIT
-__knife data bag edit__ _bag name_ _item id_ _(options)_
-
- * `-s`, `--secret SECRET`:
- A secret key used to encrypt the data bag item. See __encryption support__ below.
- * `--secret-file SECRET_FILE`:
- The path to a file containing the secret key to be used to encrypt
- the data bag item.
-
-Edit an item in a data bag.
-
-## FROM FILE
-__knife data bag from file__ _bag name_ _file_ _(options)_
-
-__knife data bag from file__ _bag name_ _file1_ _file2_ _file3_ _(options)_
-
-__knife data bag from file__ _bag name_ _folder_ _(options)_
-
- * `-s`, `--secret SECRET`:
- A secret key used to encrypt the data bag item. See __encryption support__ below.
- * `--secret-file SECRET_FILE`:
- The path to a file containing the secret key to be used to encrypt
- the data bag item.
-
-Load a data bag item from a JSON file. If _file_ is a relative or
-absolute path to the file, that file will be used. Otherwise, the _file_
-parameter is treated as the base name of a data bag file in a Chef
-repository, and `knife` will search for the file in
-`./data_bags/bag_name/file`. For example `knife data bag from file users
-dan.json` would attempt to load the file `./data_bags/users/dan.json`.
-
-## LIST
-__knife data bag list__ _(options)_
-
- * `-w`, `--with-uri`:
- Show corresponding URIs
-
-Lists the data bags that exist on the Chef Server.
-
-## SHOW
-__knife data bag show BAG [ITEM]__ _(options)_
-
- * `-s`, `--secret SECRET`:
- A secret key used to encrypt the data bag item. See __encryption support__ below.
- * `--secret-file SECRET_FILE`:
- The path to a file containing the secret key to be used to encrypt
- the data bag item.
-
-Show a specific data bag or an item in a data bag. The output will be
-formatted according to the --format option.
-
-## ENCRYPTION SUPPORT
-Data Bag Items may be encrypted to keep their contents secret. This may
-be desirable when storing sensitive information such as database
-passwords, API keys, etc.
-
-Data Bag Item encryption uses the AES-256 CBC symmetric key algorithm.
-
-__CAVEATS:__ Keys are not encrypted; only values are encrypted. The "id"
-of a Data Bag Item is not encrypted, since it is used by Chef Server to
-store the item in its database. For example, given the following data bag item:
- {"id": "important_passwords", "secret_password": "opensesame"}
-The key "secret\_password" will be visible to an evesdropper, but the
-value "opensesame" will be protected. Both the key "id" and its value
-"important\_passwords" will be visible to an evesdropper.
-
-Chef Server does not provide a secure mechanism for distributing
-encryption keys.
-
-## SEE ALSO
- __knife-search__(1)
-
-## AUTHOR
- Chef was written by Adam Jacob <adam@opscode.com> with many contributions from the community.
-
-## DOCUMENTATION
- This manual page was written by Joshua Timberman <joshua@opscode.com>.
- Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.
-
-## CHEF
- Knife is distributed with Chef. http://docs.chef.io/
-
diff --git a/distro/common/markdown/man1/knife-environment.mkd b/distro/common/markdown/man1/knife-environment.mkd
deleted file mode 100644
index 06bf423dc0..0000000000
--- a/distro/common/markdown/man1/knife-environment.mkd
+++ /dev/null
@@ -1,151 +0,0 @@
-knife-environment(1) -- Define cookbook policies for the environments in your infrastructure
-========================================
-
-## SYNOPSIS
-
-__knife__ __environment__ _sub-command_ _(options)_
-
-## SUBCOMMANDS
-Environment subcommands follow a basic create, read, update, delete
-(CRUD) pattern. The following subcommands are available:
-
-## CREATE
-__knife environment create__ _environment_ _(options)_
-
- * `-d`, `--description DESCRIPTION`:
- The value of the description field.
-
-Create a new environment object on the Chef Server. The envrionment will
-be opened in the text editor for editing prior to creation if the -n
-option is not present.
-
-## DELETE
-__knife environment delete__ _environment_ _(options)_
-
-Destroy an environment on the Chef Server. A prompt for confirmation
-will be displayed if the -y options is not given.
-
-## EDIT
-__knife environment edit__ _environment_ _(options)_
-
-Fetch _environment_ and display it in the text editor for editing. The
-environment will be saved to the Chef Server when the editing session
-exits.
-
-## FROM FILE
-__knife environment from file__ _file_ _(options)_
-
-Create or update an environment from the JSON or Ruby format _file_. See
-__format__ for the proper format of this file.
-
-## LIST
-__knife environment list__ _(options)_
- * `-w`, `--with-uri`:
- Show the resource URI for each environment
-
-## SHOW
-__knife environment show__ _environment_ _(options)_
-
-## DESCRIPTION
-Environments provide a means to apply policies to hosts in your
-infrastructure based on business function. For example, you may have a
-separate copy of your infrastructure called "dev" that runs the latest
-version of your application and should use the newest versions of your
-cookbooks when configuring systems, and a production instance of your
-infrastructure where you wish to update code and cookbooks in a more
-controlled fashion. In Chef, this function is implemented with
-_environments_.
-
-Environments contain two major components: a set of cookbook version
-constraints and environment attributes.
-
-## SYNTAX
-A cookbook version constraint is comprised of a _cookbook name_ and a
-_version constraint_. The _cookbook name_ is the name of a cookbook in
-your system, and the _version constraint_ is a String describing the
-version(s) of that cookbook allowed in the environment. Only one
-_version constraint_ is supported for a given _cookbook name_.
-
-The exact syntax used to define a cookbook version constraint varies
-depending on whether you use the JSON format or the Ruby format. In the
-JSON format, the cookbook version constraints for an environment are
-represented as a single JSON object, like this:
-
- {"apache2": ">= 1.5.0"}
-
-In the Ruby format, the cookbook version constraints for an environment
-are represented as a Ruby Hash, like this:
-
- {"apache2" => ">= 1.5.0"}
-
-A _version number_ is a String comprised of two or three digits
-separated by a dot (.) character, or in other words, strings of the form
-"major.minor" or "major.minor.patch". "1.2" and "1.2.3" are examples of
-valid version numbers. Version numbers containing more than three digits
-or alphabetic characters are not supported.
-
-A _version constraint_ String is composed of an _operator_ and a
-_version number_. The following operators are available:
-
- * `= VERSION`:
- Equality. Only the exact version specified may be used.
- * `> VERSION`:
- Greater than. Only versions greater than `VERSION` may be used.
- * `>= VERSION`:
- Greater than or equal to. Only versions equal to VERSION or greater
- may be used.
- * `< VERSION`:
- Less than. Only versions less than VERSION may be used.
- * `<= VERSION`:
- Less than or equal to. Only versions lesser or equal to VERSION may
- be used.
- * `~> VERSION`:
- Pessimistic greater than. Depending on the number of components in
- the given VERSION, the constraint will be optimistic about future
- minor or patch revisions only. For example, `~> 1.1` will match any
- version less than `2.0` and greater than or equal to `1.1.0`,
- whereas `~> 2.0.5` will match any version less than `2.1.0` and
- greater than or equal to `2.0.5`.
-
-## FORMAT
-The JSON format of an envioronment is as follows:
-
- {
- "name": "dev",
- "description": "The development environment",
- "cookbook_versions": {
- "couchdb": "= 11.0.0"
- },
- "json_class": "Chef::Environment",
- "chef_type": "environment",
- "default_attributes": {
- "apache2": { "listen_ports": [ "80", "443" ] }
- },
- "override_attributes": {
- "aws_s3_bucket": "production"
- }
- }
-
-The Ruby format of an environment is as follows:
-
- name "dev"
- description "The development environment"
- cookbook_versions "couchdb" => "= 11.0.0"
- default_attributes "apache2" => { "listen_ports" => [ "80", "443" ] }
- override_attributes "aws_s3_bucket" => "production"
-
-
-## SEE ALSO
- __knife-node(1)__ __knife-cookbook(1)__ __knife-role(1)__
- <http://docs.chef.io/environments.html>
- <http://docs.chef.io/cookbook_versions.html>
-
-## AUTHOR
- Chef was written by Adam Jacob <adam@opscode.com> with many contributions from the community.
-
-## DOCUMENTATION
- This manual page was written by Daniel DeLeo <dan@opscode.com>.
- Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.
-
-## CHEF
- Knife is distributed with Chef. <http://docs.chef.io>
diff --git a/distro/common/markdown/man1/knife-exec.mkd b/distro/common/markdown/man1/knife-exec.mkd
deleted file mode 100644
index 1b60177d16..0000000000
--- a/distro/common/markdown/man1/knife-exec.mkd
+++ /dev/null
@@ -1,42 +0,0 @@
-knife-exec(1) -- Run user scripts using the Chef API DSL
-========================================
-
-## SYNOPSIS
-
-__knife__ __exec__ _(options)_
-
- * `-E`, `--exec CODE`:
- Provide a snippet of code to evaluate on the command line
-
-## DESCRIPTION
-
-`knife exec` runs arbitrary ruby scripts in a context similar to that
-of the chef-shell(1) DSL. See the chef-shell documentation for a
-description of the commands available.
-
-## EXAMPLES
-
- * Make an API call against an arbitrary endpoint:
- knife exec -E 'api.get("nodes/fluke.localdomain/cookbooks")'
- => list of cookbooks for the node _fluke.localdomain_
- * Remove the role _obsolete_ from all nodes:
- knife exec -E 'nodes.transform(:all){|n| n.run\_list.delete("role[obsolete]")}'
- * Generate the expanded run list for hosts in the `webserver` role:
- knife exec -E 'nodes.find(:roles => "webserver") {|n| n.expand!; n[:recipes]}'
-
-## SEE ALSO
-
- __chef-shell(1)__
-
-## AUTHOR
-
- Chef was written by Adam Jacob <adam@opscode.com> with many contributions from the community.
-
-## DOCUMENTATION
-
- This manual page was written by Joshua Timberman <joshua@opscode.com>.
- Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.
-
-## CHEF
-
- Knife is distributed with Chef. <http://docs.chef.io>
diff --git a/distro/common/markdown/man1/knife-index.mkd b/distro/common/markdown/man1/knife-index.mkd
deleted file mode 100644
index f1425b8013..0000000000
--- a/distro/common/markdown/man1/knife-index.mkd
+++ /dev/null
@@ -1,30 +0,0 @@
-knife-index(1) -- Rebuild the search index on a Chef Server
-========================================
-
-## SYNOPSIS
-
-__knife__ __index rebuild__ _(options)_
-
- * `-y`, `--yes`:
- don't bother to ask if I'm sure
-
-## DESCRIPTION
-Rebuilds all the search indexes on the server. This is accomplished by
-deleting all objects from the search index, and then forwarding each
-item in the database to __chef-expander__(8) via __rabbitmq-server__(1).
-Depending on the number of objects in the database, it may take some
-time for all objects to be indexed and available for search.
-
-## SEE ALSO
- __knife-search__(1)
-
-## AUTHOR
- Chef was written by Adam Jacob <adam@opscode.com> with many contributions from the community.
-
-## DOCUMENTATION
- This manual page was written by Joshua Timberman <joshua@opscode.com>.
- Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.
-
-## CHEF
- Knife is distributed with Chef. <http://docs.chef.io>
-
diff --git a/distro/common/markdown/man1/knife-node.mkd b/distro/common/markdown/man1/knife-node.mkd
deleted file mode 100644
index 0262d64702..0000000000
--- a/distro/common/markdown/man1/knife-node.mkd
+++ /dev/null
@@ -1,130 +0,0 @@
-knife-node(1) -- Manage the hosts in your infrastructure
-========================================
-
-## SYNOPSIS
-
-__knife__ __node__ _sub-command_ _(options)_
-
-## DESCRIPTION
-Nodes are data structures that represent hosts configured with Chef.
-Nodes have a __name__, a String that uniquely identifies the node,
-__attributes__, a nested Hash of properties that describe how the host
-should be configured, a __chef\_environment__, a String representing the
-environment to which the node belongs, and a __run\_list__, an ordered
-list of __recipes__ or __roles__ that chef-client should apply when
-configuring a host.
-
-When a host communicates with a Chef Server, it authenticates using its
-__node\_name__ for identification and signs its reqests with a private
-key. The Server validates the request by looking up a __client__ object
-with a name identical to the __node\_name__ submitted with the request
-and verifes the signature using the public key for that __client__
-object. __NOTE__ that the __client__ is a different object in the
-system. It is associated with a node by virtue of having a matching
-name.
-
-By default __chef-client__(8) will create a node using the FQDN of the
-host for the node name, though this may be overridden by configuration
-settings.
-
-## NODE SUB-COMMANDS
-The following `node` subcommands are available:
-
-## BULK DELETE
-__knife node bulk delete__ _regex_ _(options)_
-
-Deletes nodes for which the name matches the regular expression _regex_
-on the Chef Server. The regular expression should be given in quotes,
-and should not be surrounded with forward slashes (as is typical of
-regular expression literals in scripting languages).
-
-## CREATE
-__knife node create__ _name_ _(options)_
-
-Create a new node. Unless the --disable-editing option is given, an empty node
-object will be created and displayed in your text editor. If the editor
-exits with a successful exit status, the node data will be posted to the
-Chef Server to create the node.
-
-## DELETE
-__knife node delete__ _name_ _(options)_
-
-Deletes the node identified by _name_ on the Chef Server.
-
-## EDIT
-__knife node edit__ _name_ _(options)_
-
- * `-a`, `--all`:
- Display all node data in the editor. By default, default, override,
- and automatic attributes are not shown.
-
-Edit the node identified by _name_. Like __knife node create__, the node
-will be displayed in your text editor unless the -n option is present.
-
-## FROM FILE
-__knife node from file__ _file_ _(options)_
-
-Create a node from a JSON format _file_.
-
-## LIST
-__knife node list__ _(options)_
-
- * `-w`, `--with-uri`:
- Show corresponding URIs
-
-List all nodes.
-
-## RUN\_LIST ADD
-__knife node run_list add__ _name_ _run list item_ _(options)_
-
- * `-a`, `--after ITEM`:
- Place the ENTRY in the run list after ITEM
-
-Add the _run list item_ to the node's `run_list`. See Run list
-
-## RUN\_LIST REMOVE
-__knife node run_list remove__ _node name_ _run list item_ _(options)_
-
-Remove the _run list item_ from the node's `run_list`.
-
-## SHOW
-__knife node show__ _node name_ _(options)_
-
- * `-a`, `--attribute [ATTR]`:
- Show only one attribute
- * `-r`, `--run-list `:
- Show only the run list
- * `-F`, `--format FORMAT`:
- Display the node in a different format.
- * `-m`, `--medium`:
- Display more, but not all, of the node's data when using the default
- _summary_ format
-
-Displays the node identified by _node name_ on stdout.
-
-## RUN LIST ITEM FORMAT
-Run list items may be either roles or recipes. When adding a role to a
-run list, the correct syntax is "role[ROLE\_NAME]"
-
-When adding a recipe to a run list, there are several valid formats:
-
- * Fully Qualified Format:
- "recipe[COOKBOOK::RECIPE\_NAME]", for example, "recipe[chef::client]"
- * Cookbook Recipe Format:
- For brevity, the recipe part of the fully qualified format may be omitted, and recipes specified as "COOKBOOK::RECIPE\_NAME", e.g., "chef::client"
- * Default Recipe Format:
- When adding the default recipe of a cookbook to a run list, the recipe name may be omitted as well, e.g., "chef::default" may be written as just "chef"
-
-## SEE ALSO
- __knife-client__(1) __knife-search__(1) __knife-role__(1)
-
-## AUTHOR
- Chef was written by Adam Jacob <adam@opscode.com> with many contributions from the community.
-
-## DOCUMENTATION
- This manual page was written by Joshua Timberman <joshua@opscode.com>.
- Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.
-
-## CHEF
- Knife is distributed with Chef. <http://docs.chef.io>
-
diff --git a/distro/common/markdown/man1/knife-role.mkd b/distro/common/markdown/man1/knife-role.mkd
deleted file mode 100644
index e202c52d81..0000000000
--- a/distro/common/markdown/man1/knife-role.mkd
+++ /dev/null
@@ -1,85 +0,0 @@
-knife-role(1) -- Group common configuration settings
-========================================
-
-## SYNOPSIS
-
-__knife__ __role__ _sub-command_ _(options)_
-
-## ROLE SUB-COMMANDS
-The following `role` subcommands are available:
-
-## LIST
-__knife role list__ _(options)_
-
- * `-w`, `--with-uri`:
- Show corresponding URIs
-
-List roles.
-
-## SHOW
-__knife role show ROLE__ _(options)_
-
- * `-a`, `--attribute ATTR`:
- Show only one attribute
-
-Show a specific role.
-
-## CREATE
-__knife role create ROLE__ _(options)_
-
- * `-d`, `--description`:
- The role description
-
-Create a new role.
-
-## EDIT
-__knife role edit ROLE__ _(options)_
-
-Edit a role.
-
-## FROM FILE
-__knife role from file FILE__ _(options)_
-
-Create or update a role from a role Ruby DSL (`.rb`) or JSON file.
-
-## DELETE
-__knife role delete ROLE__ _(options)_
-
-Delete a role.
-
-## BULK DELETE
-__knife role bulk delete REGEX__ _(options)_
-
-Delete roles on the Chef Server based on a regular expression. The regular expression (_REGEX_) should be in quotes, not in //'s.
-
-## DESCRIPTION
-Roles provide a mechanism to group repeated configuration settings.
-Roles are data structures that contain __default\_attributes__, and
-__override_attributes__, which are nested hashes of configuration
-settings, and a __run_list__, which is an ordered list of recipes and
-roles that should be applied to a host by chef-client.
-
-__default_attributes__ will be overridden if they conflict with a value
-on a node that includes the role. Conversely, __override_attributes__
-will override any values set on nodes that apply them.
-
-When __chef-client__(8) configures a host, it will "expand" the
-__run_list__ included in that host's node data. The expansion process
-will recursively replace any roles in the run\_list with that role's
-run\_list.
-
-## SEE ALSO
- __knife-node(1)__ __knife-environment(1)__
- <http://docs.chef.io/roles.html>
- <http://docs.chef.io/attributes.html>
-
-## AUTHOR
- Chef was written by Adam Jacob <adam@opscode.com> with many contributions from the community.
-
-## DOCUMENTATION
- This manual page was written by Joshua Timberman <joshua@opscode.com>.
- Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.
-
-## CHEF
- Knife is distributed with Chef. <http://docs.chef.io>
-
diff --git a/distro/common/markdown/man1/knife-search.mkd b/distro/common/markdown/man1/knife-search.mkd
deleted file mode 100644
index b289b2c83b..0000000000
--- a/distro/common/markdown/man1/knife-search.mkd
+++ /dev/null
@@ -1,180 +0,0 @@
-knife-search(1) -- Find objects on a Chef Server by query
-========================================
-
-## SYNOPSIS
-
-__knife__ __search INDEX QUERY__ _(options)_
-
- * `-a`, `--attribute ATTR`:
- Show only one attribute
- * `-i`, `--id-only`:
- Show only the ID of matching objects
- * `-q`, `--query QUERY`:
- The search query; useful to protect queries starting with -
- * `-R`, `--rows INT`:
- The number of rows to return
- * `-r`, `--run-list`:
- Show only the run list
- * `-o`, `--sort SORT`:
- The order to sort the results in
- * `-b`, `--start ROW`:
- The row to start returning results at
- * `-m`, `--medium`:
- Display medium sized output when searching nodes using the default
- summary format
- * `-l`, `--long`:
- Display long output when searching nodes using the default summary
- format
-
-## DESCRIPTION
-
-Search is a feature of the Chef Server that allows you to use a
-full-text search engine to query information about your infrastructure
-and applications. You can utilize this service via search calls in a
-recipe or the knife search command. The search syntax is based on
-Lucene.
-
-
-## INDEXES
-
-Search indexes are a feature of the Chef Server and the search
-sub-command allows querying any of the available indexes using SOLR
-query syntax. The following data types are indexed for search:
-
- * _node_
- * _role_
- * _environment_
- * _clients_
- * _data bag_
-
-Data bags are indexed by the data bag's name. For example, to search a
-data bag named "admins":
-
- knife search admins "field:search_pattern"
-
-## QUERY SYNTAX
-
-Queries have the form `field:search_pattern` where `field` is a key in
-the JSON description of the relevant objects (nodes, roles,
-environments, or data bags). Both `field` and `search_pattern` are
-case-sensitive. `search_pattern` can be an exact, wildcard,
-range, or fuzzy match (see below). The `field` supports exact
-matching and limited wildcard matching.
-
-Searches will return the relevant objects (nodes, roles, environments,
-or data bags) where the `search_pattern` matches the object's value of
-`field`.
-
-### FIELD NAMES
-
-Field names are the keys within the JSON description of the object
-being searched. Nested Keys can be searched by placing an underscore
-("_") between key names.
-
-### WILDCARD MATCHING FOR FIELD NAMES
-
-The field name also has limited support for wildcard matching. Both
-the "*" and "?" wildcards (see below) can be used within a field name;
-however, they cannot be the first character of the field name.
-
-### EXACT MATCHES
-Without any search modifiers, a search returns those fields for which
-the `search_pattern` exactly matches the value of `field` in the JSON
-description of the object.
-
-### WILDCARD MATCHES
-
-Search support both single- and multi-character wildcard searches
-within a search pattern.
-
-'?' matches exactly one character.
-
-'*' matches zero or more characters.
-
-### RANGE MATCHES
-Range searches allows one to match values between two given values. To
-match values between X and Y, inclusively, use square brackets:
-
- knife search INDEX 'field:[X TO Y]
-
-To match values between X and Y, exclusively, use curly brackets:
-
- knife search INDEX 'field:{X TO Y}'
-
-Values are sorted in lexicographic order.
-
-### FUZZY MATCHES
-
-Fuzzy searches allows one to match values based on the Levenshtein
-Distance algorithm. To perform a fuzzy match, append a tilda (~) to
-the search term:
-
- knife search INDEX 'field:term~'
-
-This search would return nodes whose `field` was 'perm' or 'germ'.
-
-### BOOLEAN OPERATORS
-
-The boolean operators NOT, AND, and OR are supported. To find values
-of `field` that are not X:
-
- knife search INDEX 'field:(NOT X)'
-
-To find records where `field1` is X and `field2` is Y:
-
- knife search INDEX 'field1:X AND field2:Y'
-
-To find records where `field` is X or Y:
-
- knife search INDEX 'field:X OR field:Y'
-
-### QUOTING AND SPECIAL CHARACTERS
-
-In order to avoid having special characters and escape sequences
-within your search term interpreted by either Ruby or the shell,
-enclose them in single quotes.
-
-Search terms that include spaces should be enclosed in double-quotes:
-
- knife search INDEX 'field:"term with spaces"'
-
-The following characters must be escaped:
-
- + - && || ! ( ) { } [ ] ^ " ~ * ? : \
-
-## EXAMPLES
-
-Find the nodes with the fully-qualified domain name (FQDN)
-www.example.com:
-
- knife search node 'fqdn:www.example.com'
-
-Find the nodes running a version of Ubuntu:
-
- knife search node 'platform:ubuntu*'
-
-Find all nodes running CentOS in the production environment:
-
- knife search node 'chef_environment:production AND platform:centos'
-
-## KNOWN BUGS
-
- * Searches against the client index return no results in most cases. (CHEF-2477)
- * Searches using the fuzzy match operator (~) produce an error. (CHEF-2478)
-
-## SEE ALSO
- __knife-ssh__(1)
- <http://docs.chef.io/attributes.html>
- [Lucene Query Parser Syntax](http://lucene.apache.org/java/2_3_2/queryparsersyntax.html)
-
-## AUTHOR
- Chef was written by Adam Jacob <adam@opscode.com> with many contributions from the community.
-
-## DOCUMENTATION
- This manual page was written by Joshua Timberman <joshua@opscode.com>.
- Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.
-
-## CHEF
- Knife is distributed with Chef. <http://docs.chef.io>
-
-
diff --git a/distro/common/markdown/man1/knife-ssh.mkd b/distro/common/markdown/man1/knife-ssh.mkd
deleted file mode 100644
index 7d37075470..0000000000
--- a/distro/common/markdown/man1/knife-ssh.mkd
+++ /dev/null
@@ -1,69 +0,0 @@
-knife-ssh(1) -- Run a command or interactive session on multiple remote hosts
-========================================
-
-## SYNOPSIS
-
-__knife__ __ssh QUERY COMMAND__ _(options)_
-
- * `-a`, `--attribute ATTR `:
- The attribute to use for opening the connection - default is fqdn
- * `-C`, `--concurrency NUM `:
- The number of concurrent connections
- * `-m`, `--manual-list `:
- QUERY is a space separated list of servers
- * `-P`, `--ssh-password PASSWORD`:
- The ssh password
- * `-x`, `--ssh-user USERNAME `:
- The ssh username
- * `-i`, `--identity-file IDENTITY_FILE`:
- The SSH identity file used for authentication
- * `-p`, `--ssh-port PORT`:
- The ssh port
- * `--[no-]host-key-verify`:
- Verify host key, enabled by default.
-
-## DESCRIPTION
-
-The _ssh_ sub-command opens an ssh session to each of the nodes in the
-search results of the _QUERY_. This sub-command requires that the
-net-ssh-multi and highline Ruby libraries are installed. On Debian
-systems, these are the libnet-ssh-multi-ruby and libhighline-ruby
-packages. They can also be installed as RubyGems (net-ssh-multi and
-highline, respectively).
-
-## TERMINAL MULTIPLEXING AND TERMINAL TAB SUPPORT
-`knife ssh` integrates with several terminal multiplexer programs to
-provide a more convenient means of managing multiple ssh sessions. When
-the _COMMAND_ option matches one of these, `knife ssh` will create
-multiple interactive ssh sessions running locally in the terminal
-multiplexer instead of invoking the command on the remote host.
-
-The available multiplexers are:
-
- * `interactive`:
- A built-in multiplexer. `interactive` supports running commands on a
- subset of the connected hosts in parallel
- * __screen__(1):
- Runs ssh interactively inside `screen`. ~/.screenrc will be sourced
- if it exists.
- * __tmux__(1):
- Runs ssh interactively inside tmux.
- * `macterm` (Mac OS X only):
- Opens a Terminal.app window and creates a tab for each ssh session.
- You must install the rb-appscript gem before you can use this
- option.
-
-## SEE ALSO
- __knife-search__(1)
-
-## AUTHOR
- Chef was written by Adam Jacob <adam@opscode.com> with many contributions from the community.
-
-## DOCUMENTATION
- This manual page was written by Joshua Timberman <joshua@opscode.com>.
- Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.
-
-## CHEF
- Knife is distributed with Chef. <http://docs.chef.io>
-
-
diff --git a/distro/common/markdown/man1/knife-status.mkd b/distro/common/markdown/man1/knife-status.mkd
deleted file mode 100644
index 0a969e40dd..0000000000
--- a/distro/common/markdown/man1/knife-status.mkd
+++ /dev/null
@@ -1,36 +0,0 @@
-knife-status(1) -- Display status information for the nodes in your infrastructure
-========================================
-
-## SYNOPSIS
-
-__knife__ __status__ _(options)_
-
- * `-r`, `--run-list RUN_LIST`:
- Show the run list
-
-## DESCRIPTION
-
-The _status_ sub-command searches the Chef Server for all nodes and
-displays information about the last time the node checked into the
-server and executed a `node.save`. The fields displayed are the relative
-checkin time, the node name, it's operating system platform and version,
-the fully-qualified domain name and the default IP address. If the `-r`
-option is given, the node's run list will also be displayed. Note that
-depending on the configuration of the nodes, the FQDN and IP displayed
-may not be publicly reachable.
-
-
-## SEE ALSO
- __knife-search__(1)
-
-## AUTHOR
- Chef was written by Adam Jacob <adam@opscode.com> with many contributions from the community.
-
-## DOCUMENTATION
- This manual page was written by Joshua Timberman <joshua@opscode.com>.
- Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.
-
-## CHEF
- Knife is distributed with Chef. <http://docs.chef.io>
-
-
diff --git a/distro/common/markdown/man1/knife-tag.mkd b/distro/common/markdown/man1/knife-tag.mkd
deleted file mode 100644
index b5bbb8236f..0000000000
--- a/distro/common/markdown/man1/knife-tag.mkd
+++ /dev/null
@@ -1,39 +0,0 @@
-knife-tag(1) -- Apply tags to nodes on a Chef Server
-========================================
-
-## SYNOPSIS
-
-__knife__ __tag__ _subcommand_ _(options)_
-
-## TAG SUBCOMMANDS
-The following `tag` subcommands are available:
-
-## CREATE
-__knife tag create__ _node_ _tag_ [_..._]
-
-Adds one or more tags to _node_
-
-## DELETE
-__knife tag delete__ _node_ _tag_ [_..._]
-
-Removes one or more tags from _node_
-
-## LIST
-__knife tag list__ _node_
-
-Lists the tags applied to _node_
-
-
-## SEE ALSO
- __knife-node(1)__
-
-## AUTHOR
- Chef was written by Adam Jacob <adam@opscode.com> with many contributions from the community.
-
-## DOCUMENTATION
- This manual page was written by Daniel DeLeo <dan@opscode.com>.
- Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.
-
-## CHEF
- Knife is distributed with Chef. <http://docs.chef.io>
-
diff --git a/distro/common/markdown/man1/knife.mkd b/distro/common/markdown/man1/knife.mkd
deleted file mode 100644
index 3d7c095c10..0000000000
--- a/distro/common/markdown/man1/knife.mkd
+++ /dev/null
@@ -1,213 +0,0 @@
-knife(1) -- Chef Server API client utility
-========================================
-
-## SYNOPSIS
-
-__knife__ _sub-command_ [_argument_...] _(options)_
-
-## DESCRIPTION
-
-Knife is a command-line utility used to manage data on a Chef server
-through the HTTP(S) API. Knife is organized into groups of subcommands
-centered around the various object types in Chef. Each category of
-subcommand is documented in its own manual page. Available topics are:
-
- * bootstrap
- * client
- * configure
- * cookbook-site
- * cookbook
- * data-bag
- * environment
- * exec
- * index
- * node
- * recipe
- * role
- * search
- * ssh
- * status
- * tag
-
-If the knife manuals are in your `MANPATH`, you can access help for the
-above topics using `man knife-TOPIC`; otherwise, you can view the
-documentation using `knife help TOPIC`.
-
-## OPTIONS
- * `-s`, `--server-url` URL:
- Chef Server URL, corresponds to `Chef::Config` `chef_server_url`.
- * `-k`, `--key` KEY:
- API Client Key, corresponds to `Chef::Config` `client_key`.
- * `-c`, `--config` CONFIG:
- The configuration file to use
- * `-E`, `--environment ENVIRONMENT`:
- Set the Chef environment (except for in searches, where this will be flagrantly ignored)
- * `-e`, `--editor` EDITOR:
- Set the editor to use for interactive commands
- * `-F`, `--format` FORMAT:
- Which format to use for output. See FORMATS for details.
- * `-d`, `--disable-editing`:
- Do not open EDITOR, just accept the data as is
- * `-u`, `--user` USER:
- API Client Username, corresponds to `Chef::Config` `node_name`.
- * `-p`, `--print-after`:
- Show the data after a destructive operation
- * `-v`, `--version`:
- Show chef version
- * `-V`, `--verbose`:
- More verbose output. Use twice for max verbosity.
- * `-y`, `--yes`:
- Say yes to all prompts for confirmation
- * `--defaults`:
- Accept default values for all questions
- * `--[no-]color`:
- Use colored output. Color enabled by default.
- * `-h`, `--help`:
- Show the available options for a command.
-
-## SUB-COMMANDS
-
-Sub-commands that operate on the basic Chef data types are structured as
-_NOUN verb NOUN (options)_. For all data types, the following commands
-are available:
-
-* create (create)
-* list and show (read)
-* edit (update)
-* delete (destroy)
-
-Knife also includes commands that take actions other than displaying or
-modifying data on the Chef Server, such as __knife-ssh(1)__.
-
-## CONFIGURATION
-
-The knife configuration file is a Ruby DSL to set configuration
-parameters for Knife's __GENERAL OPTIONS__. The default location for the
-config file is `~/.chef/knife.rb`. If managing multiple Chef
-repositories, per-repository config files can be created. The file must
-be `.chef/knife.rb` in the current directory of the repository.
-
-If the config file exists, knife uses these settings for __GENERAL OPTIONS__ defaults.
-
- * `node_name`:
- User or client identity (i.e., _name_) to use for authenticating
- requests to the Chef Server.
- * `client_key`:
- Private key file to authenticate to the Chef server. Corresponds to the
- `-k` or `--key` option.
- * `chef_server_url`:
- URL of the Chef server. Corresponds to the `-s` or `--server-url`
- option. This is requested from the user when running this sub-command.
- * `syntax_check_cache_path`:
- Specifies the path to a directory where knife caches information
- about files that it has syntax checked.
- * `validation_client_name`:
- Specifies the name of the client used to validate new clients.
- * `validation_key`:
- Specifies the private key file to use when bootstrapping new hosts.
- See knife-client(1) for more information about the validation
- client.
- * `cookbook_copyright`, `cookbook_email`, `cookbook_license`, `readme_format`
- Used by `knife cookbook create` sub-command to specify the copyright
- holder, maintainer email, license and readme format (respectively) for new cookbooks.
- The copyright holder is listed as the maintainer in the cookbook's
- metadata and as the Copyright in the comments of the default recipe. The
- maintainer email is used in the cookbook metadata. The license
- determines what preamble to put in the comment of the default recipe,
- and is listed as the license in the cookbook metadata. Currently
- supported licenses are "apachev2" and "none". Any other values will
- result in an empty license in the metadata (needs to be filled in by the
- author), and no comment preamble in the default recipe. Currently supported
- readme formats are "md", "mkd", "txt", and "rdoc". Any other value will
- result in an unformatted README.
-
-## FILES
-
-_~/.chef/knife.rb_
-
-Ruby DSL configuration file for knife. See __CONFIGURATION__.
-
-## FORMATS
-
-The amount of content displayed and the output format are
-modified by the `--format` option. If no alternate format is selected,
-the default is summary.
-
-Valid formats are:
-
- * `summary`:
- displays the node in a custom, summarized format (default)
- * `text`:
- displays the node data in its entirety using the colorized tree display
- * `json`:
- displays the node in JSON format
- * `yaml`:
- displays the node in YAML format
- * `pp`:
- displays the node using Ruby's pretty printer.
-
-For brevity, only the first character of the format is required, for
-example, -Fj will produce JSON format output.
-
-## CHEF WORKFLOW
-
-When working with Chef and Knife in the local repository, the recommended workflow outline looks like:
-
-* Create repository. A skeleton sample is provided at _http://github.com/opscode/chef-repo/_.
-* Configure knife, see __CONFIGURATION__.
-* Download cookbooks from the Opscode cookbooks site, see __COOKBOOK SITE SUB-COMMANDS__.
-* Or, create new cookbooks, see `cookbook create` sub-command.
-* Commit changes to the version control system. See your tool's documentation.
-* Upload cookbooks to the Chef Server, see __COOKBOOK SUB-COMMANDS__.
-* Launch instances in the Cloud, OR provision new hosts; see __CLOUD COMPUTING SUB-COMMANDS__ and __BOOTSTRAP SUB-COMMANDS__.
-* Watch Chef configure systems!
-
-A note about git: Opscode and many folks in the Chef community use git,
-but it is not required, except in the case of the `cookbook site vendor`
-sub-command, as it uses git directly. Version control is strongly
-recommended though, and git fits with a lot of the workflow paradigms.
-
-
-## EXAMPLES
-
-
-## ENVIRONMENT
- * `EDITOR`:
- The text editor to use for editing data. The --editor option takes
- precedence over this value, and the --disable-editing option suppresses
- data editing entirely.
-
-## SEE ALSO
- __chef-client(8)__ __chef-server(8)__ __chef-shell(1)__
-
- __knife-bootstrap(1)__ __knife-client(1)__ __knife-configure(1)__
- __knife-cookbook-site(1)__ __knife-cookbook(1)__ __knife-data-bag(1)__
- __knife-environment(1)__ __knife-exec(1)__ __knife-index(1)__
- __knife-node(1)__ __knife-recipe(1)__ __knife-role(1)__
- __knife-search(1)__ __knife-ssh(1)__ __knife-tag(1)__
-
- Complete Chef documentation is available online: <http://docs.chef.io/>
-
- JSON is JavaScript Object Notation <http://json.org/>
-
- SOLR is an open source search engine. <http://lucene.apache.org/solr/>
-
- __git(1)__ is a version control system <http://git-scm.com/>
-
- This manual page was generated from Markdown with __ronn(1)__ <http://rtomayko.github.com/ronn/ronn.1.html>
-
-## AUTHOR
- Chef was written by Adam Jacob <adam@opscode.com> of Opscode
- (<http://www.opscode.com>), with contributions from the community.
-
-## DOCUMENTATION
- This manual page was written by Joshua Timberman <joshua@opscode.com>.
-
-## LICENSE
- Both Chef and this documentation are released under the terms of the
- Apache 2.0 License. You may view the license online: <http://www.apache.org/licenses/LICENSE-2.0.html>
- On some systems, the complete text of the Apache 2.0 License may be found in `/usr/share/common-licenses/Apache-2.0`.
-
-## CHEF
- Knife is distributed with Chef. <http://docs.chef.io/>
-
diff --git a/distro/common/markdown/man8/chef-client.mkd b/distro/common/markdown/man8/chef-client.mkd
deleted file mode 100644
index 7506e3b925..0000000000
--- a/distro/common/markdown/man8/chef-client.mkd
+++ /dev/null
@@ -1,75 +0,0 @@
-chef-client(8) -- Runs a client node connecting to a chef-server.
-========================================
-
-## SYNOPSIS
-
-__chef-client__ _(options)_
-
- * `-S`, `--server CHEFSERVERURL`:
- The chef server URL
- * `-c`, `--config CONFIG`:
- The configuration file to use
- * `-d`, `--daemonize`:
- Daemonize the process
- * `-g`, `--group GROUP`:
- Group to set privilege to
- * `-i`, `--interval SECONDS`:
- Run chef-client periodically, in seconds
- * `-j`, `--json-attributes JSON_ATTRIBS`:
- Load attributes from a JSON file or URL
- * `-E`, `--environment ENVIRONMENT`:
- Set the Chef Environment on the node
- * `-l`, `--log_level LEVEL`:
- Set the log level (debug, info, warn, error, fatal)
- * `-L`, `--logfile LOGLOCATION`:
- Set the log file location, defaults to STDOUT - recommended for
- daemonizing
- * `-N`, `--node-name NODE_NAME`:
- The node name for this client
- * `-o`, `--override-runlist`:
- Replace current run list with specified items
- * `-K`, `--validation_key KEY_FILE`:
- Set the validation key file location, used for registering new clients
- * `-k`, `--client_key KEY_FILE`:
- Set the client key file location
- * `-s`, `--splay SECONDS`:
- The splay time for running at intervals, in seconds
- * `-u`, `--user USER`:
- User to set privilege to
- * `-P`, `--pid PIDFILE`:
- Set the PID file location, defaults to /tmp/chef-client.pid
- * `--once`:
- Cancel any interval or splay options, run chef once and exit
- * `--skip-cookbook-sync`:
- Skip cookbook synchronization
- * `-v`, `--version`:
- Show chef version
- * `-h`, `--help`:
- Show this message
-
-## DESCRIPTION
-
-The Chef Client is where almost all of the work in Chef is done. It
-communicates with the Chef Server via REST, authenticates via Signed
-Header Authentication, and compiles and executes Cookbooks.
-
-A Chef Client does work on behalf of a Node. A single Chef Client can
-run recipes for multiple Nodes.
-
-Clients are where all the action happens - the Chef Server and Chef Expander
-are largely services that exist only to provide the Client with information.
-
-## SEE ALSO
-
-Full documentation for Chef and chef-client is located on docs site, http://docs.chef.io/.
-
-## AUTHOR
-
-Chef was written by Adam Jacob <adam@ospcode.com> of Opscode
-(http://www.opscode.com), with contributions from the community. This
-manual page was written by Joshua Timberman <joshua@opscode.com> with
-help2man. Permission is granted to copy, distribute and / or modify
-this document under the terms of the Apache 2.0 License.
-
-On Debian systems, the complete text of the Apache 2.0 License can be
-found in /usr/share/common-licenses/Apache-2.0.
diff --git a/distro/common/markdown/man8/chef-expander.mkd b/distro/common/markdown/man8/chef-expander.mkd
deleted file mode 100644
index a2bb7d72b0..0000000000
--- a/distro/common/markdown/man8/chef-expander.mkd
+++ /dev/null
@@ -1,81 +0,0 @@
-chef-expander(8) -- fetches messages from RabbitMQ, processes, and loads into chef-solr
-========================================
-
-## SYNOPSIS
-
-__chef-expander__ _(options)_
-
- * `-c`, `--config CONFIG_FILE`:
- a configuration file to use
- * `-i`, `--index INDEX`:
- the slot this node will occupy in the ring
- * `-n`, `--node-count NUMBER`:
- the number of nodes in the ring
- * `-l`, `--log-level LOG_LEVEL`:
- set the log level
- * `-L`, `--logfile LOG_LOCATION`:
- Logfile to use
- * `-d`, `--daemonize`:
- fork into the background
- * `-P`, `--pid PIDFILE`:
- PID file
- * `-h`, `--help`:
- show help message
- * `-v`, `--version`:
- show the version and exit
-
-## DESCRIPTION
-
-Chef Expander fetches messages from RabbitMQ, processes them into the
-correct format to be loaded into Solr and loads them into Solr.
-
-__Running Chef Expander__
-
-Chef Expander is designed for clustered operation, though small
-installations will only need one worker process. To run Chef
-Expander with one worker process, run chef-expander -n 1.
-You will then have a master and worker process, which looks like
-this in ps:
-
- your-shell> ps aux|grep expander
- you 52110 0.1 0.7 2515476 62748 s003 S+ 3:49PM 0:00.80 chef-expander worker #1 (vnodes 0-1023)
- you 52108 0.1 0.5 2492880 41696 s003 S+ 3:49PM 0:00.91 ruby bin/chef-expander -n 1
-
-Workers are single threaded and therefore cannot use more than 100%
-of a single CPU. If you find that your queues are getting backlogged,
-increase the number of workers
-
-__Design__
-
-Chef Expander uses 1024 queues (called vnodes in some places) to allow
-you to scale the number of Chef Expander workers to meet the needs of
-your infrastructure. When objects are saved in the API server, they are
-added to queues based on their database IDs. These queues can be assigned
-to different Chef Expander workers to distribute the load of processing
-the index updates.
-
-__Chef Expander Operation and Troubleshooting__
-
-Chef Expander includes chef-expanderctl, a management program that allows
-you to get status information or change the logging verbosity (without
-restarting).
-
-See __chef-expanderctl__(8) for details.
-
-## SEE ALSO
-
-__chef-expanderctl__(8)
-__chef-solr__(8)
-
-Full documentation for Chef and chef-server is located on docs site, http://docs.chef.io/.
-
-## AUTHOR
-
-Chef was written by Adam Jacob <adam@ospcode.com> of Opscode
-(http://www.opscode.com), with contributions from the community. This
-manual page was created by Nuo Yan <nuo@opscode.com>. Permission is
-granted to copy, distribute and / or modify this document under the
-terms of the Apache 2.0 License.
-
-On Debian systems, the complete text of the Apache 2.0 License can be
-found in /usr/share/common-licenses/Apache-2.0.
diff --git a/distro/common/markdown/man8/chef-expanderctl.mkd b/distro/common/markdown/man8/chef-expanderctl.mkd
deleted file mode 100644
index db593cb47a..0000000000
--- a/distro/common/markdown/man8/chef-expanderctl.mkd
+++ /dev/null
@@ -1,57 +0,0 @@
-chef-expanderctl(8) -- management program for chef-expander
-========================================
-
-## SYNOPSIS
-
-__chef-expanderctl__ _COMMAND_
-
-__Commands:__
-
- * `help`:
- Show help message
- * `queue-depth`:
- display the aggregate queue backlog
- * `queue-status`:
- show the backlog and consumer count for each vnode queue
- * `node-status`:
- show the status of the nodes in the cluster
- * `log-level`:
- sets the log level of all nodes in the cluster
-
-## DESCRIPTION
-
-Chef-expanderctl is a management program that allows
-you to get status information or change the logging
-verbosity (without restarting). chef-expanderctl has
-the following commands:
-
-* __chef-expanderctl help__
-prints usage.
-* __chef-expanderctl queue-depth__
-Shows the total number of messages in the queues.
-* __chef-expanderctl queue-status__
-Show the number of messages in each queue. This is mainly of use when
-debugging a Chef Expander cluster.
-* __chef-expanderctl log-level LEVEL__
-Sets the log level on a running Chef Expander or cluster.
-If you suspect that a worker process is stuck, as long as you are using
-clustered operation, you can simply kill the worker process and it will
-be restarted by the master process.
-
-## SEE ALSO
-
-__chef-expander-cluster__(8)
-__chef-solr__(8)
-
-Full documentation for Chef and chef-server is located on docs site, http://docs.chef.io/.
-
-## AUTHOR
-
-Chef was written by Adam Jacob <adam@ospcode.com> of Opscode
-(http://www.opscode.com), with contributions from the community. This
-manual page was created by Nuo Yan <nuo@opscode.com>. Permission is
-granted to copy, distribute and / or modify this document under the
-terms of the Apache 2.0 License.
-
-On Debian systems, the complete text of the Apache 2.0 License can be
-found in /usr/share/common-licenses/Apache-2.0.
diff --git a/distro/common/markdown/man8/chef-server-webui.mkd b/distro/common/markdown/man8/chef-server-webui.mkd
deleted file mode 100644
index b176d12690..0000000000
--- a/distro/common/markdown/man8/chef-server-webui.mkd
+++ /dev/null
@@ -1,121 +0,0 @@
-chef-server-webui(8) -- Start the Chef Server merb application slice providing Web User Interface (Management Console).
-========================================
-
-## SYNOPSIS
-
-__chef-server-webui__ _(options)_
-
- * `-u`, `--user USER`:
- This flag is for having chef-server-webui run as a user other than the
- one currently logged in. Note: if you set this you must also provide a
- --group option for it to take effect.
- * `-G`, `--group GROUP`:
- This flag is for having chef-server-webui run as a group other than the
- one currently logged in. Note: if you set this you must also provide a
- --user option for it to take effect.
- * `-d`, `--daemonize`:
- This will run a single chef-server-webui in the background.
- * `-N`, `--no-daemonize`:
- This will allow you to run a cluster in console mode.
- * `-c`, `--cluster-nodes NUM_MERBS`:
- Number of merb daemons to run for chef-server-webui.
- * `-I`, `--init-file FILE`:
- File to use for initialization on load, defaults to config/init.rb.
- * `-p`, `--port PORTNUM`:
- Port to run chef-server-webui on, defaults to 4040. Additional nodes (-c)
- listen on incrementing port numbers.
- * `-o`, `--socket-file FILE`:
- Socket file to run chef-server-webui on, defaults to
- [Merb.root]/log/merb.sock. This is for web servers, like thin, that use
- sockets. Specify this *only* if you *must*.
- * `-s`, `--socket SOCKNUM`:
- Socket number to run chef-server-webui on, defaults to 0.
- * `-n`, `--name NAME`:
- Set the name of the application. This is used in the process title and
- log file names.
- * `-P`, `--pid PIDFILE`:
- PID file, defaults to [Merb.root]/log/merb.main.pid for the master
- process and[Merb.root]/log/merb.[port number].pid for worker processes.
- For clusters, use %s to specify where in the file chef-server-webui
- should place the port number. For instance: -P myapp.%s.pid.
- * `-h`, `--host HOSTNAME`:
- Host to bind to (default is 0.0.0.0).
- * `-m`, `--merb-root PATH_TO_APP_ROOT`:
- The path to the Merb.root for the app you want to run
- (default is current working directory).
- * `-a`, `--adapter ADAPTER`:
- The rack adapter to use to run chef-server-webui (default is mongrel)
- [mongrel, emongrel, thin, ebb, fastcgi, webrick].
- * `-R`, `--rackup FILE`:
- Load an alternate Rack config file (default is config/rack.rb).
- * `-i`, `--irb-console`:
- This flag will start chef-server-webui in irb console mode. All your models
- and other classes will be available for you in an irb session.
- * `-S`, `--sandbox`:
- This flag will enable a sandboxed irb console. If your ORM supports
- transactions, all edits will be rolled back on exit.
- * `-l`, `--log-level LEVEL`:
- Log levels can be set to any of these options:
- debug < info < warn < error < fatal (default is info).
- * `-L`, `--log LOGFILE`:
- A string representing the logfile to use. Defaults to
- [Merb.root]/log/merb.[main].log for the master process and
- [Merb.root]/log/merb[port number].logfor worker processes.
- * `-e`, `--environment STRING`:
- Environment to run Merb under [development, production, testing]
- (default is development).
- * `-r`, `--script-runner ['RUBY CODE'| FULL_SCRIPT_PATH]`:
- Command-line option to run scripts and/or code in the chef-server-webui
- app.
- * `-K`, `-graceful PORT or all`:
- Gracefully kill chef-server-webui proceses by port number.
- Use chef-server -K all to gracefully kill all merbs.
- * `-k`, `--kill PORT`:
- Force kill one merb worker by port number. This will cause the worker
- to be respawned.
- * `--fast-deploy`:
- Reload the code, but not yourinit.rb or gems.
- * `-X`, `--mutex on/off`:
- This flag is for turning the mutex lock on and off.
- * `-D`, `--debugger`:
- Run chef-server-webui using rDebug.
- * `-V`, `--verbose`:
- Print extra information.
- * `-C`, `--console-trap`:
- Enter an irb console on ^C.
- * `-?`, `-H`, `--help`:
- Show this help message.
-
-## DESCRIPTION
-
-The Chef Server WebUI (Management Console) is a Merb application slice.
-The default listen port is 4040.
-
-The Management Console is Chef Server's web interface. Nodes, roles,
-cookbooks, data bags, and API clients can be managed through the Management
-Console. Search can also be done on the console.
-
-In order to start using the Management Console, you need to first create
-a user or change the default password on the "admin" user.
-
-The default credentials are:
-
-- `Username`: admin
-- `Password`: p@ssw0rd1
-
-## SEE ALSO
-
-Full documentation for Chef and chef-server-webui (Management Console)
-is located on the Chef docs site, http://docs.chef.io/.
-
-## AUTHOR
-
-Chef was written by Adam Jacob <adam@ospcode.com> of Opscode
-(http://www.opscode.com), with contributions from the community. This
-manual page was written by Joshua Timberman <joshua@opscode.com> with
-help2man for the Debian project (but may be used by others). Permission
-is granted to copy, distribute and / or modify this document under the
-terms of the Apache 2.0 License.
-
-On Debian systems, the complete text of the Apache 2.0 License can be
-found in /usr/share/common-licenses/Apache-2.0.
diff --git a/distro/common/markdown/man8/chef-server.mkd b/distro/common/markdown/man8/chef-server.mkd
deleted file mode 100644
index 46a5ea4346..0000000000
--- a/distro/common/markdown/man8/chef-server.mkd
+++ /dev/null
@@ -1,120 +0,0 @@
-chef-server(8) - Start the Chef Server merb application slice.
-========================================
-
-## SYNOPSIS
-
-__chef-server__ _(options)_
-
- * `-u`, `--user USER`:
- This flag is for having chef-server-webui run as a user other than the
- one currently logged in. Note: if you set this you must also provide a
- --group option for it to take effect.
- * `-G`, `--group GROUP`:
- This flag is for having chef-server-webui run as a group other than the
- one currently logged in. Note: if you set this you must also provide a
- --user option for it to take effect.
- * `-d`, `--daemonize`:
- This will run a single chef-server-webui in the background.
- * `-N`, `--no-daemonize`:
- This will allow you to run a cluster in console mode.
- * `-c`, `--cluster-nodes NUM_MERBS`:
- Number of merb daemons to run for chef-server-webui.
- * `-I`, `--init-file FILE`:
- File to use for initialization on load, defaults to config/init.rb.
- * `-p`, `--port PORTNUM`:
- Port to run chef-server-webui on, defaults to 4040. Additional nodes (-c)
- listen on incrementing port numbers.
- * `-o`, `--socket-file FILE`:
- Socket file to run chef-server-webui on, defaults to
- [Merb.root]/log/merb.sock. This is for web servers, like thin, that use
- sockets. Specify this *only* if you *must*.
- * `-s`, `--socket SOCKNUM`:
- Socket number to run chef-server-webui on, defaults to 0.
- * `-n`, `--name NAME`:
- Set the name of the application. This is used in the process title and
- log file names.
- * `-P`, `--pid PIDFILE`:
- PID file, defaults to [Merb.root]/log/merb.main.pid for the master
- process and[Merb.root]/log/merb.[port number].pid for worker processes.
- For clusters, use %s to specify where in the file chef-server-webui
- should place the port number. For instance: -P myapp.%s.pid.
- * `-h`, `--host HOSTNAME`:
- Host to bind to (default is 0.0.0.0).
- * `-m`, `--merb-root PATH_TO_APP_ROOT`:
- The path to the Merb.root for the app you want to run
- (default is current working directory).
- * `-a`, `--adapter ADAPTER`:
- The rack adapter to use to run chef-server-webui (default is mongrel)
- [mongrel, emongrel, thin, ebb, fastcgi, webrick].
- * `-R`, `--rackup FILE`:
- Load an alternate Rack config file (default is config/rack.rb).
- * `-i`, `--irb-console`:
- This flag will start chef-server-webui in irb console mode. All your models
- and other classes will be available for you in an irb session.
- * `-S`, `--sandbox`:
- This flag will enable a sandboxed irb console. If your ORM supports
- transactions, all edits will be rolled back on exit.
- * `-l`, `--log-level LEVEL`:
- Log levels can be set to any of these options:
- debug < info < warn < error < fatal (default is info).
- * `-L`, `--log LOGFILE`:
- A string representing the logfile to use. Defaults to
- [Merb.root]/log/merb.[main].log for the master process and
- [Merb.root]/log/merb[port number].logfor worker processes.
- * `-e`, `--environment STRING`:
- Environment to run Merb under [development, production, testing]
- (default is development).
- * `-r`, `--script-runner ['RUBY CODE'| FULL_SCRIPT_PATH]`:
- Command-line option to run scripts and/or code in the chef-server-webui
- app.
- * `-K`, `-graceful PORT or all`:
- Gracefully kill chef-server-webui proceses by port number.
- Use chef-server -K all to gracefully kill all merbs.
- * `-k`, `--kill PORT`:
- Force kill one merb worker by port number. This will cause the worker
- to be respawned.
- * `--fast-deploy`:
- Reload the code, but not yourinit.rb or gems.
- * `-X`, `--mutex on/off`:
- This flag is for turning the mutex lock on and off.
- * `-D`, `--debugger`:
- Run chef-server-webui using rDebug.
- * `-V`, `--verbose`:
- Print extra information.
- * `-C`, `--console-trap`:
- Enter an irb console on ^C.
- * `-?`, `-H`, `--help`:
- Show this help message.
-
-## DESCRIPTION
-
-The Chef Server provides a central point for the distribution of Cookbooks,
-management and authentication of Nodes, and the use of Search. It provides
-a REST API.
-
-The API service is what clients use to interact with the server to manage
-node configuration in Chef. By default, the service is started on port 4000
-as a Merb application slice running with the thin server adapter.
-
-The two methods of interaction with the API for humans are the command-line
-tool Knife and the Management Console. The Chef Client library is used for
-interacting with the API for client nodes.
-
-## SEE ALSO
-
-__chef-client__(8)
-__chef-server-webui__(8)
-__knife__(1)
-
-Full documentation for Chef and chef-server is located on docs site, http://docs.chef.io/.
-
-## AUTHOR
-
-Chef was written by Adam Jacob <adam@ospcode.com> of Opscode
-(http://www.opscode.com), with contributions from the community. This
-manual page was written by Joshua Timberman <joshua@opscode.com> with
-help2man. Permission is granted to copy, distribute and / or modify
-this document under the terms of the Apache 2.0 License.
-
-On Debian systems, the complete text of the Apache 2.0 License can be
-found in /usr/share/common-licenses/Apache-2.0.
diff --git a/distro/common/markdown/man8/chef-solo.mkd b/distro/common/markdown/man8/chef-solo.mkd
deleted file mode 100644
index 9d5d9a43b7..0000000000
--- a/distro/common/markdown/man8/chef-solo.mkd
+++ /dev/null
@@ -1,107 +0,0 @@
-chef-solo(8) -- Runs chef in solo mode against a specified cookbook location.
-========================================
-
-## SYNOPSIS
-
-__chef-solo__ _(options)_
-
- * `-c`, `--config CONFIG`:
- The configuration file to use
- * `-d`, `--daemonize`:
- Daemonize the process
- * `-g`, `--group GROUP`:
- Group to set privilege to
- * `-i`, `--interval SECONDS`:
- Run chef-client periodically, in seconds
- * `-j`, `--json-attributes JSON_ATTRIBS`:
- Load attributes from a JSON file or URL
- * `-l`, `--log_level LEVEL`:
- Set the log level (debug, info, warn, error, fatal)
- * `-L`, `--logfile LOGLOCATION`:
- Set the log file location, defaults to STDOUT - recommended for
- daemonizing
- * `-N`, `--node-name NODE_NAME`:
- The node name for this client
- * `-r`, `--recipe-url RECIPE_URL`:
- Pull down a remote gzipped tarball of recipes and untar it to the
- cookbook cache.
- * `-s`, `--splay SECONDS`:
- The splay time for running at intervals, in seconds
- * `-u`, `--user USER`:
- User to set privilege to
- * `-v`, `--version`:
- Show chef version
- * `-h`, `--help`:
- Show this message
-
-## DESCRIPTION
-
-Chef Solo allows you to run Chef Cookbooks in the absence of a Chef Server.
-To do this, the complete cookbook needs to be present on disk.
-
-By default Chef Solo will look in /etc/chef/solo.rb for its configuration.
-This configuration file has two required variables: file_cache_path and
-cookbook_path.
-
-For example:
- file_cache_path "/var/chef-solo"
- cookbook_path "/var/chef-solo/cookbooks"
-
-For your own systems, you can change this to reflect any directory you like,
-but you'll need to specify absolute paths and the cookbook_path directory
-should be a subdirectory of the file_cache_path.
-
-You can also specify cookbook_path as an array, passing multiple locations
-to search for cookbooks.
-
-For example:
- file_cache_path "/var/chef-solo"
- cookbook_path ["/var/chef-solo/cookbooks", "/var/chef-solo/site-cookbooks"]
-
-Note that earlier entries are now overridden by later ones.
-
-Since chef-solo doesn't have any interaction with a Chef Server, you'll need
-to specify node-specifc attributes in a JSON file. This can be located on the
-target system itself, or it can be stored on a remote server such as S3, or a
-web server on your network.
-
-Within the JSON file, you'll also specify the recipes that Chef should run in
-the "run_list". An example JSON file, which sets a resolv.conf:
-
- {
- "resolver": {
- "nameservers": [ "10.0.0.1" ],
- "search":"int.example.com"
- },
- "run_list": [ "recipe[resolver]" ]
- }
-
-Then you can run chef-solo with -j to specify the JSON file. It will look for
-cookbooks in the cookbook_path configured in the configuration file, and apply
-attributes and use the run_list from the JSON file specified.
-
-You can use -c to specify the path to the configuration file (if you don't want
-chef-solo to use the default). You can also specify -r for a cookbook tarball.
-
-For example:
- chef-solo -c ~/solo.rb -j ~/node.json -r http://www.example.com/chef-solo.tar.gz
-
-In the above case, chef-solo would extract the tarball to your specified
-cookbook_path, use ~/solo.rb as the configuration file, and apply attributes
-and use the run_list from ~/node.json.
-
-## SEE ALSO
-
-Full documentation for Chef and chef-solo is located on the Chef docs site,
-http://docs.chef.io/.
-
-## AUTHOR
-
-Chef was written by Adam Jacob <adam@ospcode.com> of Opscode
-(http://www.opscode.com), with contributions from the community. This
-manual page was written by Joshua Timberman <joshua@opscode.com> with
-help2man. Permission is granted to copy, distribute and / or modify
-this document under the terms of the Apache 2.0 License.
-
-On Debian systems, the complete text of the Apache 2.0 License can be
-found in /usr/share/common-licenses/Apache-2.0.
diff --git a/distro/common/markdown/man8/chef-solr.mkd b/distro/common/markdown/man8/chef-solr.mkd
deleted file mode 100644
index a210a90a07..0000000000
--- a/distro/common/markdown/man8/chef-solr.mkd
+++ /dev/null
@@ -1,89 +0,0 @@
-chef-solr(8) -- Runs as Chef's search server
-========================================
-
-## SYNOPSIS
-
-__chef-solr__ _(options)_
-
- * `-c`, `--config CONFIG`:
- The configuration file to use
- * `-d`, `--daemonize`:
- Daemonize the process
- * `-g`, `--group GROUP`:
- Group to set privilege to
- * `-l`, `--log_level LEVEL`:
- Set the log level (debug, info, warn, error, fatal)
- * `-L`, `--logfile LOGLOCATION`:
- Set the log file location, defaults to STDOUT - recommended for daemonizing
- * `-P`, `--pid PIDFILE`:
- Set the PID file location, defaults to /tmp/chef-solr.pid
- * `-D`, `--solr-data-dir PATH`:
- Where the Solr data lives
- * `-x`, `--solor-heap-size SIZE`:
- Set the size of the Java Heap
- * `-H`, `--solr-home-dir PATH`:
- Solr home directory
- * `-j`, `--java-opts OPTS`:
- Raw options passed to Java
- * `-x`, `--solor-heap-size`:
- Set the size of the Java Heap
- * `-W`, `--solr-jetty-dir PATH`:
- Where to place the Solr Jetty instance
- * `-u`, `--user USER`:
- User to set privilege to
- * `-v`, `--version`:
- Show chef-solr version
- * `-h`, `--help`:
- Show this message
-
-## DESCRIPTION
-
-Chef-solr provides search service for Chef. You need to have both
-chef-solr and chef-expander-cluster running in order for search to work.
-
-__Installation__
-
-Make sure you backed up your data if you are upgrading from a previous version.
-Run chef-solr-installer to upgrade your Chef Solr installation. Answer "yes"
-when prompted for confirmation. The process should look like this:
-
- yourshell> chef-solr-installer
- Configuration setting solr_heap_size is unknown and will be ignored
-
- Chef Solr is already installed in /var/chef/solr
- Do you want to overwrite the current install? All existing Solr data will be lost. [y/n] y
- Removing the existing Chef Solr installation
- rm -rf /var/chef/solr
- rm -rf /var/chef/solr-jetty
- rm -rf /var/chef/solr/data
- Creating Solr Home Directory
- mkdir -p /var/chef/solr
- entering /var/chef/solr
- tar zxvf /Users/ddeleo/opscode/chef/chef-solr/solr/solr-home.tar.gz
- Creating Solr Data Directory
- mkdir -p /var/chef/solr/data
- Unpacking Solr Jetty
- mkdir -p /var/chef/solr-jetty
- entering /var/chef/solr-jetty
- tar zxvf /Users/ddeleo/opscode/chef/chef-solr/solr/solr-jetty.tar.gz
-
- Successfully installed Chef Solr.
- You can restore your search index using `knife index rebuild`
-
-## SEE ALSO
-
-__chef-expander-cluster__(8)
-
-Full documentation for Chef and chef-server is located on the Chef
-Docs site, http://docs.chef.io/.
-
-## AUTHOR
-
-Chef was written by Adam Jacob <adam@ospcode.com> of Opscode
-(http://www.opscode.com), with contributions from the community. This
-manual page was written by Joshua Timberman <joshua@opscode.com> with
-help2man. Permission is granted to copy, distribute and / or modify
-this document under the terms of the Apache 2.0 License.
-
-On Debian systems, the complete text of the Apache 2.0 License can be
-found in /usr/share/common-licenses/Apache-2.0.
diff --git a/distro/powershell/chef/Chef.PowerShell.dll b/distro/powershell/chef/Chef.PowerShell.dll
new file mode 100644
index 0000000000..9aa1c62850
--- /dev/null
+++ b/distro/powershell/chef/Chef.PowerShell.dll
Binary files differ
diff --git a/distro/powershell/chef/Newtonsoft.Json.dll b/distro/powershell/chef/Newtonsoft.Json.dll
new file mode 100644
index 0000000000..1971a35679
--- /dev/null
+++ b/distro/powershell/chef/Newtonsoft.Json.dll
Binary files differ
diff --git a/distro/powershell/chef/chef.psm1 b/distro/powershell/chef/chef.psm1
index 6646226795..05fee05e5e 100644
--- a/distro/powershell/chef/chef.psm1
+++ b/distro/powershell/chef/chef.psm1
@@ -109,6 +109,12 @@ public enum StandardHandle : int
Error = -12
}
+public enum HandleFlags : int
+{
+ HANDLE_FLAG_INHERIT = 0x00000001,
+ HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002
+}
+
public static class Kernel32
{
[DllImport("kernel32.dll", SetLastError=true)]
@@ -128,11 +134,12 @@ public static class Kernel32
[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr GetStdHandle(
StandardHandle nStdHandle);
-
- [DllImport("kernel32", SetLastError=true)]
- public static extern int WaitForSingleObject(
- IntPtr hHandle,
- int dwMilliseconds);
+
+ [DllImport("kernel32.dll")]
+ public static extern bool SetHandleInformation(
+ IntPtr hObject,
+ int dwMask,
+ uint dwFlags);
[DllImport("kernel32", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
@@ -144,6 +151,32 @@ public static class Kernel32
public static extern bool GetExitCodeProcess(
IntPtr hProcess,
out int lpExitCode);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern bool CreatePipe(
+ out IntPtr phReadPipe,
+ out IntPtr phWritePipe,
+ IntPtr lpPipeAttributes,
+ uint nSize);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern bool ReadFile(
+ IntPtr hFile,
+ [Out] byte[] lpBuffer,
+ uint nNumberOfBytesToRead,
+ ref int lpNumberOfBytesRead,
+ IntPtr lpOverlapped);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern bool PeekNamedPipe(
+ IntPtr handle,
+ byte[] buffer,
+ uint nBufferSize,
+ ref uint bytesRead,
+ ref uint bytesAvail,
+ ref uint BytesLeftThisMessage);
+
+ public const int STILL_ACTIVE = 259;
}
}
"@
@@ -156,13 +189,6 @@ function Run-ExecutableAndWait($AppPath, $ArgumentString) {
$si = New-Object Chef.STARTUPINFO
$pi = New-Object Chef.PROCESS_INFORMATION
- $si.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($si)
- $si.wShowWindow = [Chef.ShowWindow]::SW_SHOW
- $si.dwFlags = [Chef.STARTF]::STARTF_USESTDHANDLES
- $si.hStdError = [Chef.Kernel32]::GetStdHandle([Chef.StandardHandle]::Error)
- $si.hStdOutput = [Chef.Kernel32]::GetStdHandle([Chef.StandardHandle]::Output)
- $si.hStdInput = [Chef.Kernel32]::GetStdHandle([Chef.StandardHandle]::Input)
-
$pSec = New-Object Chef.SECURITY_ATTRIBUTES
$pSec.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($pSec)
$pSec.bInheritHandle = $true
@@ -170,25 +196,117 @@ function Run-ExecutableAndWait($AppPath, $ArgumentString) {
$tSec.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($tSec)
$tSec.bInheritHandle = $true
- $success = [Chef.Kernel32]::CreateProcess($AppPath, $ArgumentString, [ref] $pSec, [ref] $tSec, $true, [Chef.CreationFlags]::NONE, [IntPtr]::Zero, $pwd, [ref] $si, [ref] $pi)
+ # Create pipe for process stdout
+ $ptr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($si))
+ [System.Runtime.InteropServices.Marshal]::StructureToPtr($pSec, $ptr, $true)
+ $hReadOut = [IntPtr]::Zero
+ $hWriteOut = [IntPtr]::Zero
+ $success = [Chef.Kernel32]::CreatePipe([ref] $hReadOut, [ref] $hWriteOut, $ptr, 0)
+ if (-Not $success) {
+ $reason = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
+ throw "Unable to create output pipe. Error code $reason."
+ }
+ $success = [Chef.Kernel32]::SetHandleInformation($hReadOut, [Chef.HandleFlags]::HANDLE_FLAG_INHERIT, 0)
+ if (-Not $success) {
+ $reason = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
+ throw "Unable to set output pipe handle information. Error code $reason."
+ }
+
+ $si.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($si)
+ $si.wShowWindow = [Chef.ShowWindow]::SW_SHOW
+ $si.dwFlags = [Chef.STARTF]::STARTF_USESTDHANDLES
+ $si.hStdOutput = $hWriteOut
+ $si.hStdError = $hWriteOut
+ $si.hStdInput = [Chef.Kernel32]::GetStdHandle([Chef.StandardHandle]::Input)
+
+ $success = [Chef.Kernel32]::CreateProcess(
+ $AppPath,
+ $ArgumentString,
+ [ref] $pSec,
+ [ref] $tSec,
+ $true,
+ [Chef.CreationFlags]::NONE,
+ [IntPtr]::Zero,
+ $pwd,
+ [ref] $si,
+ [ref] $pi
+ )
if (-Not $success) {
$reason = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
throw "Unable to create process [$ArgumentString]. Error code $reason."
}
- $waitReason = [Chef.Kernel32]::WaitForSingleObject($pi.hProcess, -1)
- if ($waitReason -ne 0) {
- if ($waitReason -eq -1) {
+
+ $buffer = New-Object byte[] 1024
+
+ # Initialize reference variables
+ $bytesRead = 0
+ $bytesAvailable = 0
+ $bytesLeftThisMsg = 0
+ $global:LASTEXITCODE = [Chef.Kernel32]::STILL_ACTIVE
+
+ $isActive = $true
+ while ($isActive) {
+ $success = [Chef.Kernel32]::GetExitCodeProcess($pi.hProcess, [ref] $global:LASTEXITCODE)
+ if (-Not $success) {
+ $reason = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
+ throw "Process exit code unavailable. Error code $reason."
+ }
+
+ $success = [Chef.Kernel32]::PeekNamedPipe(
+ $hReadOut,
+ $null,
+ $buffer.Length,
+ [ref] $bytesRead,
+ [ref] $bytesAvailable,
+ [ref] $bytesLeftThisMsg
+ )
+ if (-Not $success) {
$reason = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
- throw "Could not wait for process to terminate. Error code $reason."
+ throw "Output pipe unavailable for peeking. Error code $reason."
+ }
+
+ if ($bytesRead -gt 0) {
+ while ([Chef.Kernel32]::ReadFile($hReadOut, $buffer, $buffer.Length, [ref] $bytesRead, 0)) {
+ $output = [Text.Encoding]::UTF8.GetString($buffer, 0, $bytesRead)
+ if ($output) {
+ $output
+ }
+ if ($bytesRead -lt $buffer.Length) {
+ # Partial buffer indicating the end of stream, break out of ReadFile loop
+ # ReadFile will block until:
+ # The number of bytes requested is read.
+ # A write operation completes on the write end of the pipe.
+ # An asynchronous handle is being used and the read is occurring asynchronously.
+ # An error occurs.
+ break
+ }
+ }
} else {
- throw "WaitForSingleObject failed with return code $waitReason - it's impossible!"
+ # For some reason, you can't read from the read-end of the read-pipe before the write end has started
+ # to write. Otherwise the process just blocks forever and never returns from the read. So we peek
+ # at the pipe until there is something. But don't peek too eagerly. This is stupid stupid stupid.
+ # There must be a way to do this without having to peek at a pipe first but I have not found it.
+ #
+ # Note to the future intrepid soul who wants to fix this:
+ # 0) This is related to unreasonable CPU usage by the wrapper PS script on a 1 VCPU VM (either Hyper-V
+ # or VirtualBox) running a consumer Windows SKU (Windows 10 for example...). Test it there.
+ # 1) Maybe this entire script is unnecessary and the bugs mentioned below have been fixed or don't need
+ # to be supported.
+ # 2) The server and consumer windows schedulers have different defaults. I had a hard time reproducing
+ # any issue on a win 2008 on win 2012 server default setup. See the "foreground application scheduler
+ # priority" setting to see if it's relevant.
+ # 3) This entire endeavor is silly anyway - why are we reimplementing process forking all over? Maybe try
+ # to get the folks above to accept patches instead of extending this crazy script.
+ Start-Sleep -s 1
+ # Start-Sleep -m 100
+ }
+
+ if ($global:LASTEXITCODE -ne [Chef.Kernel32]::STILL_ACTIVE) {
+ $isActive = $false
}
}
- $success = [Chef.Kernel32]::GetExitCodeProcess($pi.hProcess, [ref] $global:LASTEXITCODE)
- if (-Not $success) {
- $reason = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
- throw "Process exit code unavailable. Error code $reason."
- }
+
+ # Cleanup handles
$success = [Chef.Kernel32]::CloseHandle($pi.hProcess)
if (-Not $success) {
$reason = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
@@ -199,6 +317,17 @@ function Run-ExecutableAndWait($AppPath, $ArgumentString) {
$reason = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
throw "Unable to release thread handle. Error code $reason."
}
+ $success = [Chef.Kernel32]::CloseHandle($hWriteOut)
+ if (-Not $success) {
+ $reason = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
+ throw "Unable to release output write handle. Error code $reason."
+ }
+ $success = [Chef.Kernel32]::CloseHandle($hReadOut)
+ if (-Not $success) {
+ $reason = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
+ throw "Unable to release output read handle. Error code $reason."
+ }
+ [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ptr)
}
function Get-ScriptDirectory {
@@ -319,9 +448,12 @@ Export-ModuleMember -function chef-solo
Export-ModuleMember -function chef-windows-service
Export-ModuleMember -function knife
-# To debug this module, uncomment the line below and then run the following.
+# To debug this module, uncomment the line below
# Export-ModuleMember -function Run-RubyCommand
+
+# Then run the following to reload the module. Use puts_argv as a helpful debug executable.
# Remove-Module chef
# Import-Module chef
-# "puts ARGV" | Out-File C:\opscode\chef\bin\puts_args
+# "puts ARGV" | Out-File C:\opscode\chef\bin\puts_args -Encoding ASCII
+# Copy-Item C:\opscode\chef\bin\ohai.bat C:\opscode\chef\bin\puts_args.bat
# Run-RubyCommand puts_args 'Here' "are" some '"very interesting"' 'arguments[to]' "`"try out`""
diff --git a/habitat/config/client.rb b/habitat/config/client.rb
new file mode 100644
index 0000000000..2d28d223c5
--- /dev/null
+++ b/habitat/config/client.rb
@@ -0,0 +1,18 @@
+chef_repo_path "{{pkg.svc_data_path}}/chef"
+file_backup_path "{{pkg.svc_data_path}}/{{cfg.file_backup_path}}"
+pid_file "{{pkg.svc_data_path}}/{{cfg.pid_file}}"
+data_collector.server_url "{{cfg.data_collector.url}}"
+data_collector.token "{{cfg.data_collector.token}}"
+data_collector.mode "{{cfg.data_collector.mode}}".to_sym
+data_collector.raise_on_failure {{cfg.data_collector.raise_on_failure}}
+minimal_ohai {{cfg.minimal_ohai}}
+{{#if cfg.chef-client.node_name ~}}
+node_name "{{cfg.node_name}}"
+{{/if ~}}
+splay {{cfg.splay}}
+interval {{cfg.interval}}
+log_location {{cfg.log_location}}
+log_level "{{cfg.log_level}}".to_sym
+{{#if cfg.use_member_id_as_uuid ~}}
+chef_guid "{{svc.me.member_id}}"
+{{/if ~}}
diff --git a/habitat/default.toml b/habitat/default.toml
new file mode 100644
index 0000000000..1422305835
--- /dev/null
+++ b/habitat/default.toml
@@ -0,0 +1,34 @@
+pid_file = "chef.pid"
+run_list = ""
+
+# The location in which nodes are stored when the chef-client is run in local mode
+
+log_location = "STDOUT"
+
+# The level of logging to be stored in a log file
+log_level = "info"
+
+# A random number between zero and splay that is added to interval
+splay = 10
+
+# The frequency (in seconds) at which the chef-client runs
+interval = 10
+
+minimal_ohai = false
+
+# The name of the node. Determines which configuration should be applied and sets the client_name,
+# which is the name used when authenticating to a Chef server. The default value is the FQDN of the chef-client,
+#as detected by Ohai. In general, Chef recommends that you leave this setting blank and let Ohai
+# assign the FQDN of the node as the node_name during each chef-clientrun.
+node_name = ""
+
+#The name of the environment
+environment = "_default"
+
+use_member_id_as_uuid = false
+
+[data_collector]
+url="http://localhost/data-collector/v0/"
+token="93a49a4f2482c64126f7b6015e6b0f30284287ee4054ff8807fb63d9cbd1c506"
+raise_on_failure=false
+mode="both"
diff --git a/habitat/hooks/init b/habitat/hooks/init
new file mode 100644
index 0000000000..a23344b2a9
--- /dev/null
+++ b/habitat/hooks/init
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+exec 2>&1
+
+mkdir -p "{{pkg.svc_data_path}}/chef"
diff --git a/habitat/hooks/run b/habitat/hooks/run
new file mode 100644
index 0000000000..75dd1958fa
--- /dev/null
+++ b/habitat/hooks/run
@@ -0,0 +1,6 @@
+#!/bin/sh
+exec 2>&1
+
+export SSL_CERT_FILE="{{pkgPathFor "core/cacerts"}}/ssl/cert.pem"
+
+exec chef-solo --fork -c {{pkg.svc_config_path}}/client.rb
diff --git a/habitat/plan.sh b/habitat/plan.sh
new file mode 100644
index 0000000000..26fcd5dad6
--- /dev/null
+++ b/habitat/plan.sh
@@ -0,0 +1,111 @@
+pkg_name=chef-client
+pkg_origin=chef
+pkg_maintainer="The Chef Maintainers <humans@chef.io>"
+pkg_description="The Chef Client"
+pkg_version=$(cat ../VERSION)
+pkg_license=('Apache-2.0')
+pkg_filename=${pkg_dirname}.tar.gz
+pkg_bin_dirs=(bin)
+pkg_build_deps=(core/make core/gcc core/git)
+pkg_deps=(core/glibc core/ruby core/libxml2 core/libxslt core/libiconv core/xz core/zlib core/bundler core/openssl core/cacerts core/libffi core/coreutils core/libarchive)
+pkg_svc_user=root
+
+do_before() {
+ do_default_before
+ update_pkg_version
+}
+
+do_download() {
+ build_line "Fake download! Creating archive of latest repository commit."
+ # source is in this repo, so we're going to create an archive from the
+ # appropriate path within the repo and place the generated tarball in the
+ # location expected by do_unpack
+ cd $PLAN_CONTEXT/../
+ git archive --prefix=${pkg_name}-${pkg_version}/ --output=$HAB_CACHE_SRC_PATH/${pkg_filename} HEAD
+}
+
+do_verify() {
+ build_line "Skipping checksum verification on the archive we just created."
+ return 0
+}
+
+do_prepare() {
+ export OPENSSL_LIB_DIR=$(pkg_path_for openssl)/lib
+ export OPENSSL_INCLUDE_DIR=$(pkg_path_for openssl)/include
+ export SSL_CERT_FILE=$(pkg_path_for cacerts)/ssl/cert.pem
+
+ build_line "Setting link for /usr/bin/env to 'coreutils'"
+ [[ ! -f /usr/bin/env ]] && ln -s $(pkg_path_for coreutils)/bin/env /usr/bin/env
+
+ return 0
+}
+
+do_build() {
+ export CPPFLAGS="${CPPFLAGS} ${CFLAGS}"
+
+ local _bundler_dir=$(pkg_path_for bundler)
+ local _libxml2_dir=$(pkg_path_for libxml2)
+ local _libxslt_dir=$(pkg_path_for libxslt)
+ local _zlib_dir=$(pkg_path_for zlib)
+
+ export GEM_HOME=${pkg_prefix}/bundle
+ export GEM_PATH=${_bundler_dir}:${GEM_HOME}
+
+ export NOKOGIRI_CONFIG="--use-system-libraries --with-zlib-dir=${_zlib_dir} --with-xslt-dir=${_libxslt_dir} --with-xml2-include=${_libxml2_dir}/include/libxml2 --with-xml2-lib=${_libxml2_dir}/lib"
+ bundle config --local build.nokogiri '${NOKOGIRI_CONFIG}'
+
+ bundle config --local silence_root_warning 1
+
+ pushd chef-config > /dev/null
+ _bundle_install "${pkg_prefix}/bundle"
+ popd > /dev/null
+
+ _bundle_install "${pkg_prefix}/bundle"
+}
+
+do_install() {
+ mkdir -p "${pkg_prefix}/chef"
+ for dir in bin chef-config lib chef.gemspec Gemfile Gemfile.lock; do
+ cp -rv "${PLAN_CONTEXT}/../${dir}" "${pkg_prefix}/chef/"
+ done
+
+ # This is just generating binstubs with the correct path.
+ # If we generated them on install, bundler thinks our source is in $HAB_CACHE_SOURCE_PATH
+ pushd "$pkg_prefix/chef" > /dev/null
+ _bundle_install \
+ "${pkg_prefix}/bundle" \
+ --local \
+ --quiet \
+ --binstubs "${pkg_prefix}/bin"
+ popd > /dev/null
+
+ fix_interpreter "${pkg_prefix}/bin/*" core/coreutils bin/env
+ fix_interpreter "${pkg_prefix}/bin/*" core/ruby bin/ruby
+}
+
+do_end() {
+ if [[ `readlink /usr/bin/env` = "$(pkg_path_for coreutils)/bin/env" ]]; then
+ build_line "Removing the symlink we created for '/usr/bin/env'"
+ rm /usr/bin/env
+ fi
+}
+
+do_strip() {
+ return 0
+}
+
+# Helper function to wrap up some repetitive bundle install flags
+_bundle_install() {
+ local path
+ path="$1"
+ shift
+
+ bundle install ${*:-} \
+ --jobs "$(nproc)" \
+ --without development:test \
+ --path "$path" \
+ --shebang="$(pkg_path_for "core/ruby")/bin/ruby" \
+ --no-clean \
+ --retry 5 \
+ --standalone
+}
diff --git a/kitchen-tests/.kitchen.travis.yml b/kitchen-tests/.kitchen.travis.yml
index 100891bdf5..7f1cc94667 100644
--- a/kitchen-tests/.kitchen.travis.yml
+++ b/kitchen-tests/.kitchen.travis.yml
@@ -1,42 +1,131 @@
---
driver:
- name: ec2
- aws_ssh_key_id: <%= ENV['AWS_KEYPAIR_NAME'] %>
- region: "us-west-2"
- availability_zone: "us-west-2a"
- security_group_ids: ["travis-ci"]
- instance_type: "m3.medium"
+ name: dokken
+ privileged: true
+ chef_image: chef/chef
+ chef_version: current
+
+transport:
+ name: dokken
provisioner:
name: chef_github
- chef_omnibus_url: "https://omnitruck.chef.io/current/install.sh"
- chef_omnibus_install_options: "-n"
+ root_path: /opt/kitchen
+ require_chef_omnibus: latest
+ chef_omnibus_url: "https://omnitruck.chef.io/install.sh"
+ chef_omnibus_install_options: "-c current"
github_owner: "chef"
github_repo: "chef"
refname: <%= ENV['TRAVIS_COMMIT'] %>
+ ohai_refname: "master"
github_access_token: <%= ENV['KITCHEN_GITHUB_TOKEN'] %>
data_path: test/fixtures
# disable file provider diffs so we don't overflow travis' line limit
client_rb:
diff_disabled: true
-transport:
- ssh_key: <%= ENV['EC2_SSH_KEY_PATH'] %>
+verifier:
+ name: inspec
+ format: progress
platforms:
- - name: ubuntu-12.04
- driver:
- # http://cloud-images.ubuntu.com/locator/ec2/
- # 12.04 amd64 us-west-2 hvm:ssd
- image_id: ami-f3635fc3
- - name: rhel-6
- driver:
- # https://github.com/chef/releng-chef-repo/blob/master/script/ci#L93-L96
- image_id: ami-7df0bd4d
+- name: amazonlinux
+ driver:
+ image: dokken/amazonlinux
+ pid_one_command: /sbin/init
+ intermediate_instructions:
+ # TODO: inspec 2.0 requires gcc-c++, adding here until it can be added to appbundler-updater
+ - RUN yum -y install sudo gcc-c++
+ - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
+
+- name: amazonlinux-2
+ driver:
+ image: dokken/amazonlinux-2
+ pid_one_command: /usr/lib/systemd/systemd
+ intermediate_instructions:
+ - RUN yum -y install sudo gcc-c++
+ - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
+
+- name: debian-8
+ driver:
+ image: dokken/debian-8
+ pid_one_command: /bin/systemd
+ intermediate_instructions:
+ - RUN /usr/bin/apt-get update
+ - RUN /usr/bin/apt-get -y install sudo
+
+- name: debian-9
+ driver:
+ image: dokken/debian-9
+ pid_one_command: /bin/systemd
+ intermediate_instructions:
+ - RUN /usr/bin/apt-get update
+ - RUN /usr/bin/apt-get -y install sudo
+
+- name: centos-6
+ driver:
+ image: dokken/centos-6
+ pid_one_command: /sbin/init
+ intermediate_instructions:
+ - RUN yum -y install sudo gcc-c++
+ - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
+
+- name: centos-7
+ driver:
+ image: dokken/centos-7
+ pid_one_command: /usr/lib/systemd/systemd
+ intermediate_instructions:
+ - RUN yum -y install sudo gcc-c++
+ - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
+
+- name: fedora-latest
+ driver:
+ image: dokken/fedora-latest
+ pid_one_command: /usr/lib/systemd/systemd
+ intermediate_instructions:
+ - RUN dnf -y install sudo gcc-c++
+ - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
+
+- name: ubuntu-14.04
+ driver:
+ image: dokken/ubuntu-14.04
+ pid_one_command: /sbin/init
+ intermediate_instructions:
+ - RUN /usr/bin/apt-get update
+ - RUN /usr/bin/apt-get -y install sudo
+
+- name: ubuntu-16.04
+ driver:
+ image: dokken/ubuntu-16.04
+ pid_one_command: /bin/systemd
+ intermediate_instructions:
+ - RUN /usr/bin/apt-get update
+ - RUN /usr/bin/apt-get -y install sudo
+
+- name: ubuntu-18.04
+ driver:
+ image: dokken/ubuntu-18.04
+ pid_one_command: /bin/systemd
+ intermediate_instructions:
+ - RUN /usr/bin/apt-get update
+ - RUN /usr/bin/apt-get -y install sudo
+
+- name: opensuse-leap
+ driver:
+ image: dokken/opensuse-leap
+ pid_one_command: /bin/systemd
+ intermediate_instructions:
+ - RUN /usr/bin/zypper install -y sudo gcc-c++
suites:
- - name: webapp
+ - name: base
+ run_list:
+ - recipe[base::default]
+ - name: awesome_customers_ubuntu
+ includes: [ubuntu-14.04, ubuntu-16.04, ubuntu-18.04]
+ run_list:
+ - recipe[awesome_customers_ubuntu_wrapper::default]
+ - name: awesome_customers_rhel
+ includes: [centos-6, centos-7]
run_list:
- - recipe[apt::default]
- - recipe[webapp::default]
- attributes:
+ - recipe[awesome_customers_rhel_wrapper::default]
diff --git a/kitchen-tests/.kitchen.yml b/kitchen-tests/.kitchen.yml
index c853f51b8d..b449265ce7 100644
--- a/kitchen-tests/.kitchen.yml
+++ b/kitchen-tests/.kitchen.yml
@@ -5,34 +5,42 @@ driver:
cpus: 4
memory: 2048
+verifier:
+ name: inspec
+ format: progress
+
provisioner:
name: chef_github
+ chef_omnibus_url: "https://omnitruck.chef.io/install.sh"
+ chef_omnibus_install_options: "-c current"
github_owner: "chef"
github_repo: "chef"
+ ohai_refname: "master"
refname: <%= %x(git rev-parse HEAD) %>
- data_path: test/fixtures
client_rb:
diff_disabled: true
platforms:
- # upstream community mysql cookbook broken on 10.04
- #- name: ubuntu-10.04
- # run_list: apt::default
- - name: ubuntu-12.04
- run_list: apt::default
+ - name: amazonlinux
+ driver_config:
+ box: mvbcoding/awslinux
+ - name: centos-6
+ - name: centos-7
+ - name: debian-8
+ - name: debian-9
+ - name: opensuse-leap-42
- name: ubuntu-14.04
- run_list: apt::default
- # upstream community mysql cookbook also broken on 14.10
- #- name: ubuntu-14.10
- # run_list: apt::default
- - name: centos-6.4
- run_list: yum-epel::default
- - name: centos-5.10
- run_list: yum-epel::default
+ - name: ubuntu-16.04
suites:
- - name: webapp
+ - name: base
+ run_list:
+ - recipe[base::default]
+ - name: awesome_customers_ubuntu
+ includes: [ubuntu-14.04, ubuntu-16.04]
+ run_list:
+ - recipe[awesome_customers_ubuntu_wrapper::default]
+ - name: awesome_customers_rhel
+ includes: [centos-6, centos-7]
run_list:
- - recipe[apt::default]
- - recipe[webapp::default]
- attributes:
+ - recipe[awesome_customers_rhel_wrapper::default]
diff --git a/kitchen-tests/Berksfile b/kitchen-tests/Berksfile
index decb85a8a1..22f838d83e 100644
--- a/kitchen-tests/Berksfile
+++ b/kitchen-tests/Berksfile
@@ -1,5 +1,9 @@
-source "https://supermarket.getchef.com"
+source "https://supermarket.chef.io"
-cookbook "webapp", :path => "cookbooks/webapp"
+cookbook "base", path: "cookbooks/base"
-cookbook "php", "~> 1.5.0"
+# Disabled pending updating these test cases for Chef 13.
+# cookbook "awesome_customers_ubuntu_wrapper", path: "cookbooks/awesome_customers_ubuntu_wrapper"
+# cookbook "awesome_customers_ubuntu", github: "lamont-granquist/awesome_customers_ubuntu", branch: "lcg/bump-mysql-version"
+# cookbook "awesome_customers_rhel_wrapper", path: "cookbooks/awesome_customers_rhel_wrapper"
+# cookbook "awesome_customers_rhel", github: "lamont-granquist/awesome_customers_rhel", branch: "lcg/bump-mysql-version"
diff --git a/kitchen-tests/Berksfile.lock b/kitchen-tests/Berksfile.lock
deleted file mode 100644
index ee5027e4b7..0000000000
--- a/kitchen-tests/Berksfile.lock
+++ /dev/null
@@ -1,59 +0,0 @@
-DEPENDENCIES
- php (~> 1.5.0)
- webapp
- path: cookbooks/webapp
-
-GRAPH
- apache2 (3.2.2)
- apt (3.0.0)
- aws (3.3.2)
- ohai (>= 2.1.0)
- build-essential (3.2.0)
- seven_zip (>= 0.0.0)
- chef-sugar (3.3.0)
- chef_handler (1.3.0)
- database (2.3.1)
- aws (>= 0.0.0)
- mysql (~> 5.0)
- mysql-chef_gem (~> 0.0)
- postgresql (>= 1.0.0)
- xfs (>= 0.0.0)
- iis (4.1.7)
- windows (>= 1.34.6)
- mysql (5.6.3)
- yum-mysql-community (>= 0.0.0)
- mysql-chef_gem (0.0.5)
- build-essential (>= 0.0.0)
- mysql (>= 0.0.0)
- ohai (3.0.1)
- openssl (4.4.0)
- chef-sugar (>= 3.1.1)
- php (1.5.0)
- build-essential (>= 0.0.0)
- iis (>= 0.0.0)
- mysql (>= 0.0.0)
- windows (>= 0.0.0)
- xml (>= 0.0.0)
- yum-epel (>= 0.0.0)
- postgresql (4.0.6)
- apt (>= 1.9.0)
- build-essential (>= 0.0.0)
- openssl (~> 4.0)
- seven_zip (2.0.0)
- windows (>= 1.2.2)
- webapp (0.1.0)
- apache2 (>= 0.0.0)
- database (~> 2.3.1)
- mysql (>= 0.0.0)
- php (>= 0.0.0)
- windows (1.39.2)
- chef_handler (>= 0.0.0)
- xfs (2.0.1)
- xml (2.0.0)
- build-essential (>= 0.0.0)
- chef-sugar (>= 0.0.0)
- yum (3.10.0)
- yum-epel (0.6.6)
- yum (~> 3.10.0)
- yum-mysql-community (0.2.0)
- yum (>= 3.2)
diff --git a/kitchen-tests/Gemfile b/kitchen-tests/Gemfile
index acc62156ae..9cc63fb7d6 100644
--- a/kitchen-tests/Gemfile
+++ b/kitchen-tests/Gemfile
@@ -1,10 +1,12 @@
source "https://rubygems.org"
-group :end_to_end do
- gem "berkshelf"
- gem "test-kitchen", "~> 1.4"
- gem "kitchen-appbundle-updater"
- gem "kitchen-vagrant", "~> 0.17"
- gem "kitchen-ec2", github: "test-kitchen/kitchen-ec2"
- gem "vagrant-wrapper"
-end
+gem "rake" # required to build some native extensions
+gem "chef", path: ".."
+# remove this ohai pin once we ship the 14 gem
+gem "ohai", git: "https://github.com/chef/ohai.git", branch: "master"
+gem "berkshelf", git: "https://github.com/berkshelf/berkshelf.git", branch: "master"
+gem "kitchen-appbundle-updater", git: "https://github.com/chef/kitchen-appbundle-updater", branch: "master"
+gem "kitchen-dokken", "< 2.0" # 2.x fails atm: https://travis-ci.org/chef/chef/jobs/199125787
+gem "kitchen-inspec", git: "https://github.com/chef/kitchen-inspec.git", branch: "master"
+gem "kitchen-vagrant", git: "https://github.com/test-kitchen/kitchen-vagrant.git", branch: "master"
+gem "test-kitchen", git: "https://github.com/test-kitchen/test-kitchen.git", branch: "master"
diff --git a/kitchen-tests/Gemfile.lock b/kitchen-tests/Gemfile.lock
deleted file mode 100644
index 78caba0014..0000000000
--- a/kitchen-tests/Gemfile.lock
+++ /dev/null
@@ -1,173 +0,0 @@
-GIT
- remote: git://github.com/test-kitchen/kitchen-ec2.git
- revision: fec3f199a646980dc289ac6db9f90e9a9e4b0f6b
- specs:
- kitchen-ec2 (1.0.0)
- aws-sdk (~> 2)
- excon
- multi_json
- retryable (~> 2.0)
- test-kitchen (~> 1.4, >= 1.4.1)
-
-GEM
- remote: https://rubygems.org/
- specs:
- addressable (2.4.0)
- artifactory (2.3.2)
- aws-sdk (2.2.34)
- aws-sdk-resources (= 2.2.34)
- aws-sdk-core (2.2.34)
- jmespath (~> 1.0)
- aws-sdk-resources (2.2.34)
- aws-sdk-core (= 2.2.34)
- berkshelf (4.3.2)
- addressable (~> 2.3, >= 2.3.4)
- berkshelf-api-client (~> 2.0, >= 2.0.2)
- buff-config (~> 1.0)
- buff-extensions (~> 1.0)
- buff-shell_out (~> 0.1)
- celluloid (= 0.16.0)
- celluloid-io (~> 0.16.1)
- cleanroom (~> 1.0)
- faraday (~> 0.9)
- httpclient (~> 2.7)
- minitar (~> 0.5, >= 0.5.4)
- octokit (~> 4.0)
- retryable (~> 2.0)
- ridley (~> 4.5)
- solve (~> 2.0)
- thor (~> 0.19)
- berkshelf-api-client (2.0.2)
- faraday (~> 0.9.1)
- httpclient (~> 2.7.0)
- ridley (~> 4.5)
- buff-config (1.0.1)
- buff-extensions (~> 1.0)
- varia_model (~> 0.4)
- buff-extensions (1.0.0)
- buff-ignore (1.1.1)
- buff-ruby_engine (0.1.0)
- buff-shell_out (0.2.0)
- buff-ruby_engine (~> 0.1.0)
- celluloid (0.16.0)
- timers (~> 4.0.0)
- celluloid-io (0.16.2)
- celluloid (>= 0.16.0)
- nio4r (>= 1.1.0)
- chef-config (12.9.38)
- fuzzyurl (~> 0.8.0)
- mixlib-config (~> 2.0)
- mixlib-shellout (~> 2.0)
- cleanroom (1.0.0)
- diff-lcs (1.2.5)
- erubis (2.7.0)
- excon (0.49.0)
- faraday (0.9.2)
- multipart-post (>= 1.2, < 3)
- ffi (1.9.10-x86-mingw32)
- fuzzyurl (0.8.0)
- hashie (3.4.3)
- hitimes (1.2.3)
- hitimes (1.2.3-x86-mingw32)
- httpclient (2.7.1)
- jmespath (1.2.4)
- json_pure (>= 1.8.1)
- json (1.8.3)
- json_pure (1.8.3)
- kitchen-appbundle-updater (0.1.2)
- kitchen-vagrant (0.20.0)
- test-kitchen (~> 1.4)
- minitar (0.5.4)
- mixlib-authentication (1.4.0)
- mixlib-log
- rspec-core (~> 3.2)
- rspec-expectations (~> 3.2)
- rspec-mocks (~> 3.2)
- mixlib-config (2.2.1)
- mixlib-install (1.0.7)
- artifactory
- mixlib-shellout
- mixlib-versioning
- mixlib-log (1.6.0)
- mixlib-shellout (2.2.6)
- mixlib-shellout (2.2.6-universal-mingw32)
- win32-process (~> 0.8.2)
- wmi-lite (~> 1.0)
- mixlib-versioning (1.1.0)
- molinillo (0.4.4)
- multi_json (1.11.2)
- multipart-post (2.0.0)
- net-scp (1.2.1)
- net-ssh (>= 2.6.5)
- net-ssh (3.1.1)
- nio4r (1.2.1)
- octokit (4.3.0)
- sawyer (~> 0.7.0, >= 0.5.3)
- retryable (2.0.3)
- ridley (4.5.0)
- addressable
- buff-config (~> 1.0)
- buff-extensions (~> 1.0)
- buff-ignore (~> 1.1)
- buff-shell_out (~> 0.1)
- celluloid (~> 0.16.0)
- celluloid-io (~> 0.16.1)
- chef-config (>= 12.5.0)
- erubis
- faraday (~> 0.9.0)
- hashie (>= 2.0.2, < 4.0.0)
- httpclient (~> 2.7)
- json (>= 1.7.7)
- mixlib-authentication (>= 1.3.0)
- retryable (~> 2.0)
- semverse (~> 1.1)
- varia_model (~> 0.4.0)
- rspec-core (3.4.4)
- rspec-support (~> 3.4.0)
- rspec-expectations (3.4.0)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.4.0)
- rspec-mocks (3.4.1)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.4.0)
- rspec-support (3.4.1)
- safe_yaml (1.0.4)
- sawyer (0.7.0)
- addressable (>= 2.3.5, < 2.5)
- faraday (~> 0.8, < 0.10)
- semverse (1.2.1)
- solve (2.0.3)
- molinillo (~> 0.4.2)
- semverse (~> 1.1)
- test-kitchen (1.7.3)
- mixlib-install (~> 1.0, >= 1.0.4)
- mixlib-shellout (>= 1.2, < 3.0)
- net-scp (~> 1.1)
- net-ssh (>= 2.9, < 4.0)
- safe_yaml (~> 1.0)
- thor (~> 0.18)
- thor (0.19.1)
- timers (4.0.4)
- hitimes
- vagrant-wrapper (2.0.3)
- varia_model (0.4.1)
- buff-extensions (~> 1.0)
- hashie (>= 2.0.2, < 4.0.0)
- win32-process (0.8.3)
- ffi (>= 1.0.0)
- wmi-lite (1.0.0)
-
-PLATFORMS
- ruby
- x86-mingw32
-
-DEPENDENCIES
- berkshelf
- kitchen-appbundle-updater
- kitchen-ec2!
- kitchen-vagrant (~> 0.17)
- test-kitchen (~> 1.4)
- vagrant-wrapper
-
-BUNDLED WITH
- 1.11.2
diff --git a/kitchen-tests/cookbooks/audit_test/Berksfile b/kitchen-tests/cookbooks/audit_test/Berksfile
index 0ac9b78cf7..967b9a78b6 100644
--- a/kitchen-tests/cookbooks/audit_test/Berksfile
+++ b/kitchen-tests/cookbooks/audit_test/Berksfile
@@ -1,3 +1,3 @@
-source "https://supermarket.getchef.com"
+source "https://supermarket.chef.io"
metadata
diff --git a/kitchen-tests/cookbooks/audit_test/chefignore b/kitchen-tests/cookbooks/audit_test/chefignore
deleted file mode 100644
index 80dc2d20ef..0000000000
--- a/kitchen-tests/cookbooks/audit_test/chefignore
+++ /dev/null
@@ -1,95 +0,0 @@
-# Put files/directories that should be ignored in this file when uploading
-# or sharing to the community site.
-# Lines that start with '# ' are comments.
-
-# OS generated files #
-######################
-.DS_Store
-Icon?
-nohup.out
-ehthumbs.db
-Thumbs.db
-
-# SASS #
-########
-.sass-cache
-
-# EDITORS #
-###########
-\#*
-.#*
-*~
-*.sw[a-z]
-*.bak
-REVISION
-TAGS*
-tmtags
-*_flymake.*
-*_flymake
-*.tmproj
-.project
-.settings
-mkmf.log
-
-## COMPILED ##
-##############
-a.out
-*.o
-*.pyc
-*.so
-*.com
-*.class
-*.dll
-*.exe
-*/rdoc/
-
-# Testing #
-###########
-.watchr
-.rspec
-spec/*
-spec/fixtures/*
-test/*
-features/*
-Guardfile
-Procfile
-
-# SCM #
-#######
-.git
-*/.git
-.gitignore
-.gitmodules
-.gitconfig
-.gitattributes
-.svn
-*/.bzr/*
-*/.hg/*
-*/.svn/*
-
-# Berkshelf #
-#############
-Berksfile
-Berksfile.lock
-cookbooks/*
-tmp
-
-# Cookbooks #
-#############
-CONTRIBUTING
-
-# Strainer #
-############
-Colanderfile
-Strainerfile
-.colander
-.strainer
-
-# Vagrant #
-###########
-.vagrant
-Vagrantfile
-
-# Travis #
-##########
-.travis.yml
diff --git a/kitchen-tests/cookbooks/audit_test/metadata.rb b/kitchen-tests/cookbooks/audit_test/metadata.rb
index 3fbda5dbe1..2b34ce28d0 100644
--- a/kitchen-tests/cookbooks/audit_test/metadata.rb
+++ b/kitchen-tests/cookbooks/audit_test/metadata.rb
@@ -1,7 +1,7 @@
name "audit_test"
maintainer "The Authors"
maintainer_email "you@example.com"
-license "all_rights"
+license "Apache-2.0"
description "Installs/Configures audit_test"
long_description "Installs/Configures audit_test"
version "0.1.0"
diff --git a/kitchen-tests/cookbooks/awesome_customers_rhel_wrapper/metadata.rb b/kitchen-tests/cookbooks/awesome_customers_rhel_wrapper/metadata.rb
new file mode 100644
index 0000000000..49dbb50869
--- /dev/null
+++ b/kitchen-tests/cookbooks/awesome_customers_rhel_wrapper/metadata.rb
@@ -0,0 +1,9 @@
+name "awesome_customers_rhel_wrapper"
+maintainer "The Authors"
+maintainer_email "you@example.com"
+license "Apache-2.0"
+description "Installs/Configures awesome_customers_rhel"
+long_description "Installs/Configures awesome_customers_rhel"
+version "0.1.0"
+
+depends "awesome_customers_rhel"
diff --git a/kitchen-tests/cookbooks/awesome_customers_rhel_wrapper/recipes/default.rb b/kitchen-tests/cookbooks/awesome_customers_rhel_wrapper/recipes/default.rb
new file mode 100644
index 0000000000..c58db5fe29
--- /dev/null
+++ b/kitchen-tests/cookbooks/awesome_customers_rhel_wrapper/recipes/default.rb
@@ -0,0 +1,8 @@
+#
+# Cookbook:: awesome_customers_rhel
+# Recipe:: default
+#
+# Copyright:: 2014-2017, Chef Software, Inc.
+#
+
+include_recipe "awesome_customers_rhel::default"
diff --git a/kitchen-tests/cookbooks/awesome_customers_ubuntu_wrapper/metadata.rb b/kitchen-tests/cookbooks/awesome_customers_ubuntu_wrapper/metadata.rb
new file mode 100644
index 0000000000..d07a2c2c13
--- /dev/null
+++ b/kitchen-tests/cookbooks/awesome_customers_ubuntu_wrapper/metadata.rb
@@ -0,0 +1,9 @@
+name "awesome_customers_ubuntu_wrapper"
+maintainer "The Authors"
+maintainer_email "you@example.com"
+license "Apache-2.0"
+description "Installs/Configures awesome_customers_ubuntu"
+long_description "Installs/Configures awesome_customers_ubuntu"
+version "0.1.0"
+
+depends "awesome_customers_ubuntu"
diff --git a/kitchen-tests/cookbooks/awesome_customers_ubuntu_wrapper/recipes/default.rb b/kitchen-tests/cookbooks/awesome_customers_ubuntu_wrapper/recipes/default.rb
new file mode 100644
index 0000000000..f6fd388f16
--- /dev/null
+++ b/kitchen-tests/cookbooks/awesome_customers_ubuntu_wrapper/recipes/default.rb
@@ -0,0 +1,8 @@
+#
+# Cookbook:: awesome_customers_ubuntu
+# Recipe:: default
+#
+# Copyright:: 2016-2017, Chef Software, Inc.
+#
+
+include_recipe "awesome_customers_ubuntu::default"
diff --git a/kitchen-tests/cookbooks/base/Berksfile b/kitchen-tests/cookbooks/base/Berksfile
new file mode 100644
index 0000000000..967b9a78b6
--- /dev/null
+++ b/kitchen-tests/cookbooks/base/Berksfile
@@ -0,0 +1,3 @@
+source "https://supermarket.chef.io"
+
+metadata
diff --git a/kitchen-tests/cookbooks/base/README.md b/kitchen-tests/cookbooks/base/README.md
new file mode 100644
index 0000000000..2889a2d0dc
--- /dev/null
+++ b/kitchen-tests/cookbooks/base/README.md
@@ -0,0 +1,3 @@
+# base
+
+A standard chef "base" cookbook that performs various base system configuration tasks using common community cookbooks.
diff --git a/kitchen-tests/cookbooks/base/attributes/default.rb b/kitchen-tests/cookbooks/base/attributes/default.rb
new file mode 100644
index 0000000000..5e8cee6736
--- /dev/null
+++ b/kitchen-tests/cookbooks/base/attributes/default.rb
@@ -0,0 +1,76 @@
+puts "CHEF SUGAR THINKS WE ARE ON UBUNTU" if ubuntu?
+puts "CHEF SUGAR THINKS WE ARE ON RHEL" if rhel?
+
+#
+# ubuntu cookbook overrides
+#
+
+default["ubuntu"]["include_source_packages"] = true
+default["ubuntu"]["components"] = "main restricted universe multiverse"
+
+#
+# openssh cookbook overrides
+#
+
+# turn off old protocols client-side
+default["openssh"]["client"]["host_based_authentication"] = "no"
+# allow typical ssh v2 rsa/dsa/ecdsa key auth client-side
+default["openssh"]["client"]["pubkey_authentication"] = "yes"
+# allow password auth client-side (we can ssh 'to' hosts that require passwords)
+default["openssh"]["client"]["password_authentication"] = "yes"
+# turn off kerberos client-side
+default["openssh"]["client"]["gssapi_authentication"] = "no"
+default["openssh"]["client"]["check_host_ip"] = "no"
+# everone turns strict host key checking off anyway
+default["openssh"]["client"]["strict_host_key_checking"] = "no"
+# force protocol 2
+default["openssh"]["client"]["protocol"] = "2"
+
+# it is mostly important that the aes*-ctr ciphers appear first in this list, the cbc ciphers are for compatibility
+default["openssh"]["server"]["ciphers"] = "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-cbc"
+# DNS causes long timeouts when connecting clients have busted DNS
+default["openssh"]["server"]["use_dns"] = "no"
+default["openssh"]["server"]["syslog_facility"] = "AUTH"
+# only allow access via ssh pubkeys, all other mechanisms including passwords are turned off for all users
+default["openssh"]["server"]["pubkey_authentication"] = "yes"
+default["openssh"]["server"]["password_authentication"] = "no"
+default["openssh"]["server"]["host_based_authentication"] = "no"
+default["openssh"]["server"]["gssapi_authentication"] = "no"
+default["openssh"]["server"]["permit_root_login"] = "without-password"
+default["openssh"]["server"]["ignore_rhosts"] = "yes"
+default["openssh"]["server"]["permit_empty_passwords"] = "no"
+default["openssh"]["server"]["challenge_response_authentication"] = "no"
+default["openssh"]["server"]["kerberos_authentication"] = "no"
+# tcp keepalives are useful to keep connections up through VPNs and firewalls
+default["openssh"]["server"]["tcp_keepalive"] = "yes"
+default["openssh"]["server"]["max_start_ups"] = "10"
+# PAM (i think) already prints the motd on login
+default["openssh"]["server"]["print_motd"] = "no"
+# force only protocol 2 connections
+default["openssh"]["server"]["protocol"] = "2"
+# allow tunnelling x-applications back to the client
+default["openssh"]["server"]["x11_forwarding"] = "yes"
+
+#
+# chef-client cookbook overrides
+#
+
+# always wait at least 30 mins (1800 secs) between daemonized chef-client runs
+default["chef_client"]["interval"] = 1800
+# wait an additional random interval of up to 30 mins (1800 secs) between daemonized runs
+default["chef_client"]["splay"] = 1800
+# only log what we change
+default["chef_client"]["config"]["verbose_logging"] = false
+
+#
+# resolver cookbook overrides
+#
+
+default["resolver"]["nameservers"] = [ "8.8.8.8", "8.8.4.4" ]
+default["resolver"]["search"] = "chef.io"
+
+#
+# nscd cookbook overrides
+#
+
+default["nscd"]["server_user"] = "nobody"
diff --git a/kitchen-tests/cookbooks/base/libraries/chef-sugar.rb b/kitchen-tests/cookbooks/base/libraries/chef-sugar.rb
new file mode 100644
index 0000000000..90d02a361f
--- /dev/null
+++ b/kitchen-tests/cookbooks/base/libraries/chef-sugar.rb
@@ -0,0 +1,4 @@
+require "chef/sugar"
+
+# hack until this gets baked into chef-sugar so we can use chef-sugar in attributes files
+Chef::Node.send(:include, Chef::Sugar::DSL)
diff --git a/kitchen-tests/cookbooks/base/metadata.rb b/kitchen-tests/cookbooks/base/metadata.rb
new file mode 100644
index 0000000000..f526318bb7
--- /dev/null
+++ b/kitchen-tests/cookbooks/base/metadata.rb
@@ -0,0 +1,33 @@
+name "base"
+maintainer ""
+maintainer_email ""
+license "Apache-2.0"
+description "Installs/Configures base"
+long_description "Installs/Configures base"
+version "0.1.0"
+
+gem "chef-sugar"
+
+depends "chef-client"
+depends "logrotate"
+depends "multipackage"
+depends "nscd"
+depends "ntp"
+depends "openssh"
+depends "resolver"
+depends "selinux"
+depends "ubuntu"
+depends "users"
+depends "cron"
+depends "git"
+
+supports "ubuntu"
+supports "debian"
+supports "redhat"
+supports "opensuse"
+supports "fedora"
+supports "amazon"
+
+chef_version ">= 14"
+issues_url "https://github.com/chef/chef/issues"
+source_url "https://github.com/chef/chef"
diff --git a/kitchen-tests/cookbooks/base/recipes/default.rb b/kitchen-tests/cookbooks/base/recipes/default.rb
new file mode 100644
index 0000000000..ea0215ca96
--- /dev/null
+++ b/kitchen-tests/cookbooks/base/recipes/default.rb
@@ -0,0 +1,87 @@
+#
+# Cookbook:: base
+# Recipe:: default
+#
+# Copyright:: 2014-2017, Chef Software, Inc.
+#
+
+hostname "chef-travis-ci.chef.io"
+
+apt_update
+
+include_recipe "ubuntu" if platform?("ubuntu")
+
+if platform_family?("rhel", "fedora", "amazon")
+ include_recipe "selinux::disabled"
+end
+
+yum_repository "epel" do
+ enabled true
+ description "Extra Packages for Enterprise Linux #{node['platform_version'].to_i} - $basearch"
+ failovermethod "priority"
+ gpgkey "https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-#{node['platform_version'].to_i}"
+ gpgcheck true
+ mirrorlist "https://mirrors.fedoraproject.org/metalink?repo=epel-#{node['platform_version'].to_i}&arch=$basearch"
+ only_if { platform_family?("rhel") }
+end
+
+build_essential
+
+include_recipe "::packages"
+
+include_recipe "ntp"
+
+include_recipe "resolver"
+
+users_manage "sysadmin" do
+ group_id 2300
+ action [:create]
+end
+
+sudo "sysadmins" do
+ group ["sysadmin", "%superadmin"]
+ nopasswd true
+end
+
+sudo "some_person" do
+ nopasswd true
+ user "some_person"
+ commands ["/opt/chef/bin/chef-client"]
+ env_keep_add %w{PATH RBENV_ROOT RBENV_VERSION}
+end
+
+include_recipe "chef-client::delete_validation"
+include_recipe "chef-client::config"
+include_recipe "chef-client"
+
+include_recipe "openssh"
+
+include_recipe "nscd"
+
+include_recipe "logrotate"
+
+include_recipe "cron"
+
+include_recipe "git"
+
+directory "/etc/ssl"
+
+# Generate new key and certificate
+openssl_dhparam "/etc/ssl/dhparam.pem" do
+ key_length 1024
+ action :create
+end
+
+# Generate new key with aes-128-cbc cipher
+openssl_rsa_private_key "/etc/ssl/rsakey_aes128cbc.pem" do
+ key_length 1024
+ key_cipher "aes-128-cbc"
+ action :create
+end
+
+openssl_rsa_public_key "/etc/ssl/rsakey_aes128cbc.pub" do
+ private_key_path "/etc/ssl/rsakey_aes128cbc.pem"
+ action :create
+end
+
+include_recipe "::tests"
diff --git a/kitchen-tests/cookbooks/base/recipes/packages.rb b/kitchen-tests/cookbooks/base/recipes/packages.rb
new file mode 100644
index 0000000000..f8ceb342ad
--- /dev/null
+++ b/kitchen-tests/cookbooks/base/recipes/packages.rb
@@ -0,0 +1,24 @@
+#
+# Cookbook:: base
+# Recipe:: packages
+#
+# Copyright:: 2014-2017, Chef Software, Inc.
+#
+
+# this is just a list of package that exist on every O/S we test, and often aren't installed by default. you don't
+# have to get too clever here, you can delete packages if they don't exist everywhere we test.
+pkgs = %w{lsof tcpdump strace zsh dmidecode ltrace bc curl wget subversion traceroute htop tmux }
+
+# this deliberately calls the multipackage API N times in order to do one package installation in order to exercise the
+# multipackage cookbook.
+pkgs.each do |pkg|
+ multipackage pkgs
+end
+
+gems = %w{fpm community_cookbook_releaser}
+
+gems.each do |gem|
+ chef_gem gem do
+ compile_time false
+ end
+end
diff --git a/kitchen-tests/cookbooks/base/recipes/tests.rb b/kitchen-tests/cookbooks/base/recipes/tests.rb
new file mode 100644
index 0000000000..c6095eaeb5
--- /dev/null
+++ b/kitchen-tests/cookbooks/base/recipes/tests.rb
@@ -0,0 +1,21 @@
+#
+# Cookbook:: base
+# Recipe:: tests
+#
+# Copyright:: 2014-2017, Chef Software, Inc.
+#
+
+#
+# this file is for random tests to check specific chef-client internal functionality
+#
+
+file "/tmp/chef-test-ümlauts" do
+ content "testing UTF-8 char in the filename"
+end
+
+# this caught a regression in 12.14.70 before it was released when i
+# ran it in lamont-ci, so added the test here so everyone else other than
+# me gets coverage for this as well.
+file "/tmp/chef-test-\xFDmlaut" do
+ content "testing illegal UTF-8 char in the filename"
+end
diff --git a/kitchen-tests/cookbooks/webapp/Berksfile b/kitchen-tests/cookbooks/webapp/Berksfile
deleted file mode 100644
index 4b6079016e..0000000000
--- a/kitchen-tests/cookbooks/webapp/Berksfile
+++ /dev/null
@@ -1,5 +0,0 @@
-source "https://api.berkshelf.com"
-
-metadata
-
-cookbook "apt"
diff --git a/kitchen-tests/cookbooks/webapp/README.md b/kitchen-tests/cookbooks/webapp/README.md
deleted file mode 100644
index f19ab46735..0000000000
--- a/kitchen-tests/cookbooks/webapp/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# webapp
-
-TODO: Enter the cookbook description here.
diff --git a/kitchen-tests/cookbooks/webapp/attributes/default.rb b/kitchen-tests/cookbooks/webapp/attributes/default.rb
deleted file mode 100644
index 2ff7a6c5ff..0000000000
--- a/kitchen-tests/cookbooks/webapp/attributes/default.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-default["apache"]["remote_host_ip"] = "127.0.0.1"
-
-default["webapp"]["database"] = "webapp"
-default["webapp"]["db_username"] = "webapp"
-default["webapp"]["path"] = "/srv/webapp"
-
-# XXX: apache2 cookbook 2.0.0 has bugs around changing the mpm and then attempting a graceful restart
-# which fails and leaves the service down.
-case node["platform"]
-when "ubuntu"
- if node["platform_version"].to_f >= 14.04
- default[:apache][:mpm] = "event"
- end
-end
diff --git a/kitchen-tests/cookbooks/webapp/metadata.rb b/kitchen-tests/cookbooks/webapp/metadata.rb
deleted file mode 100644
index f1f07d952b..0000000000
--- a/kitchen-tests/cookbooks/webapp/metadata.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-name "webapp"
-maintainer ""
-maintainer_email ""
-license ""
-description "Installs/Configures webapp"
-long_description "Installs/Configures webapp"
-version "0.1.0"
-
-depends "apache2"
-depends "database", "~> 2.3.1"
-depends "mysql"
-depends "php"
diff --git a/kitchen-tests/cookbooks/webapp/recipes/default.rb b/kitchen-tests/cookbooks/webapp/recipes/default.rb
deleted file mode 100644
index 2b3459b794..0000000000
--- a/kitchen-tests/cookbooks/webapp/recipes/default.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-#
-# Cookbook Name:: webapp
-# Recipe:: default
-#
-# Copyright (C) 2014
-#
-
-include_recipe "apache2"
-include_recipe "database::mysql"
-include_recipe "php"
-
-creds = Hash.new
-%w{mysql webapp}.each do |item_name|
- creds[item_name] = data_bag_item("passwords", item_name)
-end
-
-web_app "webapp" do
- server_name "localhost"
- server_aliases [node["fqdn"], node["hostname"], "localhost.localdomain"]
- docroot node["webapp"]["path"]
- cookbook "apache2"
-end
-
-mysql_service "default" do
- server_root_password creds["mysql"]["server_root_password"]
- server_repl_password creds["mysql"]["server_repl_password"]
-end
-
-mysql_database node["webapp"]["database"] do
- connection ({
- :host => "localhost",
- :username => "root",
- :password => creds["mysql"]["server_root_password"],
- })
- action :create
-end
-
-mysql_database_user node["webapp"]["db_username"] do
- connection ({
- :host => "localhost",
- :username => "root",
- :password => creds["mysql"]["server_root_password"],
- })
- password creds["webapp"]["db_password"]
- database_name node["webapp"]["database"]
- privileges [:select, :update, :insert, :create, :delete]
- action :grant
-end
-
-directory node["webapp"]["path"] do
- owner "root"
- group "root"
- mode "0755"
- action :create
- recursive true
-end
-
-template "#{node['webapp']['path']}/index.html" do
- source "index.html.erb"
-end
-
-template "#{node['webapp']['path']}/index.php" do
- source "index.php.erb"
-end
diff --git a/kitchen-tests/cookbooks/webapp/templates/default/index.html.erb b/kitchen-tests/cookbooks/webapp/templates/default/index.html.erb
deleted file mode 100644
index 6da0629b9e..0000000000
--- a/kitchen-tests/cookbooks/webapp/templates/default/index.html.erb
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
- <body>
- <h1>Hello, World!</h1>
- </body>
-</html>
diff --git a/kitchen-tests/cookbooks/webapp/templates/default/index.php.erb b/kitchen-tests/cookbooks/webapp/templates/default/index.php.erb
deleted file mode 100644
index b08b076614..0000000000
--- a/kitchen-tests/cookbooks/webapp/templates/default/index.php.erb
+++ /dev/null
@@ -1,8 +0,0 @@
-<html>
- <head>
- <title>PHP Test</title>
- </head>
- <body>
- <?php echo '<p>Hello, World!</p>'; ?>
- </body>
-</html>
diff --git a/kitchen-tests/data_bags/passwords/webapp.json b/kitchen-tests/data_bags/passwords/webapp.json
deleted file mode 100644
index 43c0ae1ced..0000000000
--- a/kitchen-tests/data_bags/passwords/webapp.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "id": "webapp",
- "db_password": "supersecretdbpassword"
-}
diff --git a/kitchen-tests/data_bags/users/adam.json b/kitchen-tests/data_bags/users/adam.json
new file mode 100644
index 0000000000..f96d7c213f
--- /dev/null
+++ b/kitchen-tests/data_bags/users/adam.json
@@ -0,0 +1,9 @@
+{
+ "id": "adam",
+ "uid": 666, // yes? i figure adam likes metal, shout out to iron maiden...
+ "gid": 666,
+ "shell": "/bin/zsh",
+ "groups": [ "sysadmin" ],
+ "comment": "Adam Jacob",
+ "password": "*"
+}
diff --git a/kitchen-tests/test/fixtures/platforms/centos/5.json b/kitchen-tests/test/fixtures/platforms/centos/5.json
deleted file mode 100644
index 9d324a2f03..0000000000
--- a/kitchen-tests/test/fixtures/platforms/centos/5.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "apache": {
- "package": "httpd",
- "service_name": "httpd"
- },
- "mysql": {
- "server_package": "mysql-server",
- "client_package": "mysql",
- "service_name": "mysqld"
- },
- "php" : {
- "package": "php53"
- }
-}
diff --git a/kitchen-tests/test/fixtures/platforms/centos/6.json b/kitchen-tests/test/fixtures/platforms/centos/6.json
deleted file mode 100644
index 4f74a3ed4a..0000000000
--- a/kitchen-tests/test/fixtures/platforms/centos/6.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "apache": {
- "package": "httpd",
- "service_name": "httpd"
- },
- "mysql": {
- "server_package": "mysql-server",
- "client_package": "mysql",
- "service_name": "mysqld"
- },
- "php" : {
- "package": "php"
- }
-}
diff --git a/kitchen-tests/test/fixtures/platforms/ubuntu/10.04.json b/kitchen-tests/test/fixtures/platforms/ubuntu/10.04.json
deleted file mode 100644
index a9677c7ca5..0000000000
--- a/kitchen-tests/test/fixtures/platforms/ubuntu/10.04.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "apache": {
- "package": "apache2",
- "service_name": "apache2"
- },
- "mysql": {
- "server_package": "mysql-server-5.1",
- "client_package": "mysql-client-5.1",
- "service_name": "mysql"
- },
- "php" : {
- "package": "php5"
- }
-}
diff --git a/kitchen-tests/test/fixtures/platforms/ubuntu/12.04.json b/kitchen-tests/test/fixtures/platforms/ubuntu/12.04.json
deleted file mode 100644
index eab46db2e5..0000000000
--- a/kitchen-tests/test/fixtures/platforms/ubuntu/12.04.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "apache": {
- "package": "apache2",
- "service_name": "apache2"
- },
- "mysql": {
- "server_package": "mysql-server-5.5",
- "client_package": "mysql-client-5.5",
- "service_name": "mysql"
- },
- "php" : {
- "package": "php5"
- }
-}
diff --git a/kitchen-tests/test/fixtures/platforms/ubuntu/14.04.json b/kitchen-tests/test/fixtures/platforms/ubuntu/14.04.json
deleted file mode 100644
index eab46db2e5..0000000000
--- a/kitchen-tests/test/fixtures/platforms/ubuntu/14.04.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "apache": {
- "package": "apache2",
- "service_name": "apache2"
- },
- "mysql": {
- "server_package": "mysql-server-5.5",
- "client_package": "mysql-client-5.5",
- "service_name": "mysql"
- },
- "php" : {
- "package": "php5"
- }
-}
diff --git a/kitchen-tests/test/fixtures/platforms/ubuntu/14.10.json b/kitchen-tests/test/fixtures/platforms/ubuntu/14.10.json
deleted file mode 100644
index eab46db2e5..0000000000
--- a/kitchen-tests/test/fixtures/platforms/ubuntu/14.10.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "apache": {
- "package": "apache2",
- "service_name": "apache2"
- },
- "mysql": {
- "server_package": "mysql-server-5.5",
- "client_package": "mysql-client-5.5",
- "service_name": "mysql"
- },
- "php" : {
- "package": "php5"
- }
-}
diff --git a/kitchen-tests/test/fixtures/serverspec_helper.rb b/kitchen-tests/test/fixtures/serverspec_helper.rb
deleted file mode 100644
index feb4c21200..0000000000
--- a/kitchen-tests/test/fixtures/serverspec_helper.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# Shamelessly copied from https://github.com/onehealth-cookbooks/apache2/blob/master/test/fixtures/serverspec_helper.rb
-# The commented-out platforms in the osmapping hash can be added once we have added them into
-# our .kitchen.yml and .kitchen.travis.yml and added the appropriate JSON under test/fixtures/platforms.
-
-require "serverspec"
-require "json"
-require "ffi_yajl"
-
-set :backend, :exec
-
-include Specinfra::Helper::Properties
-
-require "pp"
-pp os
-
-def load_nodestub
- case os[:family]
- when "ubuntu", "debian"
- platform = os[:family]
- platform_version = os[:release]
- when "redhat"
- platform = "centos"
- platform_version = os[:release].to_i
- end
- FFI_Yajl::Parser.parse(IO.read("#{ENV['BUSSER_ROOT']}/../kitchen/data/platforms/#{platform}/#{platform_version}.json"), :symbolize_names => true)
-end
-
-# centos-59 doesn't have /sbin in the default path,
-# so we must ensure it's on serverspec's path
-set :path, "$PATH:/sbin"
-
-set_property load_nodestub
diff --git a/kitchen-tests/test/integration/webapp/serverspec/Gemfile b/kitchen-tests/test/integration/webapp/serverspec/Gemfile
deleted file mode 100644
index eef1450f7a..0000000000
--- a/kitchen-tests/test/integration/webapp/serverspec/Gemfile
+++ /dev/null
@@ -1,4 +0,0 @@
-# This Gemfile is only needed so that busser will install gems it needs for serverspec_helper.rb to work
-source "https://rubygems.org"
-
-gem "ffi-yajl", "~> 1.1" # Go away, JSON gem
diff --git a/kitchen-tests/test/integration/webapp/serverspec/Gemfile.lock b/kitchen-tests/test/integration/webapp/serverspec/Gemfile.lock
deleted file mode 100644
index ac6c11f28c..0000000000
--- a/kitchen-tests/test/integration/webapp/serverspec/Gemfile.lock
+++ /dev/null
@@ -1,19 +0,0 @@
-GEM
- remote: https://rubygems.org/
- specs:
- ffi (1.9.10)
- ffi (1.9.10-x86-mingw32)
- ffi-yajl (1.4.0)
- ffi (~> 1.5)
- libyajl2 (~> 1.2)
- libyajl2 (1.2.0)
-
-PLATFORMS
- ruby
- x86-mingw32
-
-DEPENDENCIES
- ffi-yajl (~> 1.1)
-
-BUNDLED WITH
- 1.11.2
diff --git a/kitchen-tests/test/integration/webapp/serverspec/localhost/default_spec.rb b/kitchen-tests/test/integration/webapp/serverspec/localhost/default_spec.rb
deleted file mode 100644
index 992e4f7683..0000000000
--- a/kitchen-tests/test/integration/webapp/serverspec/localhost/default_spec.rb
+++ /dev/null
@@ -1,127 +0,0 @@
-
-require "net/http"
-require "uri"
-
-require "#{ENV['BUSSER_ROOT']}/../kitchen/data/serverspec_helper"
-
-describe "webapp::default", :end_to_end => true do
-
- describe "installed packages" do
- shared_examples_for "a package" do
- it "is installed" do
- expect(package(package_name)).to be_installed
- end
- end
-
- describe "#{property[:apache][:package]} package" do
- include_examples "a package" do
- let(:package_name) { property[:apache][:package] }
- end
- end
-
- describe "#{property[:mysql][:server_package]} package" do
- include_examples "a package" do
- let(:package_name) { property[:mysql][:server_package] }
- end
- end
-
- describe "#{property[:mysql][:client_package]} package" do
- include_examples "a package" do
- let(:package_name) { property[:mysql][:client_package] }
- end
- end
-
- describe "php package" do
- include_examples "a package" do
- let(:package_name) { property[:php][:package] }
- end
- end
- end
-
- describe "enabled/running services" do
- shared_examples_for "a service" do
- it "is enabled" do
- expect(service(service_name)).to be_enabled
- end
-
- it "is running" do
- expect(service(service_name)).to be_enabled
- end
- end
-
- describe "#{property[:apache][:service_name]} service" do
- include_examples "a service" do
- let(:service_name) { property[:apache][:service_name] }
- end
- end
-
- describe "mysql service" do
- include_examples "a service" do
- let(:service_name) { property[:mysql][:service_name] }
- end
- end
-
- end
-
- describe "mysql database" do
- let(:db_query) { "mysql -u root -pilikerandompasswordstoo -e \"#{statement}\"" }
- let(:statement) { "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='webapp'" }
- it "creates a database called 'webapp'" do
- expect(command(db_query).stdout).to match /webapp/
- end
-
- describe "mysql database user 'webapp'" do
- let(:statement) { "SELECT Host, Db FROM mysql.db WHERE User='webapp'\\G" }
- it "adds user 'webapp' to database 'webapp@localhost'" do
- expect(command(db_query).stdout).to match /Host: localhost\n Db: webapp/
- end
-
- describe "grants" do
- shared_examples_for "a privilege" do |priv|
- let(:statement) {
- "SELECT #{priv_query}" \
- " FROM mysql.db" \
- " WHERE Host='localhost' AND Db='webapp' AND User='webapp'\\G"
- }
- let(:priv_query) { "#{priv.capitalize}_priv" }
-
- it "has privilege #{priv} on 'webapp@localhost'" do
- expect(command(db_query).stdout).to match /#{priv_query}: Y/
- end
- end
-
- %w{select update insert delete create}.each do |priv|
- include_examples "a privilege", priv do
- end
- end
- end
- end
- end
-
- describe "generated webpages" do
- let(:get_response) { Net::HTTP.get_response(uri) }
- shared_examples_for "a webpage" do
- it "exists" do
- expect(get_response).to be_kind_of(Net::HTTPSuccess)
- end
-
- it "displays content" do
- expect(get_response.body).to include(content)
- end
- end
-
- describe "http://localhost/index.html" do
- include_examples "a webpage" do
- let(:uri) { URI.parse("http://localhost/index.html") }
- let(:content) { "Hello, World!" }
- end
- end
-
- describe "http://localhost/index.php" do
- include_examples "a webpage" do
- let(:uri) { URI.parse("http://localhost/index.php") }
- let(:content) { "Hello, World!" }
- end
- end
- end
-end
diff --git a/lib-backcompat/chef/chef_fs/file_system/acl_entry.rb b/lib-backcompat/chef/chef_fs/file_system/acl_entry.rb
deleted file mode 100644
index f28b9f86e3..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/acl_entry.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require "chef/chef_fs/file_system/chef_server/acl_entry"
-
-module Chef::ChefFS::FileSystem
- AclEntry = ChefServer::AclEntry
-end
diff --git a/lib-backcompat/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb b/lib-backcompat/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb
deleted file mode 100644
index 123fb9ee9a..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require "chef/chef_fs/file_system/repository/chef_repository_file_system_root_dir"
-
-module Chef::ChefFS::FileSystem
- ChefRepositoryFileSystemRootDir = Repository::ChefRepositoryFileSystemRootDir
-end
diff --git a/lib-backcompat/chef/chef_fs/file_system/chef_server_root_dir.rb b/lib-backcompat/chef/chef_fs/file_system/chef_server_root_dir.rb
deleted file mode 100644
index acb81dd08a..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/chef_server_root_dir.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require "chef/chef_fs/file_system/chef_server/chef_server_root_dir"
-
-module Chef::ChefFS::FileSystem
- ChefServerRootDir = ChefServer::ChefServerRootDir
-end
diff --git a/lib-backcompat/chef/chef_fs/file_system/cookbook_frozen_error.rb b/lib-backcompat/chef/chef_fs/file_system/cookbook_frozen_error.rb
deleted file mode 100644
index 6d9973e8a4..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/cookbook_frozen_error.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/chef_fs/file_system/exceptions"
-Chef.log_deprecation "Individual ChefFS error files are deprecated. Please require 'chef/chef_fs/file_system/exceptions' rather than 'chef/chef_fs/file_system/#{File.basename(__FILE__, ".rb")}'."
diff --git a/lib-backcompat/chef/chef_fs/file_system/default_environment_cannot_be_modified_error.rb b/lib-backcompat/chef/chef_fs/file_system/default_environment_cannot_be_modified_error.rb
deleted file mode 100644
index 6d9973e8a4..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/default_environment_cannot_be_modified_error.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/chef_fs/file_system/exceptions"
-Chef.log_deprecation "Individual ChefFS error files are deprecated. Please require 'chef/chef_fs/file_system/exceptions' rather than 'chef/chef_fs/file_system/#{File.basename(__FILE__, ".rb")}'."
diff --git a/lib-backcompat/chef/chef_fs/file_system/file_system_error.rb b/lib-backcompat/chef/chef_fs/file_system/file_system_error.rb
deleted file mode 100644
index 6d9973e8a4..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/file_system_error.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/chef_fs/file_system/exceptions"
-Chef.log_deprecation "Individual ChefFS error files are deprecated. Please require 'chef/chef_fs/file_system/exceptions' rather than 'chef/chef_fs/file_system/#{File.basename(__FILE__, ".rb")}'."
diff --git a/lib-backcompat/chef/chef_fs/file_system/must_delete_recursively_error.rb b/lib-backcompat/chef/chef_fs/file_system/must_delete_recursively_error.rb
deleted file mode 100644
index 6d9973e8a4..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/must_delete_recursively_error.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/chef_fs/file_system/exceptions"
-Chef.log_deprecation "Individual ChefFS error files are deprecated. Please require 'chef/chef_fs/file_system/exceptions' rather than 'chef/chef_fs/file_system/#{File.basename(__FILE__, ".rb")}'."
diff --git a/lib-backcompat/chef/chef_fs/file_system/not_found_error.rb b/lib-backcompat/chef/chef_fs/file_system/not_found_error.rb
deleted file mode 100644
index 6d9973e8a4..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/not_found_error.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/chef_fs/file_system/exceptions"
-Chef.log_deprecation "Individual ChefFS error files are deprecated. Please require 'chef/chef_fs/file_system/exceptions' rather than 'chef/chef_fs/file_system/#{File.basename(__FILE__, ".rb")}'."
diff --git a/lib-backcompat/chef/chef_fs/file_system/operation_failed_error.rb b/lib-backcompat/chef/chef_fs/file_system/operation_failed_error.rb
deleted file mode 100644
index 6d9973e8a4..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/operation_failed_error.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/chef_fs/file_system/exceptions"
-Chef.log_deprecation "Individual ChefFS error files are deprecated. Please require 'chef/chef_fs/file_system/exceptions' rather than 'chef/chef_fs/file_system/#{File.basename(__FILE__, ".rb")}'."
diff --git a/lib-backcompat/chef/chef_fs/file_system/operation_not_allowed_error.rb b/lib-backcompat/chef/chef_fs/file_system/operation_not_allowed_error.rb
deleted file mode 100644
index 6d9973e8a4..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/operation_not_allowed_error.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/chef_fs/file_system/exceptions"
-Chef.log_deprecation "Individual ChefFS error files are deprecated. Please require 'chef/chef_fs/file_system/exceptions' rather than 'chef/chef_fs/file_system/#{File.basename(__FILE__, ".rb")}'."
diff --git a/lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_acls_dir.rb b/lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_acls_dir.rb
deleted file mode 100644
index d9bdbc104c..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_acls_dir.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require "chef/chef_fs/file_system/repository/acls_dir"
-
-module Chef::ChefFS::FileSystem::Repository
- ChefRepositoryFileSystemAclsDir = AclsDir
-end
diff --git a/lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_client_keys_dir.rb b/lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_client_keys_dir.rb
deleted file mode 100644
index 4ebcb5f010..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_client_keys_dir.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require "chef/chef_fs/file_system/repository/client_keys_dir"
-
-module Chef::ChefFS::FileSystem::Repository
- ChefRepositoryFileSystemClientKeysDir = ClientKeysDir
-end
diff --git a/lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_entry.rb b/lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_entry.rb
deleted file mode 100644
index eb2c3e8ff6..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_entry.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-require "chef/chef_fs/file_system/repository/file_system_entry"
-
-module Chef::ChefFS::FileSystem::Repository
- Chef.log_deprecation "Chef::ChefFS::FileSystem::Repository::ChefRepositoryFileSystemEntry is deprecated. Please use FileSystemEntry directly"
- ChefRepositoryFileSystemEntry = FileSystemEntry
-end
diff --git a/lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_policies_dir.rb b/lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_policies_dir.rb
deleted file mode 100644
index 393e4aa85d..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_policies_dir.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require "chef/chef_fs/file_system/repository/policies_dir"
-
-module Chef::ChefFS::FileSystem::Repository
- ChefRepositoryFileSystemPoliciesDir = PoliciesDir
-end
diff --git a/lib-backcompat/chef/chef_fs/file_system/repository/file_system_root_dir.rb b/lib-backcompat/chef/chef_fs/file_system/repository/file_system_root_dir.rb
deleted file mode 100644
index 8565ee6029..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/repository/file_system_root_dir.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/chef_fs/file_system/repository/file_system_entry"
-
-class Chef
- module ChefFS
- module FileSystem
- module Repository
- class FileSystemRootDir < FileSystemEntry
- def initialize(file_path)
- Chef.log_deprecation "Chef::ChefFS::FileSystem::Repository::FileSystemRootDir is deprecated."
- super("", nil, file_path)
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef.rb b/lib/chef.rb
index 3e161dc365..3d6b783253 100644
--- a/lib/chef.rb
+++ b/lib/chef.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,6 @@ require "chef/log"
require "chef/config"
require "chef/providers"
require "chef/resources"
-require "chef/shell_out"
require "chef/daemon"
diff --git a/lib/chef/api_client.rb b/lib/chef/api_client.rb
index 3c1ef7a2b6..656a0e364f 100644
--- a/lib/chef/api_client.rb
+++ b/lib/chef/api_client.rb
@@ -47,7 +47,7 @@ class Chef
# Gets or sets the client name.
#
- # @params [Optional String] The name must be alpha-numeric plus - and _.
+ # @param [Optional String] The name must be alpha-numeric plus - and _.
# @return [String] The current value of the name.
def name(arg = nil)
set_or_return(
@@ -59,7 +59,7 @@ class Chef
# Gets or sets whether this client is an admin.
#
- # @params [Optional True/False] Should be true or false - default is false.
+ # @param [Optional True/False] Should be true or false - default is false.
# @return [True/False] The current value
def admin(arg = nil)
set_or_return(
@@ -71,7 +71,7 @@ class Chef
# Gets or sets the public key.
#
- # @params [Optional String] The string representation of the public key.
+ # @param [Optional String] The string representation of the public key.
# @return [String] The current value.
def public_key(arg = nil)
set_or_return(
@@ -83,7 +83,7 @@ class Chef
# Gets or sets whether this client is a validator.
#
- # @params [Boolean] whether or not the client is a validator. If
+ # @param [Boolean] arg whether or not the client is a validator. If
# `nil`, retrieves the already-set value.
# @return [Boolean] The current value
def validator(arg = nil)
@@ -96,7 +96,7 @@ class Chef
# Gets or sets the private key.
#
- # @params [Optional String] The string representation of the private key.
+ # @param [Optional String] The string representation of the private key.
# @return [String] The current value.
def private_key(arg = nil)
set_or_return(
@@ -140,11 +140,6 @@ class Chef
client
end
- def self.json_create(data)
- Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please use Chef::ApiClient#from_hash")
- from_hash(data)
- end
-
def self.from_json(j)
from_hash(Chef::JSONCompat.parse(j))
end
@@ -162,7 +157,7 @@ class Chef
if inflate
response = Hash.new
Chef::Search::Query.new.search(:client) do |n|
- n = self.json_create(n) if n.instance_of?(Hash)
+ n = json_create(n) if n.instance_of?(Hash)
response[n.name] = n
end
response
@@ -188,15 +183,13 @@ class Chef
# Save this client via the REST API, returns a hash including the private key
def save
- begin
- http_api.put("clients/#{name}", { :name => self.name, :admin => self.admin, :validator => self.validator })
- rescue Net::HTTPServerException => e
- # If that fails, go ahead and try and update it
- if e.response.code == "404"
- http_api.post("clients", { :name => self.name, :admin => self.admin, :validator => self.validator })
- else
- raise e
- end
+ http_api.put("clients/#{name}", { :name => name, :admin => admin, :validator => validator })
+ rescue Net::HTTPServerException => e
+ # If that fails, go ahead and try and update it
+ if e.response.code == "404"
+ http_api.post("clients", { :name => name, :admin => admin, :validator => validator })
+ else
+ raise e
end
end
diff --git a/lib/chef/api_client/registration.rb b/lib/chef/api_client/registration.rb
index e8ab0149e8..27e1f18c17 100644
--- a/lib/chef/api_client/registration.rb
+++ b/lib/chef/api_client/registration.rb
@@ -19,6 +19,7 @@
require "chef/config"
require "chef/server_api"
require "chef/exceptions"
+require "fileutils"
class Chef
class ApiClient
@@ -69,8 +70,15 @@ class Chef
end
def assert_destination_writable!
- if (File.exists?(destination) && !File.writable?(destination)) || !File.writable?(File.dirname(destination))
- abs_path = File.expand_path(destination)
+ abs_path = File.expand_path(destination)
+ if !File.exists?(File.dirname(abs_path))
+ begin
+ FileUtils.mkdir_p(File.dirname(abs_path))
+ rescue Errno::EACCES
+ raise Chef::Exceptions::CannotWritePrivateKey, "I can't create the configuration directory at #{File.dirname(abs_path)} - check permissions?"
+ end
+ end
+ if (File.exists?(abs_path) && !File.writable?(abs_path)) || !File.writable?(File.dirname(abs_path))
raise Chef::Exceptions::CannotWritePrivateKey, "I can't write your private key to #{abs_path} - check permissions?"
end
end
diff --git a/lib/chef/api_client_v1.rb b/lib/chef/api_client_v1.rb
index 47b0cd1c53..7fbff6d6ba 100644
--- a/lib/chef/api_client_v1.rb
+++ b/lib/chef/api_client_v1.rb
@@ -70,7 +70,7 @@ class Chef
# Gets or sets the client name.
#
- # @params [Optional String] The name must be alpha-numeric plus - and _.
+ # @param arg [Optional String] The name must be alpha-numeric plus - and _.
# @return [String] The current value of the name.
def name(arg = nil)
set_or_return(
@@ -82,7 +82,7 @@ class Chef
# Gets or sets whether this client is an admin.
#
- # @params [Optional True/False] Should be true or false - default is false.
+ # @param arg [Optional True/False] Should be true or false - default is false.
# @return [True/False] The current value
def admin(arg = nil)
set_or_return(
@@ -94,7 +94,7 @@ class Chef
# Gets or sets the public key.
#
- # @params [Optional String] The string representation of the public key.
+ # @param arg [Optional String] The string representation of the public key.
# @return [String] The current value.
def public_key(arg = nil)
set_or_return(
@@ -106,7 +106,7 @@ class Chef
# Gets or sets whether this client is a validator.
#
- # @params [Boolean] whether or not the client is a validator. If
+ # @param arg [Boolean] whether or not the client is a validator. If
# `nil`, retrieves the already-set value.
# @return [Boolean] The current value
def validator(arg = nil)
@@ -120,7 +120,7 @@ class Chef
# Private key. The server will return it as a string.
# Set to true under API V0 to have the server regenerate the default key.
#
- # @params [Optional String] The string representation of the private key.
+ # @param arg [Optional String] The string representation of the private key.
# @return [String] The current value.
def private_key(arg = nil)
set_or_return(
@@ -132,7 +132,7 @@ class Chef
# Used to ask server to generate key pair under api V1
#
- # @params [Optional True/False] Should be true or false - default is false.
+ # @param arg [Optional True/False] Should be true or false - default is false.
# @return [True/False] The current value
def create_key(arg = nil)
set_or_return(
@@ -190,7 +190,7 @@ class Chef
if inflate
response = Hash.new
Chef::Search::Query.new.search(:client) do |n|
- n = self.from_hash(n) if n.instance_of?(Hash)
+ n = from_hash(n) if n.instance_of?(Hash)
response[n.name] = n
end
response
@@ -200,6 +200,7 @@ class Chef
end
# Load a client by name via the API
+ # @param name [String] the client name
def self.load(name)
response = http_api.get("clients/#{name}")
Chef::ApiClientV1.from_hash(response)
@@ -212,15 +213,13 @@ class Chef
# Save this client via the REST API, returns a hash including the private key
def save
- begin
- update
- rescue Net::HTTPServerException => e
- # If that fails, go ahead and try and update it
- if e.response.code == "404"
- create
- else
- raise e
- end
+ update
+ rescue Net::HTTPServerException => e
+ # If that fails, go ahead and try and update it
+ if e.response.code == "404"
+ create
+ else
+ raise e
end
end
@@ -313,7 +312,7 @@ class Chef
new_client = chef_rest_v0.post("clients", payload)
end
- Chef::ApiClientV1.from_hash(self.to_hash.merge(new_client))
+ Chef::ApiClientV1.from_hash(to_hash.merge(new_client))
end
# As a string
diff --git a/lib/chef/application.rb b/lib/chef/application.rb
index 7dbffd8dec..811c713464 100644
--- a/lib/chef/application.rb
+++ b/lib/chef/application.rb
@@ -1,7 +1,7 @@
#
# Author:: AJ Christensen (<aj@chef.io>)
# Author:: Mark Mzyk (mmzyk@chef.io)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,6 +27,7 @@ require "chef/platform"
require "mixlib/cli"
require "tmpdir"
require "rbconfig"
+require "chef/application/exit_code"
class Chef
class Application
@@ -42,8 +43,15 @@ class Chef
# from failing due to permissions when launched as a less privileged user.
end
+ # Configure mixlib-cli to always separate defaults from user-supplied CLI options
+ def self.use_separate_defaults?
+ true
+ end
+
# Reconfigure the application. You'll want to override and super this method.
def reconfigure
+ # In case any gems were installed for use in the config.
+ Gem.clear_paths
configure_chef
configure_logging
configure_encoding
@@ -60,37 +68,63 @@ class Chef
def setup_signal_handlers
trap("INT") do
- Chef::Application.fatal!("SIGINT received, stopping", 2)
+ Chef::Application.fatal!("SIGINT received, stopping", Chef::Exceptions::SigInt.new)
end
trap("TERM") do
- Chef::Application.fatal!("SIGTERM received, stopping", 3)
+ Chef::Application.fatal!("SIGTERM received, stopping", Chef::Exceptions::SigTerm.new)
end
unless Chef::Platform.windows?
trap("QUIT") do
- Chef::Log.info("SIGQUIT received, call stack:\n " + caller.join("\n "))
+ logger.info("SIGQUIT received, call stack:\n " + caller.join("\n "))
end
trap("HUP") do
- Chef::Log.info("SIGHUP received, reconfiguring")
+ logger.info("SIGHUP received, reconfiguring")
reconfigure
end
end
end
+ def emit_warnings
+ logger.warn "chef_config[:zypper_check_gpg] is set to false which disables security checking on zypper packages" unless chef_config[:zypper_check_gpg]
+ end
+
# Parse configuration (options and config file)
def configure_chef
parse_options
load_config_file
- Chef::Config.export_proxies
- Chef::Config.init_openssl
+ chef_config.export_proxies
+ chef_config.init_openssl
+ File.umask chef_config[:umask]
+ end
+
+ # @api private (test injection)
+ def chef_config
+ Chef::Config
+ end
+
+ # @api private (test injection)
+ def logger
+ Chef::Log
+ end
+
+ def self.logger
+ Chef::Log
+ end
+
+ # @api private (test injection)
+ def chef_configfetcher
+ Chef::ConfigFetcher
end
# Parse the config file
def load_config_file
- config_fetcher = Chef::ConfigFetcher.new(config[:config_file])
+ # apply the default cli options first
+ chef_config.merge!(default_config)
+ config_fetcher = chef_configfetcher.new(config[:config_file])
# Some config settings are derived relative to the config file path; if
# given as a relative path, this is computed relative to cwd, but
# chef-client will later chdir to root, so we need to get the absolute path
@@ -98,22 +132,31 @@ class Chef
config[:config_file] = config_fetcher.expanded_path
if config[:config_file].nil?
- Chef::Log.warn("No config file found or specified on command line, using command line options.")
+ logger.warn("No config file found or specified on command line, using command line options.")
elsif config_fetcher.config_missing?
- Chef::Log.warn("*****************************************")
- Chef::Log.warn("Did not find config file: #{config[:config_file]}, using command line options.")
- Chef::Log.warn("*****************************************")
+ logger.warn("*****************************************")
+ logger.warn("Did not find config file: #{config[:config_file]}, using command line options.")
+ logger.warn("*****************************************")
else
config_content = config_fetcher.read_config
apply_config(config_content, config[:config_file])
end
- Chef::Config.merge!(config)
+ extra_config_options = config.delete(:config_option)
+ chef_config.merge!(config)
+ apply_extra_config_options(extra_config_options)
+ end
+
+ def apply_extra_config_options(extra_config_options)
+ chef_config.apply_extra_config_options(extra_config_options)
+ rescue ChefConfig::UnparsableConfigOption => e
+ Chef::Application.fatal!(e.message)
end
def set_specific_recipes
- Chef::Config[:specific_recipes] =
- cli_arguments.map { |file| File.expand_path(file) } if
- cli_arguments.respond_to?(:map)
+ if cli_arguments.respond_to?(:map)
+ chef_config[:specific_recipes] =
+ cli_arguments.map { |file| File.expand_path(file) }
+ end
end
# Initialize and configure the logger.
@@ -130,62 +173,54 @@ class Chef
# unattended, the `force_formatter` option is provided.
#
# === Auto Log Level
- # When `log_level` is set to `:auto` (default), the log level will be `:warn`
- # when the primary output mode is an output formatter (see
- # +using_output_formatter?+) and `:info` otherwise.
+ # The `log_level` of `:auto` means `:warn` in the formatter and `:info` in
+ # the logger.
#
- # === Automatic STDOUT Logging
- # When `force_logger` is configured (e.g., Chef 10 mode), a second logger
- # with output on STDOUT is added when running in a console (STDOUT is a tty)
- # and the configured log_location isn't STDOUT. This accounts for the case
- # that a user has configured a log_location in client.rb, but is running
- # chef-client by hand to troubleshoot a problem.
def configure_logging
configure_log_location
- Chef::Log.init(MonoLogger.new(Chef::Config[:log_location]))
+ logger.init(MonoLogger.new(chef_config[:log_location]))
if want_additional_logger?
configure_stdout_logger
end
- Chef::Log.level = resolve_log_level
+ logger.level = resolve_log_level
rescue StandardError => error
- Chef::Log.fatal("Failed to open or create log file at #{Chef::Config[:log_location]}: #{error.class} (#{error.message})")
- Chef::Application.fatal!("Aborting due to invalid 'log_location' configuration", 2)
+ logger.fatal("Failed to open or create log file at #{chef_config[:log_location]}: #{error.class} (#{error.message})")
+ Chef::Application.fatal!("Aborting due to invalid 'log_location' configuration", error)
end
# Turn `log_location :syslog` and `log_location :win_evt` into the
# appropriate loggers.
def configure_log_location
- log_location = Chef::Config[:log_location]
+ log_location = chef_config[:log_location]
return unless log_location.respond_to?(:to_sym)
- Chef::Config[:log_location] =
+ chef_config[:log_location] =
case log_location.to_sym
- when :syslog then Chef::Log::Syslog.new
- when :win_evt then Chef::Log::WinEvt.new
+ when :syslog then logger::Syslog.new
+ when :win_evt then logger::WinEvt.new
else log_location # Probably a path; let MonoLogger sort it out
end
end
- def configure_stdout_logger
- stdout_logger = MonoLogger.new(STDOUT)
- stdout_logger.formatter = Chef::Log.logger.formatter
- Chef::Log.loggers << stdout_logger
- end
-
# Based on config and whether or not STDOUT is a tty, should we setup a
# secondary logger for stdout?
def want_additional_logger?
- ( Chef::Config[:log_location] != STDOUT ) && STDOUT.tty? && (!Chef::Config[:daemonize]) && (Chef::Config[:force_logger])
+ !( chef_config[:log_location].is_a?(IO) && chef_config[:log_location].tty? ) && !chef_config[:daemonize]
end
- # Use of output formatters is assumed if `force_formatter` is set or if
- # `force_logger` is not set and STDOUT is to a console (tty)
+ def configure_stdout_logger
+ stdout_logger = MonoLogger.new(STDOUT)
+ stdout_logger.formatter = logger.logger.formatter
+ logger.loggers << stdout_logger
+ end
+
+ # Use of output formatters is assumed if `force_formatter` is set or if `force_logger` is not set
def using_output_formatter?
- Chef::Config[:force_formatter] || (!Chef::Config[:force_logger] && STDOUT.tty?)
+ chef_config[:force_formatter] || !chef_config[:force_logger]
end
def auto_log_level?
- Chef::Config[:log_level] == :auto
+ chef_config[:log_level] == :auto
end
# if log_level is `:auto`, convert it to :warn (when using output formatter)
@@ -198,13 +233,13 @@ class Chef
:info
end
else
- Chef::Config[:log_level]
+ chef_config[:log_level]
end
end
# Sets the default external encoding to UTF-8 (users can change this, but they shouldn't)
def configure_encoding
- Encoding.default_external = Chef::Config[:ruby_encoding]
+ Encoding.default_external = chef_config[:ruby_encoding]
end
# Called prior to starting the application, by the run method
@@ -230,7 +265,8 @@ class Chef
@chef_client_json,
override_runlist: override_runlist,
specific_recipes: specific_recipes,
- runlist: config[:runlist]
+ runlist: config[:runlist],
+ logger: logger
)
@chef_client_json = nil
@@ -252,7 +288,7 @@ class Chef
# win32-process gem exposes some form of :fork for Process
# class. So we are separately ensuring that the platform we're
# running on is not windows before forking.
- Chef::Config[:client_fork] && Process.respond_to?(:fork) && !Chef::Platform.windows?
+ chef_config[:client_fork] && Process.respond_to?(:fork) && !Chef::Platform.windows?
end
# Run chef-client once and then exit. If TERM signal is received, ignores the
@@ -260,7 +296,7 @@ class Chef
def run_with_graceful_exit_option
# Override the TERM signal.
trap("TERM") do
- Chef::Log.debug("SIGTERM received during converge," +
+ logger.debug("SIGTERM received during converge," +
" finishing converge to exit normally (send SIGINT to terminate immediately)")
end
@@ -269,31 +305,31 @@ class Chef
end
def fork_chef_client
- Chef::Log.info "Forking chef instance to converge..."
+ logger.info "Forking chef instance to converge..."
pid = fork do
# Want to allow forked processes to finish converging when
# TERM singal is received (exit gracefully)
trap("TERM") do
- Chef::Log.debug("SIGTERM received during converge," +
+ logger.debug("SIGTERM received during converge," +
" finishing converge to exit normally (send SIGINT to terminate immediately)")
end
- client_solo = Chef::Config[:solo] ? "chef-solo" : "chef-client"
+ client_solo = chef_config[:solo] ? "chef-solo" : "chef-client"
$0 = "#{client_solo} worker: ppid=#{Process.ppid};start=#{Time.new.strftime("%R:%S")};"
begin
- Chef::Log.debug "Forked instance now converging"
+ logger.trace "Forked instance now converging"
@chef_client.run
rescue Exception => e
- Chef::Log.error(e.to_s)
- exit 1
+ logger.error(e.to_s)
+ exit Chef::Application.normalize_exit_code(e)
else
exit 0
end
end
- Chef::Log.debug "Fork successful. Waiting for new chef pid: #{pid}"
+ logger.trace "Fork successful. Waiting for new chef pid: #{pid}"
result = Process.waitpid2(pid)
handle_child_exit(result)
- Chef::Log.debug "Forked instance successfully reaped (pid: #{pid})"
+ logger.trace "Forked instance successfully reaped (pid: #{pid})"
true
end
@@ -309,12 +345,12 @@ class Chef
end
def apply_config(config_content, config_file_path)
- Chef::Config.from_string(config_content, config_file_path)
+ chef_config.from_string(config_content, config_file_path)
rescue Exception => error
- Chef::Log.fatal("Configuration error #{error.class}: #{error.message}")
+ logger.fatal("Configuration error #{error.class}: #{error.message}")
filtered_trace = error.backtrace.grep(/#{Regexp.escape(config_file_path)}/)
- filtered_trace.each { |line| Chef::Log.fatal(" " + line ) }
- Chef::Application.fatal!("Aborting due to error in '#{config_file_path}'", 2)
+ filtered_trace.each { |line| logger.fatal(" " + line ) }
+ Chef::Application.fatal!("Aborting due to error in '#{config_file_path}'", error)
end
# This is a hook for testing
@@ -322,34 +358,39 @@ class Chef
ENV
end
- def emit_warnings
- if Chef::Config[:chef_gem_compile_time]
- Chef.log_deprecation "setting chef_gem_compile_time to true is deprecated"
- end
- end
-
class << self
def debug_stacktrace(e)
message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
+
+ cause = e.cause if e.respond_to?(:cause)
+ until cause.nil?
+ message << "\n\n>>>> Caused by #{cause.class}: #{cause}\n#{cause.backtrace.join("\n")}"
+ cause = cause.respond_to?(:cause) ? cause.cause : nil
+ end
+
chef_stacktrace_out = "Generated at #{Time.now}\n"
chef_stacktrace_out += message
Chef::FileCache.store("chef-stacktrace.out", chef_stacktrace_out)
- Chef::Log.fatal("Stacktrace dumped to #{Chef::FileCache.load("chef-stacktrace.out", false)}")
- Chef::Log.fatal("Please provide the contents of the stacktrace.out file if you file a bug report")
- Chef::Log.debug(message)
+ logger.fatal("Stacktrace dumped to #{Chef::FileCache.load("chef-stacktrace.out", false)}")
+ logger.fatal("Please provide the contents of the stacktrace.out file if you file a bug report")
+ logger.debug(message)
true
end
+ def normalize_exit_code(exit_code)
+ Chef::Application::ExitCode.normalize_exit_code(exit_code)
+ end
+
# Log a fatal error message to both STDERR and the Logger, exit the application
- def fatal!(msg, err = -1)
- Chef::Log.fatal(msg)
- Process.exit err
+ def fatal!(msg, err = nil)
+ logger.fatal(msg)
+ Process.exit(normalize_exit_code(err))
end
- def exit!(msg, err = -1)
- Chef::Log.debug(msg)
- Process.exit err
+ def exit!(msg, err = nil)
+ logger.debug(msg)
+ Process.exit(normalize_exit_code(err))
end
end
diff --git a/lib/chef/application/apply.rb b/lib/chef/application/apply.rb
index 03c86b86f2..ec627ada2e 100644
--- a/lib/chef/application/apply.rb
+++ b/lib/chef/application/apply.rb
@@ -70,7 +70,7 @@ class Chef::Application::Apply < Chef::Application
option :log_level,
:short => "-l LEVEL",
:long => "--log_level LEVEL",
- :description => "Set the log level (debug, info, warn, error, fatal)",
+ :description => "Set the log level (trace, debug, info, warn, error, fatal)",
:proc => lambda { |l| l.to_sym }
option :help,
@@ -137,11 +137,11 @@ class Chef::Application::Apply < Chef::Application
def read_recipe_file(file_name)
if file_name.nil?
- Chef::Application.fatal!("No recipe file was provided", 1)
+ Chef::Application.fatal!("No recipe file was provided", Chef::Exceptions::RecipeNotFound.new)
else
recipe_path = File.expand_path(file_name)
unless File.exist?(recipe_path)
- Chef::Application.fatal!("No file exists at #{recipe_path}", 1)
+ Chef::Application.fatal!("No file exists at #{recipe_path}", Chef::Exceptions::RecipeNotFound.new)
end
recipe_fh = open(recipe_path)
recipe_text = recipe_fh.read
@@ -150,7 +150,7 @@ class Chef::Application::Apply < Chef::Application
end
def get_recipe_and_run_context
- Chef::Config[:solo] = true
+ Chef::Config[:solo_legacy_mode] = true
@chef_client = Chef::Client.new(@json_attribs)
@chef_client.run_ohai
@chef_client.load_node
@@ -183,7 +183,7 @@ class Chef::Application::Apply < Chef::Application
else
if !ARGV[0]
puts opt_parser
- Chef::Application.exit! "No recipe file provided", 1
+ Chef::Application.exit! "No recipe file provided", Chef::Exceptions::RecipeNotFound.new
end
@recipe_filename = ARGV[0]
@recipe_text, @recipe_fh = read_recipe_file @recipe_filename
@@ -200,16 +200,14 @@ class Chef::Application::Apply < Chef::Application
end
def run_application
- begin
- parse_options
- run_chef_recipe
- Chef::Application.exit! "Exiting", 0
- rescue SystemExit
- raise
- rescue Exception => e
- Chef::Application.debug_stacktrace(e)
- Chef::Application.fatal!("#{e.class}: #{e.message}", 1)
- end
+ parse_options
+ run_chef_recipe
+ Chef::Application.exit! "Exiting", 0
+ rescue SystemExit
+ raise
+ rescue Exception => e
+ Chef::Application.debug_stacktrace(e)
+ Chef::Application.fatal!("#{e.class}: #{e.message}", e)
end
# Get this party started
diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb
index ac46e533dd..ffb997f187 100644
--- a/lib/chef/application/client.rb
+++ b/lib/chef/application/client.rb
@@ -2,7 +2,7 @@
# Author:: AJ Christensen (<aj@chef.io)
# Author:: Christopher Brown (<cb@chef.io>)
# Author:: Mark Mzyk (mmzyk@chef.io)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,6 +27,8 @@ require "chef/handler/error_report"
require "chef/workstation_config_loader"
require "chef/mixin/shell_out"
require "chef-config/mixin/dot_d"
+require "mixlib/archive"
+require "uri"
class Chef::Application::Client < Chef::Application
include Chef::Mixin::ShellOut
@@ -40,6 +42,14 @@ class Chef::Application::Client < Chef::Application
:long => "--config CONFIG",
:description => "The configuration file to use"
+ option :config_option,
+ :long => "--config-option OPTION=VALUE",
+ :description => "Override a single configuration option",
+ :proc => lambda { |option, existing|
+ (existing ||= []) << option
+ existing
+ }
+
option :formatter,
:short => "-F FORMATTER",
:long => "--format FORMATTER",
@@ -73,7 +83,7 @@ class Chef::Application::Client < Chef::Application
option :log_level,
:short => "-l LEVEL",
:long => "--log_level LEVEL",
- :description => "Set the log level (auto, debug, info, warn, error, fatal)",
+ :description => "Set the log level (auto, trace, debug, info, warn, error, fatal)",
:proc => lambda { |l| l.to_sym }
option :log_location,
@@ -193,22 +203,22 @@ class Chef::Application::Client < Chef::Application
:short => "-o RunlistItem,RunlistItem...",
:long => "--override-runlist RunlistItem,RunlistItem...",
:description => "Replace current run list with specified items for a single run",
- :proc => lambda {|items|
+ :proc => lambda { |items|
items = items.split(",")
- items.compact.map {|item|
+ items.compact.map do |item|
Chef::RunList::RunListItem.new(item)
- }
+ end
}
option :runlist,
:short => "-r RunlistItem,RunlistItem...",
:long => "--runlist RunlistItem,RunlistItem...",
:description => "Permanently replace current run list with specified items",
- :proc => lambda {|items|
+ :proc => lambda { |items|
items = items.split(",")
- items.compact.map {|item|
+ items.compact.map do |item|
Chef::RunList::RunListItem.new(item)
- }
+ end
}
option :why_run,
:short => "-W",
@@ -219,8 +229,7 @@ class Chef::Application::Client < Chef::Application
option :client_fork,
:short => "-f",
:long => "--[no-]fork",
- :description => "Fork client",
- :boolean => true
+ :description => "Fork client"
option :recipe_url,
:long => "--recipe-url=RECIPE_URL",
@@ -277,10 +286,10 @@ class Chef::Application::Client < Chef::Application
option :listen,
:long => "--[no-]listen",
:description => "Whether a local mode (-z) server binds to a port",
- :boolean => true
+ :boolean => false
option :fips,
- :long => "--fips",
+ :long => "--[no-]fips",
:description => "Enable fips mode",
:boolean => true
@@ -295,6 +304,7 @@ class Chef::Application::Client < Chef::Application
:boolean => false
IMMEDIATE_RUN_SIGNAL = "1".freeze
+ RECONFIGURE_SIGNAL = "H".freeze
attr_reader :chef_client_json
@@ -324,18 +334,19 @@ class Chef::Application::Client < Chef::Application
if Chef::Config[:recipe_url]
if !Chef::Config.local_mode
- Chef::Application.fatal!("chef-client recipe-url can be used only in local-mode", 1)
+ Chef::Application.fatal!("chef-client recipe-url can be used only in local-mode")
else
if Chef::Config[:delete_entire_chef_repo]
- Chef::Log.debug "Cleanup path #{Chef::Config.chef_repo_path} before extract recipes into it"
+ Chef::Log.trace "Cleanup path #{Chef::Config.chef_repo_path} before extract recipes into it"
FileUtils.rm_rf(recipes_path, :secure => true)
end
- Chef::Log.debug "Creating path #{Chef::Config.chef_repo_path} to extract recipes into"
+ Chef::Log.trace "Creating path #{Chef::Config.chef_repo_path} to extract recipes into"
FileUtils.mkdir_p(Chef::Config.chef_repo_path)
tarball_path = File.join(Chef::Config.chef_repo_path, "recipes.tgz")
fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path)
- result = shell_out!("tar zxvf #{tarball_path} -C #{Chef::Config.chef_repo_path}")
- Chef::Log.debug "#{result.stdout}"
+ Mixlib::Archive.new(tarball_path).extract(Chef::Config.chef_repo_path, perms: false, ignore: /^\.$/)
+ config_path = File.join(Chef::Config.chef_repo_path, ".chef/config.rb")
+ Chef::Config.from_string(IO.read(config_path), config_path) if File.file?(config_path)
end
end
@@ -351,6 +362,11 @@ class Chef::Application::Client < Chef::Application
Chef::Config[:splay] = nil
end
+ # supervisor processes are enabled by default for interval-running processes but not for one-shot runs
+ if Chef::Config[:client_fork].nil?
+ Chef::Config[:client_fork] = !!Chef::Config[:interval]
+ end
+
if !Chef::Config[:client_fork] && Chef::Config[:interval] && !Chef::Platform.windows?
Chef::Application.fatal!(unforked_interval_error_message)
end
@@ -401,9 +417,15 @@ class Chef::Application::Client < Chef::Application
SELF_PIPE.replace IO.pipe
trap("USR1") do
- Chef::Log.info("SIGUSR1 received, waking up")
+ Chef::Log.info("SIGUSR1 received, will run now or after the current run")
SELF_PIPE[1].putc(IMMEDIATE_RUN_SIGNAL) # wakeup master process from select
end
+
+ # Override the trap setup in the parent so we can avoid running reconfigure during a run
+ trap("HUP") do
+ Chef::Log.info("SIGHUP received, will reconfigure now or after the current run")
+ SELF_PIPE[1].putc(RECONFIGURE_SIGNAL) # wakeup master process from select
+ end
end
end
@@ -420,7 +442,7 @@ class Chef::Application::Client < Chef::Application
rescue SystemExit
raise
rescue Exception => e
- Chef::Application.fatal!("#{e.class}: #{e.message}", 1)
+ Chef::Application.fatal!("#{e.class}: #{e.message}", e)
end
else
interval_run_chef_client
@@ -446,28 +468,24 @@ class Chef::Application::Client < Chef::Application
end
def sleep_then_run_chef_client(sleep_sec)
- @signal = test_signal
- unless @signal == IMMEDIATE_RUN_SIGNAL
- Chef::Log.debug("Sleeping for #{sleep_sec} seconds")
- interval_sleep(sleep_sec)
- end
- @signal = nil
+ Chef::Log.trace("Sleeping for #{sleep_sec} seconds")
+
+ # interval_sleep will return early if we received a signal (unless on windows)
+ interval_sleep(sleep_sec)
run_chef_client(Chef::Config[:specific_recipes])
+
+ reconfigure
rescue SystemExit => e
raise
rescue Exception => e
if Chef::Config[:interval]
Chef::Log.error("#{e.class}: #{e}")
- Chef::Log.debug("#{e.class}: #{e}\n#{e.backtrace.join("\n")}")
+ Chef::Log.trace("#{e.class}: #{e}\n#{e.backtrace.join("\n")}")
retry
end
- Chef::Application.fatal!("#{e.class}: #{e.message}", 1)
- end
-
- def test_signal
- @signal = interval_sleep(0)
+ Chef::Application.fatal!("#{e.class}: #{e.message}", e)
end
def time_to_sleep
@@ -477,20 +495,26 @@ class Chef::Application::Client < Chef::Application
duration
end
+ # sleep and handle queued signals
def interval_sleep(sec)
unless SELF_PIPE.empty?
- client_sleep(sec)
+ # mimic sleep with a timeout on IO.select, listening for signals setup in #setup_signal_handlers
+ return unless IO.select([ SELF_PIPE[0] ], nil, nil, sec)
+
+ signal = SELF_PIPE[0].getc.chr
+
+ return if signal == IMMEDIATE_RUN_SIGNAL # be explicit about this behavior
+
+ # we need to sleep again after reconfigure to avoid stampeding when logrotate runs out of cron
+ if signal == RECONFIGURE_SIGNAL
+ reconfigure
+ interval_sleep(sec)
+ end
else
- # Windows
sleep(sec)
end
end
- def client_sleep(sec)
- return unless IO.select([ SELF_PIPE[0] ], nil, nil, sec)
- @signal = SELF_PIPE[0].getc.chr
- end
-
def unforked_interval_error_message
"Unforked chef-client interval runs are disabled in Chef 12." +
"\nConfiguration settings:" +
@@ -510,11 +534,18 @@ class Chef::Application::Client < Chef::Application
end
def fetch_recipe_tarball(url, path)
- Chef::Log.debug("Download recipes tarball from #{url} to #{path}")
- File.open(path, "wb") do |f|
- open(url) do |r|
- f.write(r.read)
+ Chef::Log.trace("Download recipes tarball from #{url} to #{path}")
+ if url =~ URI.regexp
+ File.open(path, "wb") do |f|
+ open(url) do |r|
+ f.write(r.read)
+ end
end
+ elsif File.exist?(url)
+ FileUtils.cp(url, path)
+ else
+ Chef::Application.fatal! "You specified --recipe-url but the value is neither a valid URL nor a path to a file that exists on disk." +
+ "Please confirm the location of the tarball and try again."
end
end
end
diff --git a/lib/chef/application/exit_code.rb b/lib/chef/application/exit_code.rb
new file mode 100644
index 0000000000..917aa16e62
--- /dev/null
+++ b/lib/chef/application/exit_code.rb
@@ -0,0 +1,164 @@
+#
+# Author:: Steven Murawski (<smurawski@chef.io>)
+# Copyright:: Copyright 2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ class Application
+
+ # These are the exit codes defined in Chef RFC 062
+ # https://github.com/chef/chef-rfc/blob/master/rfc062-exit-status.md
+ class ExitCode
+
+ # -1 is defined as DEPRECATED_FAILURE in RFC 062, so it is
+ # not enumerated in an active constant.
+ #
+ VALID_RFC_062_EXIT_CODES = {
+ SUCCESS: 0,
+ GENERIC_FAILURE: 1,
+ SIGINT_RECEIVED: 2,
+ SIGTERM_RECEIVED: 3,
+ REBOOT_SCHEDULED: 35,
+ REBOOT_NEEDED: 37,
+ REBOOT_FAILED: 41,
+ AUDIT_MODE_FAILURE: 42,
+ CLIENT_UPGRADED: 213,
+ }
+
+ DEPRECATED_RFC_062_EXIT_CODES = {
+ DEPRECATED_FAILURE: -1,
+ }
+
+ class << self
+
+ def normalize_exit_code(exit_code = nil)
+ normalized_exit_code = normalize_legacy_exit_code(exit_code)
+ if valid_exit_codes.include? normalized_exit_code
+ normalized_exit_code
+ else
+ Chef::Log.warn(non_standard_exit_code_warning(normalized_exit_code))
+ VALID_RFC_062_EXIT_CODES[:GENERIC_FAILURE]
+ end
+ end
+
+ private
+
+ def normalize_legacy_exit_code(exit_code)
+ case exit_code
+ when Integer
+ exit_code
+ when Exception
+ lookup_exit_code_by_exception(exit_code)
+ else
+ VALID_RFC_062_EXIT_CODES[:GENERIC_FAILURE]
+ end
+ end
+
+ def lookup_exit_code_by_exception(exception)
+ if sigint_received?(exception)
+ VALID_RFC_062_EXIT_CODES[:SIGINT_RECEIVED]
+ elsif sigterm_received?(exception)
+ VALID_RFC_062_EXIT_CODES[:SIGTERM_RECEIVED]
+ elsif reboot_scheduled?(exception)
+ VALID_RFC_062_EXIT_CODES[:REBOOT_SCHEDULED]
+ elsif reboot_needed?(exception)
+ VALID_RFC_062_EXIT_CODES[:REBOOT_NEEDED]
+ elsif reboot_failed?(exception)
+ VALID_RFC_062_EXIT_CODES[:REBOOT_FAILED]
+ elsif audit_failure?(exception)
+ VALID_RFC_062_EXIT_CODES[:AUDIT_MODE_FAILURE]
+ elsif client_upgraded?(exception)
+ VALID_RFC_062_EXIT_CODES[:CLIENT_UPGRADED]
+ else
+ VALID_RFC_062_EXIT_CODES[:GENERIC_FAILURE]
+ end
+ end
+
+ def reboot_scheduled?(exception)
+ resolve_exception_array(exception).any? do |e|
+ e.is_a? Chef::Exceptions::Reboot
+ end
+ end
+
+ def reboot_needed?(exception)
+ resolve_exception_array(exception).any? do |e|
+ e.is_a? Chef::Exceptions::RebootPending
+ end
+ end
+
+ def reboot_failed?(exception)
+ resolve_exception_array(exception).any? do |e|
+ e.is_a? Chef::Exceptions::RebootFailed
+ end
+ end
+
+ def audit_failure?(exception)
+ resolve_exception_array(exception).any? do |e|
+ e.is_a? Chef::Exceptions::AuditError
+ end
+ end
+
+ def client_upgraded?(exception)
+ resolve_exception_array(exception).any? do |e|
+ e.is_a? Chef::Exceptions::ClientUpgraded
+ end
+ end
+
+ def sigint_received?(exception)
+ resolve_exception_array(exception).any? do |e|
+ e.is_a? Chef::Exceptions::SigInt
+ end
+ end
+
+ def sigterm_received?(exception)
+ resolve_exception_array(exception).any? do |e|
+ e.is_a? Chef::Exceptions::SigTerm
+ end
+ end
+
+ def resolve_exception_array(exception)
+ exception_array = [exception]
+ if exception.respond_to?(:wrapped_errors)
+ exception.wrapped_errors.each do |e|
+ exception_array.push e
+ end
+ end
+ exception_array
+ end
+
+ def valid_exit_codes
+ VALID_RFC_062_EXIT_CODES.values
+ end
+
+ def notify_on_deprecation(message)
+ Chef.deprecated(:exit_code, message)
+ rescue Chef::Exceptions::DeprecatedFeatureError
+ # Have to rescue this, otherwise this unhandled error preempts
+ # the current exit code assignment.
+ end
+
+ def non_standard_exit_code_warning(exit_code)
+ "Chef attempted to exit with a non-standard exit code of #{exit_code}." \
+ " Chef RFC 062 (https://github.com/chef/chef-rfc/blob/master/rfc062-exit-status.md) defines the" \
+ " exit codes that should be used with Chef. Chef::Application::ExitCode defines valid exit codes" \
+ " Non-standard exit codes are redefined as GENERIC_FAILURE."
+ end
+
+ end
+ end
+
+ end
+end
diff --git a/lib/chef/application/knife.rb b/lib/chef/application/knife.rb
index 34598574dd..c972e9313e 100644
--- a/lib/chef/application/knife.rb
+++ b/lib/chef/application/knife.rb
@@ -33,6 +33,14 @@ class Chef::Application::Knife < Chef::Application
:description => "The configuration file to use",
:proc => lambda { |path| File.expand_path(path, Dir.pwd) }
+ option :config_option,
+ :long => "--config-option OPTION=VALUE",
+ :description => "Override a single configuration option",
+ :proc => lambda { |option, existing|
+ (existing ||= []) << option
+ existing
+ }
+
verbosity_level = 0
option :verbosity,
:short => "-V",
@@ -124,7 +132,7 @@ class Chef::Application::Knife < Chef::Application
option :listen,
:long => "--[no-]listen",
:description => "Whether a local mode (-z) server binds to a port",
- :boolean => true
+ :boolean => false
option :version,
:short => "-v",
@@ -140,6 +148,10 @@ class Chef::Application::Knife < Chef::Application
:boolean => true,
:default => nil
+ option :profile,
+ :long => "--profile PROFILE",
+ :description => "The credentials profile to select"
+
# Run knife
def run
Mixlib::Log::Formatter.show_time = false
@@ -195,11 +207,11 @@ class Chef::Application::Knife < Chef::Application
Chef::Log.error(fatal_message) if fatal_message
begin
- self.parse_options
+ parse_options
rescue OptionParser::InvalidOption => e
puts "#{e}\n"
end
- puts self.opt_parser
+ puts opt_parser
puts
Chef::Knife.list_commands
exit exitcode
diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb
index a39546092e..646d68182c 100644
--- a/lib/chef/application/solo.rb
+++ b/lib/chef/application/solo.rb
@@ -1,7 +1,7 @@
#
# Author:: AJ Christensen (<aj@chef.io>)
# Author:: Mark Mzyk (mmzyk@chef.io)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,16 +18,17 @@
require "chef"
require "chef/application"
+require "chef/application/client"
require "chef/client"
require "chef/config"
require "chef/daemon"
require "chef/log"
-require "chef/rest"
require "chef/config_fetcher"
require "fileutils"
require "chef/mixin/shell_out"
require "pathname"
require "chef-config/mixin/dot_d"
+require "mixlib/archive"
class Chef::Application::Solo < Chef::Application
include Chef::Mixin::ShellOut
@@ -39,6 +40,14 @@ class Chef::Application::Solo < Chef::Application
:default => Chef::Config.platform_specific_path("/etc/chef/solo.rb"),
:description => "The configuration file to use"
+ option :config_option,
+ :long => "--config-option OPTION=VALUE",
+ :description => "Override a single configuration option",
+ :proc => lambda { |option, existing|
+ (existing ||= []) << option
+ existing
+ }
+
option :formatter,
:short => "-F FORMATTER",
:long => "--format FORMATTER",
@@ -72,7 +81,7 @@ class Chef::Application::Solo < Chef::Application
option :log_level,
:short => "-l LEVEL",
:long => "--log_level LEVEL",
- :description => "Set the log level (debug, info, warn, error, fatal)",
+ :description => "Set the log level (trace, debug, info, warn, error, fatal)",
:proc => lambda { |l| l.to_sym }
option :log_location,
@@ -156,18 +165,17 @@ class Chef::Application::Solo < Chef::Application
:short => "-o RunlistItem,RunlistItem...",
:long => "--override-runlist RunlistItem,RunlistItem...",
:description => "Replace current run list with specified items",
- :proc => lambda {|items|
+ :proc => lambda { |items|
items = items.split(",")
- items.compact.map {|item|
+ items.compact.map do |item|
Chef::RunList::RunListItem.new(item)
- }
+ end
}
option :client_fork,
:short => "-f",
:long => "--[no-]fork",
- :description => "Fork client",
- :boolean => true
+ :description => "Fork client"
option :why_run,
:short => "-W",
@@ -200,10 +208,24 @@ class Chef::Application::Solo < Chef::Application
:description => "DANGEROUS: does what it says, only useful with --recipe-url",
:boolean => true
+ option :solo_legacy_mode,
+ :long => "--legacy-mode",
+ :description => "Run chef-solo in legacy mode",
+ :boolean => true
+
attr_reader :chef_client_json
- def initialize
- super
+ # Get this party started
+ def run
+ setup_signal_handlers
+ reconfigure
+ for_ezra if Chef::Config[:ez]
+ if !Chef::Config[:solo_legacy_mode]
+ Chef::Application::Client.new.run
+ else
+ setup_application
+ run_application
+ end
end
def reconfigure
@@ -215,28 +237,49 @@ class Chef::Application::Solo < Chef::Application
Chef::Config[:solo] = true
+ if !Chef::Config[:solo_legacy_mode]
+ # Because we re-parse ARGV when we move to chef-client, we need to tidy up some options first.
+ ARGV.delete("--ez")
+
+ # For back compat reasons, we need to ensure that we try and use the cache_path as a repo first
+ Chef::Log.trace "Current chef_repo_path is #{Chef::Config.chef_repo_path}"
+
+ if !Chef::Config.has_key?(:cookbook_path) && !Chef::Config.has_key?(:chef_repo_path)
+ Chef::Config.chef_repo_path = Chef::Config.find_chef_repo_path(Chef::Config[:cache_path])
+ end
+
+ Chef::Config[:local_mode] = true
+ Chef::Config[:listen] = false
+ else
+ configure_legacy_mode!
+ end
+ end
+
+ def configure_legacy_mode!
if Chef::Config[:daemonize]
Chef::Config[:interval] ||= 1800
end
- Chef::Application.fatal!(unforked_interval_error_message) if !Chef::Config[:client_fork] && Chef::Config[:interval]
+ # supervisor processes are enabled by default for interval-running processes but not for one-shot runs
+ if Chef::Config[:client_fork].nil?
+ Chef::Config[:client_fork] = !!Chef::Config[:interval]
+ end
- Chef::Log.deprecation("-r MUST be changed to --recipe-url, the -r option will be changed in Chef 13.0") if ARGV.include?("-r")
+ Chef::Application.fatal!(unforked_interval_error_message) if !Chef::Config[:client_fork] && Chef::Config[:interval]
if Chef::Config[:recipe_url]
cookbooks_path = Array(Chef::Config[:cookbook_path]).detect { |e| Pathname.new(e).cleanpath.to_s =~ /\/cookbooks\/*$/ }
recipes_path = File.expand_path(File.join(cookbooks_path, ".."))
if Chef::Config[:delete_entire_chef_repo]
- Chef::Log.debug "Cleanup path #{recipes_path} before extract recipes into it"
+ Chef::Log.trace "Cleanup path #{recipes_path} before extract recipes into it"
FileUtils.rm_rf(recipes_path, :secure => true)
end
- Chef::Log.debug "Creating path #{recipes_path} to extract recipes into"
+ Chef::Log.trace "Creating path #{recipes_path} to extract recipes into"
FileUtils.mkdir_p(recipes_path)
tarball_path = File.join(recipes_path, "recipes.tgz")
fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path)
- result = shell_out!("tar zxvf #{tarball_path} -C #{recipes_path}")
- Chef::Log.debug "#{result.stdout}"
+ Mixlib::Archive.new(tarball_path).extract(Chef::Config.chef_repo_path, perms: false, ignore: /^\.$/)
end
# json_attribs shuld be fetched after recipe_url tarball is unpacked.
@@ -255,7 +298,6 @@ class Chef::Application::Solo < Chef::Application
end
def run_application
- for_ezra if Chef::Config[:ez]
if !Chef::Config[:client_fork] || Chef::Config[:once]
# Run immediately without interval sleep or splay
begin
@@ -263,7 +305,7 @@ class Chef::Application::Solo < Chef::Application
rescue SystemExit
raise
rescue Exception => e
- Chef::Application.fatal!("#{e.class}: #{e.message}", 1)
+ Chef::Application.fatal!("#{e.class}: #{e.message}", e)
end
else
interval_run_chef_client
@@ -294,7 +336,7 @@ EOH
sleep_sec += rand(Chef::Config[:splay]) if Chef::Config[:splay]
sleep_sec += Chef::Config[:interval] if Chef::Config[:interval]
if sleep_sec != 0
- Chef::Log.debug("Sleeping for #{sleep_sec} seconds")
+ Chef::Log.trace("Sleeping for #{sleep_sec} seconds")
sleep(sleep_sec)
end
@@ -307,17 +349,17 @@ EOH
rescue Exception => e
if Chef::Config[:interval]
Chef::Log.error("#{e.class}: #{e}")
- Chef::Log.debug("#{e.class}: #{e}\n#{e.backtrace.join("\n")}")
+ Chef::Log.trace("#{e.class}: #{e}\n#{e.backtrace.join("\n")}")
retry
else
- Chef::Application.fatal!("#{e.class}: #{e.message}", 1)
+ Chef::Application.fatal!("#{e.class}: #{e.message}", e)
end
end
end
end
def fetch_recipe_tarball(url, path)
- Chef::Log.debug("Download recipes tarball from #{url} to #{path}")
+ Chef::Log.trace("Download recipes tarball from #{url} to #{path}")
File.open(path, "wb") do |f|
open(url) do |r|
f.write(r.read)
diff --git a/lib/chef/application/windows_service.rb b/lib/chef/application/windows_service.rb
index fca1ed3689..41dcc55d70 100644
--- a/lib/chef/application/windows_service.rb
+++ b/lib/chef/application/windows_service.rb
@@ -1,6 +1,6 @@
#
# Author:: Christopher Maier (<maier@lambda.local>)
-# Copyright:: Copyright 2011-2016, Chef Software, Inc.
+# Copyright:: Copyright 2011-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -109,9 +109,9 @@ class Chef
# Daemon class needs to have all the signal callbacks return
# before service_main returns.
- Chef::Log.debug("Giving signal callbacks some time to exit...")
+ Chef::Log.trace("Giving signal callbacks some time to exit...")
sleep 1
- Chef::Log.debug("Exiting service...")
+ Chef::Log.trace("Exiting service...")
end
################################################################################
@@ -135,7 +135,7 @@ class Chef
run_warning_displayed = true
end
- Chef::Log.debug("Waiting for chef-client run...")
+ Chef::Log.trace("Waiting for chef-client run...")
sleep 1
end
end
@@ -183,39 +183,38 @@ class Chef
# The chef client will be started in a new process. We have used shell_out to start the chef-client.
# The log_location and config_file of the parent process is passed to the new chef-client process.
# We need to add the --no-fork, as by default it is set to fork=true.
- begin
- Chef::Log.info "Starting chef-client in a new process"
- # Pass config params to the new process
- config_params = " --no-fork"
- config_params += " -c #{Chef::Config[:config_file]}" unless Chef::Config[:config_file].nil?
- # log_location might be an event logger and if so we cannot pass as a command argument
- # but shed no tears! If the logger is an event logger, it must have been configured
- # as such in the config file and chef-client will use that when no arg is passed here
- config_params += " -L #{resolve_log_location}" if resolve_log_location.is_a?(String)
-
- # Starts a new process and waits till the process exits
-
- result = shell_out(
- "chef-client.bat #{config_params}",
- :timeout => Chef::Config[:windows_service][:watchdog_timeout],
- :logger => Chef::Log
- )
- Chef::Log.debug "#{result.stdout}"
- Chef::Log.debug "#{result.stderr}"
- rescue Mixlib::ShellOut::CommandTimeout => e
- Chef::Log.error "chef-client timed out\n(#{e})"
- Chef::Log.error(<<-EOF)
+
+ Chef::Log.info "Starting chef-client in a new process"
+ # Pass config params to the new process
+ config_params = " --no-fork"
+ config_params += " -c #{Chef::Config[:config_file]}" unless Chef::Config[:config_file].nil?
+ # log_location might be an event logger and if so we cannot pass as a command argument
+ # but shed no tears! If the logger is an event logger, it must have been configured
+ # as such in the config file and chef-client will use that when no arg is passed here
+ config_params += " -L #{resolve_log_location}" if resolve_log_location.is_a?(String)
+
+ # Starts a new process and waits till the process exits
+
+ result = shell_out(
+ "chef-client.bat #{config_params}",
+ :timeout => Chef::Config[:windows_service][:watchdog_timeout],
+ :logger => Chef::Log
+ )
+ Chef::Log.trace "#{result.stdout}"
+ Chef::Log.trace "#{result.stderr}"
+ rescue Mixlib::ShellOut::CommandTimeout => e
+ Chef::Log.error "chef-client timed out\n(#{e})"
+ Chef::Log.error(<<-EOF)
Your chef-client run timed out. You can increase the time chef-client is given
to complete by configuring windows_service.watchdog_timeout in your client.rb.
EOF
- rescue Mixlib::ShellOut::ShellCommandFailed => e
- Chef::Log.warn "Not able to start chef-client in new process (#{e})"
- rescue => e
- Chef::Log.error e
- ensure
- # Once process exits, we log the current process' pid
- Chef::Log.info "Child process exited (pid: #{Process.pid})"
- end
+ rescue Mixlib::ShellOut::ShellCommandFailed => e
+ Chef::Log.warn "Not able to start chef-client in new process (#{e})"
+ rescue => e
+ Chef::Log.error e
+ ensure
+ # Once process exits, we log the current process' pid
+ Chef::Log.info "Child process exited (pid: #{Process.pid})"
end
def apply_config(config_file_path)
@@ -257,13 +256,13 @@ class Chef
# Based on config and whether or not STDOUT is a tty, should we setup a
# secondary logger for stdout?
def want_additional_logger?
- ( Chef::Config[:log_location] != STDOUT ) && STDOUT.tty? && (!Chef::Config[:daemonize]) && (Chef::Config[:force_logger])
+ ( Chef::Config[:log_location] != STDOUT ) && STDOUT.tty? && !Chef::Config[:daemonize]
end
# Use of output formatters is assumed if `force_formatter` is set or if
- # `force_logger` is not set and STDOUT is to a console (tty)
+ # `force_logger` is not set
def using_output_formatter?
- Chef::Config[:force_formatter] || (!Chef::Config[:force_logger] && STDOUT.tty?)
+ Chef::Config[:force_formatter] || !Chef::Config[:force_logger]
end
def auto_log_level?
@@ -319,11 +318,11 @@ class Chef
Chef::Config.merge!(config)
rescue SocketError
- Chef::Application.fatal!("Error getting config file #{Chef::Config[:config_file]}", 2)
+ Chef::Application.fatal!("Error getting config file #{Chef::Config[:config_file]}")
rescue Chef::Exceptions::ConfigurationError => error
- Chef::Application.fatal!("Error processing config file #{Chef::Config[:config_file]} with error #{error.message}", 2)
+ Chef::Application.fatal!("Error processing config file #{Chef::Config[:config_file]} with error #{error.message}")
rescue Exception => error
- Chef::Application.fatal!("Unknown error processing config file #{Chef::Config[:config_file]} with error #{error.message}", 2)
+ Chef::Application.fatal!("Unknown error processing config file #{Chef::Config[:config_file]} with error #{error.message}")
end
end
diff --git a/lib/chef/application/windows_service_manager.rb b/lib/chef/application/windows_service_manager.rb
index 6f81dccc67..5c572bc260 100644
--- a/lib/chef/application/windows_service_manager.rb
+++ b/lib/chef/application/windows_service_manager.rb
@@ -126,10 +126,12 @@ class Chef
:password => @password,
:dependencies => @dependencies
)
- ::Win32::Service.configure(
- :service_name => @service_name,
- :delayed_start => @delayed_start
- ) unless @delayed_start.nil?
+ unless @delayed_start.nil?
+ ::Win32::Service.configure(
+ :service_name => @service_name,
+ :delayed_start => @delayed_start
+ )
+ end
puts "Service '#{@service_name}' has successfully been installed."
end
when "status"
@@ -166,7 +168,7 @@ class Chef
PAUSED = "paused"
def service_exists?
- return ::Win32::Service.exists?(@service_name)
+ ::Win32::Service.exists?(@service_name)
end
def take_action(action = nil, desired_state = nil)
diff --git a/lib/chef/audit/audit_event_proxy.rb b/lib/chef/audit/audit_event_proxy.rb
index c4d67fa8f4..e2e6ab1ede 100644
--- a/lib/chef/audit/audit_event_proxy.rb
+++ b/lib/chef/audit/audit_event_proxy.rb
@@ -37,7 +37,7 @@ class Chef
if notification.group.parent_groups.size == 1
# top level `control_group` block
desc = notification.group.description
- Chef::Log.debug("Entered `control_group` block named #{desc}")
+ Chef::Log.trace("Entered `control_group` block named #{desc}")
events.control_group_started(desc)
end
end
@@ -78,14 +78,14 @@ class Chef
# We know all of our examples each live in a top-level `control_group` block - get this name now
outermost_group_desc = describe_groups.shift
- return outermost_group_desc, {
+ [outermost_group_desc, {
:name => example.description,
:desc => example.full_description,
:resource_type => resource_type,
:resource_name => resource_name,
:context => describe_groups,
:line_number => example.metadata[:line_number],
- }
+ }]
end
end
diff --git a/lib/chef/audit/audit_reporter.rb b/lib/chef/audit/audit_reporter.rb
index a40cae93dd..bb5537b97b 100644
--- a/lib/chef/audit/audit_reporter.rb
+++ b/lib/chef/audit/audit_reporter.rb
@@ -1,7 +1,7 @@
#
# Author:: Tyler Ball (<tball@chef.io>)
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -42,14 +42,14 @@ class Chef
end
def audit_phase_start(run_status)
- Chef::Log.debug("Audit Reporter starting")
+ Chef::Log.trace("Audit Reporter starting")
@audit_data = AuditData.new(run_status.node.name, run_status.run_id)
@run_status = run_status
end
def audit_phase_complete(audit_output)
- Chef::Log.debug("Audit Reporter completed successfully without errors.")
- ordered_control_groups.each do |name, control_group|
+ Chef::Log.trace("Audit Reporter completed successfully without errors.")
+ ordered_control_groups.each_value do |control_group|
audit_data.add_control_group(control_group)
end
end
@@ -61,8 +61,8 @@ class Chef
def audit_phase_failed(error, audit_output)
# The stacktrace information has already been logged elsewhere
@audit_phase_error = error
- Chef::Log.debug("Audit Reporter failed.")
- ordered_control_groups.each do |name, control_group|
+ Chef::Log.trace("Audit Reporter failed.")
+ ordered_control_groups.each_value do |control_group|
audit_data.add_control_group(control_group)
end
end
@@ -104,12 +104,12 @@ class Chef
def post_auditing_data
unless auditing_enabled?
- Chef::Log.debug("Audit Reports are disabled. Skipping sending reports.")
+ Chef::Log.trace("Audit Reports are disabled. Skipping sending reports.")
return
end
unless run_status
- Chef::Log.debug("Run failed before audit mode was initialized, not sending audit report to server")
+ Chef::Log.trace("Run failed before audit mode was initialized, not sending audit report to server")
return
end
@@ -117,7 +117,7 @@ class Chef
audit_data.end_time = iso8601ify(run_status.end_time)
audit_history_url = "controls"
- Chef::Log.debug("Sending audit report (run-id: #{audit_data.run_id})")
+ Chef::Log.trace("Sending audit report (run-id: #{audit_data.run_id})")
run_data = audit_data.to_hash
if @audit_phase_error
@@ -126,7 +126,7 @@ class Chef
run_data[:error] = error_info
end
- Chef::Log.debug "Audit Report:\n#{Chef::JSONCompat.to_json_pretty(run_data)}"
+ Chef::Log.trace "Audit Report:\n#{Chef::JSONCompat.to_json_pretty(run_data)}"
begin
rest_client.post(audit_history_url, run_data, headers)
rescue StandardError => e
@@ -134,13 +134,17 @@ class Chef
# 404 error code is OK. This means the version of server we're running against doesn't support
# audit reporting. Don't alarm failure in this case.
if e.response.code == "404"
- Chef::Log.debug("Server doesn't support audit reporting. Skipping report.")
+ Chef::Log.trace("Server doesn't support audit reporting. Skipping report.")
return
else
# Save the audit report to local disk
error_file = "failed-audit-data.json"
Chef::FileCache.store(error_file, Chef::JSONCompat.to_json_pretty(run_data), 0640)
- Chef::Log.error("Failed to post audit report to server. Saving report to #{Chef::FileCache.load(error_file, false)}")
+ if Chef::Config.chef_zero.enabled
+ Chef::Log.trace("Saving audit report to #{Chef::FileCache.load(error_file, false)}")
+ else
+ Chef::Log.error("Failed to post audit report to server. Saving report to #{Chef::FileCache.load(error_file, false)}")
+ end
end
else
Chef::Log.error("Failed to post audit report to server (#{e})")
diff --git a/lib/chef/audit/control_group_data.rb b/lib/chef/audit/control_group_data.rb
index 4dffbdf3dd..476ebe95e9 100644
--- a/lib/chef/audit/control_group_data.rb
+++ b/lib/chef/audit/control_group_data.rb
@@ -118,7 +118,7 @@ class Chef
def initialize(control_data = {})
control_data.each do |k, v|
- self.instance_variable_set("@#{k}", v)
+ instance_variable_set("@#{k}", v)
end
end
diff --git a/lib/chef/audit/runner.rb b/lib/chef/audit/runner.rb
index 100a72d2e1..f6fed3fc64 100644
--- a/lib/chef/audit/runner.rb
+++ b/lib/chef/audit/runner.rb
@@ -1,6 +1,6 @@
#
# Author:: Claire McQuin (<claire@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -163,9 +163,9 @@ class Chef
# or use example group filters.
def register_control_groups
add_example_group_methods
- run_context.audits.each do |name, group|
+ run_context.audits.each do |name, group| # rubocop:disable Performance/HashEachMethods
ctl_grp = RSpec::Core::ExampleGroup.__control_group__(*group.args, &group.block)
- RSpec.world.register(ctl_grp)
+ RSpec.world.record(ctl_grp)
end
end
diff --git a/lib/chef/blacklist.rb b/lib/chef/blacklist.rb
new file mode 100644
index 0000000000..a49c318295
--- /dev/null
+++ b/lib/chef/blacklist.rb
@@ -0,0 +1,81 @@
+
+require "chef/exceptions"
+
+class Chef
+ class Blacklist
+
+ # filter takes two arguments - the data you want to filter, and a blacklisted array
+ # of keys you want discluded. You can capture a subtree of the data to filter by
+ # providing a "/"-delimited string of keys. If some key includes "/"-characters,
+ # you must provide an array of keys instead.
+ #
+ # Blacklist.filter(
+ # { "filesystem" => {
+ # "/dev/disk" => {
+ # "size" => "10mb"
+ # },
+ # "map - autohome" => {
+ # "size" => "10mb"
+ # }
+ # },
+ # "network" => {
+ # "interfaces" => {
+ # "eth0" => {...},
+ # "eth1" => {...}
+ # }
+ # }
+ # },
+ # ["network/interfaces/eth0", ["filesystem", "/dev/disk"]])
+ # will exclude the eth0 and /dev/disk subtrees.
+ def self.filter(data, blacklist = nil)
+ return data if blacklist.nil?
+
+ blacklist.each do |item|
+ Chef::Log.warn("Removing item #{item}")
+ remove_data(data, item)
+ end
+ data
+ end
+
+ # Walk the data according to the keys provided by the blacklisted item
+ # to get a reference to the item that will be removed.
+ def self.remove_data(data, item)
+ parts = to_array(item)
+
+ item_ref = data
+ parts[0..-2].each do |part|
+ unless item_ref[part]
+ Chef::Log.warn("Could not find blacklist attribute #{item}.")
+ return nil
+ end
+
+ item_ref = item_ref[part]
+ end
+
+ unless item_ref.key?(parts[-1])
+ Chef::Log.warn("Could not find blacklist attribute #{item}.")
+ return nil
+ end
+
+ item_ref.delete(parts[-1])
+ data
+ end
+
+ private_class_method :remove_data
+
+ # Accepts a String or an Array, and returns an Array of String keys that
+ # are used to traverse the data hash. Strings are split on "/", Arrays are
+ # assumed to contain exact keys (that is, Array elements will not be split
+ # by "/").
+ def self.to_array(item)
+ return item if item.kind_of? Array
+
+ parts = item.split("/")
+ parts.shift if !parts.empty? && parts[0].empty?
+ parts
+ end
+
+ private_class_method :to_array
+
+ end
+end
diff --git a/lib/chef/chef_class.rb b/lib/chef/chef_class.rb
index f019448bd8..bfea51609a 100644
--- a/lib/chef/chef_class.rb
+++ b/lib/chef/chef_class.rb
@@ -30,6 +30,9 @@ require "chef/platform/provider_priority_map"
require "chef/platform/resource_priority_map"
require "chef/platform/provider_handler_map"
require "chef/platform/resource_handler_map"
+require "chef/deprecated"
+require "chef/event_dispatch/dsl"
+require "chef/deprecated"
class Chef
class << self
@@ -197,26 +200,35 @@ class Chef
#
# Emit a deprecation message.
#
- # @param message The message to send.
+ # @param [Symbol] type The message to send. This should refer to a class
+ # defined in Chef::Deprecated
+ # @param message An explicit message to display, rather than the generic one
+ # associated with the deprecation.
# @param location The location. Defaults to the caller who called you (since
# generally the person who triggered the check is the one that needs to be
# fixed).
#
# @example
- # Chef.deprecation("Deprecated!")
+ # Chef.deprecated(:my_deprecation, message: "This is deprecated!")
#
# @api private this will likely be removed in favor of an as-yet unwritten
# `Chef.log`
- def log_deprecation(message, location = nil)
+ def deprecated(type, message, location = nil)
location ||= Chef::Log.caller_location
+ deprecation = Chef::Deprecated.create(type, message, location)
# `run_context.events` is the primary deprecation target if we're in a
# run. If we are not yet in a run, print to `Chef::Log`.
if run_context && run_context.events
- run_context.events.deprecation(message, location)
+ run_context.events.deprecation(deprecation, location)
else
- Chef::Log.deprecation(message, location)
+ Chef::Log.deprecation(deprecation, location)
end
end
+
+ def log_deprecation(message, location = nil)
+ location ||= Chef::Log.caller_location
+ Chef.deprecated(:generic, message, location)
+ end
end
# @api private Only for test dependency injection; not evenly implemented as yet.
diff --git a/lib/chef/chef_fs/chef_fs_data_store.rb b/lib/chef/chef_fs/chef_fs_data_store.rb
index aa5a6d5a69..697c2db232 100644
--- a/lib/chef/chef_fs/chef_fs_data_store.rb
+++ b/lib/chef/chef_fs/chef_fs_data_store.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright 2012-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -326,7 +326,7 @@ class Chef
else
relative << path[1]
end
- relative = relative + file[:path].split("/")
+ relative += file[:path].split("/")
file["url"] = ChefZero::RestBase.build_uri(request.base_uri, relative)
end
end
@@ -458,6 +458,7 @@ class Chef
# We want to delete just the ones that == POLICY
next unless policy.name.rpartition("-")[0] == path[1]
policy.delete(false)
+ FileSystemCache.instance.delete!(policy.file_path)
found_policy = true
end
raise ChefZero::DataStore::DataNotFoundError.new(path) if !found_policy
@@ -610,7 +611,7 @@ class Chef
private
def use_memory_store?(path)
- return path[0] == "sandboxes" || path[0] == "file_store" && path[1] == "checksums" || path == %w{environments _default}
+ path[0] == "sandboxes" || path[0] == "file_store" && path[1] == "checksums" || path == %w{environments _default}
end
def write_cookbook(path, data, *options)
@@ -767,7 +768,7 @@ class Chef
end
elsif path.length == 2 && path[0] != "cookbooks"
- path[1] = path[1][0..-6]
+ path[1] = path[1].gsub(/\.(rb|json)/, "")
end
path
@@ -778,15 +779,13 @@ class Chef
end
def path_always_exists?(path)
- return path.length == 1 && BASE_DIRNAMES.include?(path[0])
+ path.length == 1 && BASE_DIRNAMES.include?(path[0])
end
def with_entry(path)
- begin
- yield Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path))
- rescue Chef::ChefFS::FileSystem::NotFoundError => e
- raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
- end
+ yield Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path))
+ rescue Chef::ChefFS::FileSystem::NotFoundError => e
+ raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
end
def with_parent_dir(path, *options)
diff --git a/lib/chef/chef_fs/command_line.rb b/lib/chef/chef_fs/command_line.rb
index 4a9a712c20..5f449bad04 100644
--- a/lib/chef/chef_fs/command_line.rb
+++ b/lib/chef/chef_fs/command_line.rb
@@ -63,6 +63,13 @@ class Chef
end
when :deleted
+ # This is kind of a kludge - because the "new" entry isn't there, we can't predict
+ # it's true file name, because we've not got enough information. So because we know
+ # the two entries really ought to have the same extension, we'll just grab the old one
+ # and use it. (This doesn't affect cookbook files, since they'll always have extensions)
+ if File.extname(old_path) != File.extname(new_path)
+ new_path += File.extname(old_path)
+ end
next if diff_filter && diff_filter !~ /D/
if output_mode == :name_only
yield "#{new_path}\n"
@@ -139,38 +146,38 @@ class Chef
if old_entry.dir?
if new_entry.dir?
if recurse_depth == 0
- return [ [ :common_subdirectories, old_entry, new_entry ] ]
+ [ [ :common_subdirectories, old_entry, new_entry ] ]
else
- return Chef::ChefFS::Parallelizer.parallelize(Chef::ChefFS::FileSystem.child_pairs(old_entry, new_entry)) do |old_child, new_child|
+ Chef::ChefFS::Parallelizer.parallelize(Chef::ChefFS::FileSystem.child_pairs(old_entry, new_entry)) do |old_child, new_child|
Chef::ChefFS::CommandLine.diff_entries(old_child, new_child, recurse_depth ? recurse_depth - 1 : nil, get_content)
end.flatten(1)
end
# If old is a directory and new is a file
elsif new_entry.exists?
- return [ [ :directory_to_file, old_entry, new_entry ] ]
+ [ [ :directory_to_file, old_entry, new_entry ] ]
# If old is a directory and new does not exist
elsif new_entry.parent.can_have_child?(old_entry.name, old_entry.dir?)
- return [ [ :deleted, old_entry, new_entry ] ]
+ [ [ :deleted, old_entry, new_entry ] ]
# If the new entry does not and *cannot* exist, report that.
else
- return [ [ :new_cannot_upload, old_entry, new_entry ] ]
+ [ [ :new_cannot_upload, old_entry, new_entry ] ]
end
# If new is a directory and old is a file
elsif new_entry.dir?
if old_entry.exists?
- return [ [ :file_to_directory, old_entry, new_entry ] ]
+ [ [ :file_to_directory, old_entry, new_entry ] ]
# If new is a directory and old does not exist
elsif old_entry.parent.can_have_child?(new_entry.name, new_entry.dir?)
- return [ [ :added, old_entry, new_entry ] ]
+ [ [ :added, old_entry, new_entry ] ]
# If the new entry does not and *cannot* exist, report that.
else
- return [ [ :old_cannot_upload, old_entry, new_entry ] ]
+ [ [ :old_cannot_upload, old_entry, new_entry ] ]
end
# Neither is a directory, so they are diffable with file diff
@@ -222,7 +229,7 @@ class Chef
end
end
- if old_value == :none || (old_value == nil && !old_entry.exists?)
+ if old_value == :none || (old_value.nil? && !old_entry.exists?)
return [ [ :added, old_entry, new_entry, old_value, new_value ] ]
elsif new_value == :none
return [ [ :deleted, old_entry, new_entry, old_value, new_value ] ]
@@ -232,33 +239,34 @@ class Chef
end
end
rescue Chef::ChefFS::FileSystem::FileSystemError => e
- return [ [ :error, old_entry, new_entry, nil, nil, e ] ]
+ [ [ :error, old_entry, new_entry, nil, nil, e ] ]
end
- private
+ class << self
+ private
- def self.sort_keys(json_object)
- if json_object.is_a?(Array)
- json_object.map { |o| sort_keys(o) }
- elsif json_object.is_a?(Hash)
- new_hash = {}
- json_object.keys.sort.each { |key| new_hash[key] = sort_keys(json_object[key]) }
- new_hash
- else
- json_object
+ def sort_keys(json_object)
+ if json_object.is_a?(Array)
+ json_object.map { |o| sort_keys(o) }
+ elsif json_object.is_a?(Hash)
+ new_hash = {}
+ json_object.keys.sort.each { |key| new_hash[key] = sort_keys(json_object[key]) }
+ new_hash
+ else
+ json_object
+ end
end
- end
- def self.canonicalize_json(json_text)
- parsed_json = Chef::JSONCompat.parse(json_text)
- sorted_json = sort_keys(parsed_json)
- Chef::JSONCompat.to_json_pretty(sorted_json)
- end
+ def canonicalize_json(json_text)
+ parsed_json = Chef::JSONCompat.parse(json_text)
+ sorted_json = sort_keys(parsed_json)
+ Chef::JSONCompat.to_json_pretty(sorted_json)
+ end
+
+ def diff_text(old_path, new_path, old_value, new_value)
+ # Copy to tempfiles before diffing
+ # TODO don't copy things that are already in files! Or find an in-memory diff algorithm
- def self.diff_text(old_path, new_path, old_value, new_value)
- # Copy to tempfiles before diffing
- # TODO don't copy things that are already in files! Or find an in-memory diff algorithm
- begin
new_tempfile = Tempfile.new("new")
new_tempfile.write(new_value)
new_tempfile.close
diff --git a/lib/chef/chef_fs/config.rb b/lib/chef/chef_fs/config.rb
index 6e3cc50ac1..0725426275 100644
--- a/lib/chef/chef_fs/config.rb
+++ b/lib/chef/chef_fs/config.rb
@@ -66,7 +66,7 @@ class Chef
# upgrade/migration of older Chef Servers, so they should be considered
# frozen in time.
- CHEF_11_OSS_STATIC_OBJECTS = %w{cookbooks cookbook_artifacts data_bags environments roles}.freeze
+ CHEF_11_OSS_STATIC_OBJECTS = %w{cookbooks data_bags environments roles}.freeze
CHEF_11_OSS_DYNAMIC_OBJECTS = %w{clients nodes users}.freeze
RBAC_OBJECT_NAMES = %w{acls containers groups }.freeze
CHEF_12_OBJECTS = %w{ cookbook_artifacts policies policy_groups client_keys }.freeze
@@ -242,7 +242,7 @@ class Chef
# Print the given server path, relative to the current directory
def format_path(entry)
- server_path = entry.path
+ server_path = entry.respond_to?(:display_path) ? entry.display_path : entry.path
if base_path && server_path[0, base_path.length] == base_path
if server_path == base_path
return "."
diff --git a/lib/chef/chef_fs/data_handler/acl_data_handler.rb b/lib/chef/chef_fs/data_handler/acl_data_handler.rb
index 6c8833004a..834fc5ca0b 100644
--- a/lib/chef/chef_fs/data_handler/acl_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/acl_data_handler.rb
@@ -13,7 +13,7 @@ class Chef
"delete" => {},
"grant" => {},
})
- result.keys.each do |key|
+ result.each_key do |key|
result[key] = normalize_hash(result[key], { "actors" => [], "groups" => [] })
result[key]["actors"] = result[key]["actors"].sort
result[key]["groups"] = result[key]["groups"].sort
diff --git a/lib/chef/chef_fs/data_handler/client_data_handler.rb b/lib/chef/chef_fs/data_handler/client_data_handler.rb
index 5e120035ac..6c01d8d54c 100644
--- a/lib/chef/chef_fs/data_handler/client_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/client_data_handler.rb
@@ -25,7 +25,7 @@ class Chef
end
def preserve_key?(key)
- return key == "name"
+ key == "name"
end
def chef_class
diff --git a/lib/chef/chef_fs/data_handler/container_data_handler.rb b/lib/chef/chef_fs/data_handler/container_data_handler.rb
index 04973b5135..a8bd5f084c 100644
--- a/lib/chef/chef_fs/data_handler/container_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/container_data_handler.rb
@@ -12,7 +12,7 @@ class Chef
end
def preserve_key?(key)
- return key == "containername"
+ key == "containername"
end
# Verify that the JSON hash for this type has a key that matches its name.
diff --git a/lib/chef/chef_fs/data_handler/cookbook_data_handler.rb b/lib/chef/chef_fs/data_handler/cookbook_data_handler.rb
index 156c1eef4e..2861c5d3eb 100644
--- a/lib/chef/chef_fs/data_handler/cookbook_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/cookbook_data_handler.rb
@@ -24,7 +24,7 @@ class Chef
end
def preserve_key?(key)
- return key == "cookbook_name" || key == "version"
+ key == "cookbook_name" || key == "version"
end
def chef_class
diff --git a/lib/chef/chef_fs/data_handler/data_bag_item_data_handler.rb b/lib/chef/chef_fs/data_handler/data_bag_item_data_handler.rb
index c6b6449d52..3b1fb53da6 100644
--- a/lib/chef/chef_fs/data_handler/data_bag_item_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/data_bag_item_data_handler.rb
@@ -5,6 +5,8 @@ class Chef
module ChefFS
module DataHandler
class DataBagItemDataHandler < DataHandlerBase
+ RESERVED_NAMES = /^(node|role|environment|client)$/
+
def normalize(data_bag_item, entry)
# If it's wrapped with raw_data, unwrap it.
if data_bag_item["json_class"] == "Chef::DataBagItem" && data_bag_item["raw_data"]
@@ -35,7 +37,7 @@ class Chef
end
def preserve_key?(key)
- return key == "id"
+ key == "id"
end
def chef_class
@@ -43,6 +45,7 @@ class Chef
end
# Verify that the JSON hash for this type has a key that matches its name.
+ # Also check that the data bag name is not a reserved search index name.
#
# @param object [Object] JSON hash of the object
# @param entry [Chef::ChefFS::FileSystem::BaseFSObject] filesystem object we are verifying
@@ -52,6 +55,8 @@ class Chef
base_name = remove_dot_json(entry.name)
if object["raw_data"]["id"] != base_name
yield("ID in #{entry.path_for_printing} must be '#{base_name}' (is '#{object['raw_data']['id']}')")
+ elsif entry.parent.name =~ RESERVED_NAMES
+ yield("Data bag name ('#{entry.parent.name}') must not match #{RESERVED_NAMES.inspect}")
end
end
diff --git a/lib/chef/chef_fs/data_handler/data_handler_base.rb b/lib/chef/chef_fs/data_handler/data_handler_base.rb
index b30ae9c708..4b4696ce5e 100644
--- a/lib/chef/chef_fs/data_handler/data_handler_base.rb
+++ b/lib/chef/chef_fs/data_handler/data_handler_base.rb
@@ -24,15 +24,14 @@ class Chef
object
end
- #
- # Takes a name like blah.json and removes the .json from it.
- #
- def remove_dot_json(name)
- if name.length < 5 || name[-5, 5] != ".json"
- raise "Invalid name #{path}: must end in .json"
+ def remove_file_extension(name, ext = ".*")
+ if %w{ .rb .json }.include?(File.extname(name))
+ File.basename(name, ext)
+ else
+ name
end
- name[0, name.length - 5]
end
+ alias_method :remove_dot_json, :remove_file_extension
#
# Return true if minimize() should preserve a key even if it is the same
@@ -57,8 +56,7 @@ class Chef
# 2. Put the actual values in the order of the defaults
# 3. Move any other values to the end
#
- # == Example
- #
+ # @example
# normalize_hash({x: 100, c: 2, a: 1}, { a: 10, b: 20, c: 30})
# -> { a: 1, b: 20, c: 2, x: 100}
#
@@ -94,7 +92,7 @@ class Chef
# name to recipe[name]. Then calls uniq on the result.
#
def normalize_run_list(run_list)
- run_list.map {|item|
+ run_list.map do |item|
case item.to_s
when /^recipe\[.*\]$/
item # explicit recipe
@@ -103,14 +101,16 @@ class Chef
else
"recipe[#{item}]"
end
- }.uniq
+ end.uniq
end
#
# Bring in an instance of this object from Ruby. (Like roles/x.rb)
#
- def from_ruby(ruby)
- chef_class.from_file(ruby).to_hash
+ def from_ruby(path)
+ r = chef_class.new
+ r.from_file(path)
+ r.to_hash
end
#
@@ -139,8 +139,7 @@ class Chef
# the keys specified in "keys"; anything else must be emitted by the
# caller.
#
- # == Example
- #
+ # @example
# to_ruby_keys({"name" => "foo", "environment" => "desert", "foo": "bar"}, [ "name", "environment" ])
# ->
# 'name "foo"
@@ -192,7 +191,7 @@ class Chef
# @yield [s] callback to handle errors
# @yieldparam [s<string>] error message
def verify_integrity(object, entry)
- base_name = remove_dot_json(entry.name)
+ base_name = remove_file_extension(entry.name)
if object["name"] != base_name
yield("Name must be '#{base_name}' (is '#{object['name']}')")
end
diff --git a/lib/chef/chef_fs/data_handler/environment_data_handler.rb b/lib/chef/chef_fs/data_handler/environment_data_handler.rb
index 8d066764be..ab8104b483 100644
--- a/lib/chef/chef_fs/data_handler/environment_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/environment_data_handler.rb
@@ -7,7 +7,7 @@ class Chef
class EnvironmentDataHandler < DataHandlerBase
def normalize(environment, entry)
normalize_hash(environment, {
- "name" => remove_dot_json(entry.name),
+ "name" => remove_file_extension(entry.name),
"description" => "",
"cookbook_versions" => {},
"default_attributes" => {},
@@ -18,7 +18,7 @@ class Chef
end
def preserve_key?(key)
- return key == "name"
+ key == "name"
end
def chef_class
diff --git a/lib/chef/chef_fs/data_handler/group_data_handler.rb b/lib/chef/chef_fs/data_handler/group_data_handler.rb
index 7f38784826..8daced2e2e 100644
--- a/lib/chef/chef_fs/data_handler/group_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/group_data_handler.rb
@@ -41,7 +41,7 @@ class Chef
end
def preserve_key?(key)
- return key == "name"
+ key == "name"
end
def chef_class
diff --git a/lib/chef/chef_fs/data_handler/node_data_handler.rb b/lib/chef/chef_fs/data_handler/node_data_handler.rb
index 36a7bf545b..a0087ec5d6 100644
--- a/lib/chef/chef_fs/data_handler/node_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/node_data_handler.rb
@@ -22,7 +22,7 @@ class Chef
end
def preserve_key?(key)
- return key == "name"
+ key == "name"
end
def chef_class
diff --git a/lib/chef/chef_fs/data_handler/organization_data_handler.rb b/lib/chef/chef_fs/data_handler/organization_data_handler.rb
index 0facd5d55d..0a697ab69a 100644
--- a/lib/chef/chef_fs/data_handler/organization_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/organization_data_handler.rb
@@ -16,7 +16,7 @@ class Chef
end
def preserve_key?(key)
- return key == "name"
+ key == "name"
end
# Verify that the JSON hash for this type has a key that matches its name.
diff --git a/lib/chef/chef_fs/data_handler/organization_invites_data_handler.rb b/lib/chef/chef_fs/data_handler/organization_invites_data_handler.rb
index c5a5f873c5..7ae08823b8 100644
--- a/lib/chef/chef_fs/data_handler/organization_invites_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/organization_invites_data_handler.rb
@@ -5,7 +5,7 @@ class Chef
module DataHandler
class OrganizationInvitesDataHandler < DataHandlerBase
def normalize(invites, entry)
- invites.map { |invite| invite.is_a?(Hash) ? invite["username"] : invite }.sort.uniq
+ invites.map { |invite| invite.is_a?(Hash) ? invite["username"] : invite }.compact.sort.uniq
end
def minimize(invites, entry)
diff --git a/lib/chef/chef_fs/data_handler/policy_data_handler.rb b/lib/chef/chef_fs/data_handler/policy_data_handler.rb
index fa7bbe9101..91c59f2a0b 100644
--- a/lib/chef/chef_fs/data_handler/policy_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/policy_data_handler.rb
@@ -28,7 +28,7 @@ class Chef
# Verify that the JSON hash for this type has a key that matches its name.
#
- # @param object [Object] JSON hash of the object
+ # @param object_data [Object] JSON hash of the object
# @param entry [Chef::ChefFS::FileSystem::BaseFSObject] filesystem object we are verifying
# @yield [s] callback to handle errors
# @yieldparam [s<string>] error message
diff --git a/lib/chef/chef_fs/data_handler/policy_group_data_handler.rb b/lib/chef/chef_fs/data_handler/policy_group_data_handler.rb
index f7aa92373c..397abdcc3e 100644
--- a/lib/chef/chef_fs/data_handler/policy_group_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/policy_group_data_handler.rb
@@ -17,7 +17,7 @@ class Chef
# Verify that the JSON hash for this type has a key that matches its name.
#
- # @param object [Object] JSON hash of the object
+ # @param object_data [Object] JSON hash of the object
# @param entry [Chef::ChefFS::FileSystem::BaseFSObject] filesystem object we are verifying
# @yield [s] callback to handle errors
# @yieldparam [s<string>] error message
diff --git a/lib/chef/chef_fs/data_handler/role_data_handler.rb b/lib/chef/chef_fs/data_handler/role_data_handler.rb
index 74533cff05..b26271f3e8 100644
--- a/lib/chef/chef_fs/data_handler/role_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/role_data_handler.rb
@@ -7,7 +7,7 @@ class Chef
class RoleDataHandler < DataHandlerBase
def normalize(role, entry)
result = normalize_hash(role, {
- "name" => remove_dot_json(entry.name),
+ "name" => remove_file_extension(entry.name),
"description" => "",
"json_class" => "Chef::Role",
"chef_type" => "role",
@@ -24,7 +24,7 @@ class Chef
end
def preserve_key?(key)
- return key == "name"
+ key == "name"
end
def chef_class
diff --git a/lib/chef/chef_fs/data_handler/user_data_handler.rb b/lib/chef/chef_fs/data_handler/user_data_handler.rb
index c2df4db49d..4da2f8225d 100644
--- a/lib/chef/chef_fs/data_handler/user_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/user_data_handler.rb
@@ -19,7 +19,7 @@ class Chef
end
def preserve_key?(key)
- return key == "name"
+ key == "name"
end
# There is no chef_class for users, nor does to_ruby work.
diff --git a/lib/chef/chef_fs/file_pattern.rb b/lib/chef/chef_fs/file_pattern.rb
index a308a0fe2c..835e06eab7 100644
--- a/lib/chef/chef_fs/file_pattern.rb
+++ b/lib/chef/chef_fs/file_pattern.rb
@@ -84,7 +84,7 @@ class Chef
# Otherwise, it's possible we could match: the path matches to this point, and the pattern is longer than the path.
# TODO There is one edge case where the double star comes after some characters like abc**def--we could check whether the next
# bit of path starts with abc in that case.
- return true
+ true
end
# Returns the immediate child of a path that would be matched
@@ -114,7 +114,7 @@ class Chef
path = path[1, path.length - 1] if Chef::ChefFS::PathUtils.is_absolute?(path)
dirs_in_path = Chef::ChefFS::PathUtils.split(path).length
return nil if exact_parts.length <= dirs_in_path
- return exact_parts[dirs_in_path]
+ exact_parts[dirs_in_path]
end
# If this pattern represents an exact path, returns the exact path.
@@ -160,7 +160,7 @@ class Chef
pattern
end
- private
+ private
def regexp
calculate
diff --git a/lib/chef/chef_fs/file_system.rb b/lib/chef/chef_fs/file_system.rb
index 2b553b19a7..cbd8aa71d9 100644
--- a/lib/chef/chef_fs/file_system.rb
+++ b/lib/chef/chef_fs/file_system.rb
@@ -52,13 +52,13 @@ class Chef
def list_from(entry, &block)
# Include self in results if it matches
- if pattern.match?(entry.path)
+ if pattern.match?(entry.display_path)
yield(entry)
end
- if pattern.could_match_children?(entry.path)
+ if pattern.could_match_children?(entry.display_path)
# If it's possible that our children could match, descend in and add matches.
- exact_child_name = pattern.exact_child_name_under(entry.path)
+ exact_child_name = pattern.exact_child_name_under(entry.display_path)
# If we've got an exact name, don't bother listing children; just grab the
# child with the given name.
@@ -68,7 +68,7 @@ class Chef
list_from(exact_child, &block)
end
- # Otherwise, go through all children and find any matches
+ # Otherwise, go through all children and find any matches
elsif entry.dir?
results = Parallelizer.parallelize(entry.children) { |child| Chef::ChefFS::FileSystem.list(child, pattern) }
results.flatten(1).each(&block)
@@ -186,16 +186,16 @@ class Chef
# Make sure everything on the server is also on the filesystem, and diff
found_paths = Set.new
Chef::ChefFS::FileSystem.list(a_root, pattern).each do |a|
- found_paths << a.path
- b = Chef::ChefFS::FileSystem.resolve_path(b_root, a.path)
+ found_paths << a.display_path
+ b = Chef::ChefFS::FileSystem.resolve_path(b_root, a.display_path)
yield [ a, b ]
end
# Check the outer regex pattern to see if it matches anything on the
# filesystem that isn't on the server
Chef::ChefFS::FileSystem.list(b_root, pattern).each do |b|
- if !found_paths.include?(b.path)
- a = Chef::ChefFS::FileSystem.resolve_path(a_root, b.path)
+ if !found_paths.include?(b.display_path)
+ a = Chef::ChefFS::FileSystem.resolve_path(a_root, b.display_path)
yield [ a, b ]
end
end
@@ -222,14 +222,14 @@ class Chef
result = []
a_children_names = Set.new
a.children.each do |a_child|
- a_children_names << a_child.name
- result << [ a_child, b.child(a_child.name) ]
+ a_children_names << a_child.bare_name
+ result << [ a_child, b.child(a_child.bare_name) ]
end
# Check b for children that aren't in a
b.children.each do |b_child|
- if !a_children_names.include?(b_child.name)
- result << [ a.child(b_child.name), b_child ]
+ if !a_children_names.include?(b_child.bare_name)
+ result << [ a.child(b_child.bare_name), b_child ]
end
end
result
@@ -257,170 +257,174 @@ class Chef
[ are_same, a_value, b_value ]
end
- private
-
- # Copy two entries (could be files or dirs)
- def self.copy_entries(src_entry, dest_entry, new_dest_parent, recurse_depth, options, ui, format_path)
- # A NOTE about this algorithm:
- # There are cases where this algorithm does too many network requests.
- # knife upload with a specific filename will first check if the file
- # exists (a "dir" in the parent) before deciding whether to POST or
- # PUT it. If we just tried PUT (or POST) and then tried the other if
- # the conflict failed, we wouldn't need to check existence.
- # On the other hand, we may already have DONE the request, in which
- # case we shouldn't waste time trying PUT if we know the file doesn't
- # exist.
- # Will need to decide how that works with checksums, though.
- error = false
- begin
- dest_path = format_path.call(dest_entry) if ui
- src_path = format_path.call(src_entry) if ui
- if !src_entry.exists?
- if options[:purge]
- # If we would not have uploaded it, we will not purge it.
- if src_entry.parent.can_have_child?(dest_entry.name, dest_entry.dir?)
- if options[:dry_run]
- ui.output "Would delete #{dest_path}" if ui
- else
- begin
- dest_entry.delete(true)
- ui.output "Deleted extra entry #{dest_path} (purge is on)" if ui
- rescue Chef::ChefFS::FileSystem::NotFoundError
- ui.output "Entry #{dest_path} does not exist. Nothing to do. (purge is on)" if ui
+ class << self
+ private
+
+ # Copy two entries (could be files or dirs)
+ def copy_entries(src_entry, dest_entry, new_dest_parent, recurse_depth, options, ui, format_path)
+ # A NOTE about this algorithm:
+ # There are cases where this algorithm does too many network requests.
+ # knife upload with a specific filename will first check if the file
+ # exists (a "dir" in the parent) before deciding whether to POST or
+ # PUT it. If we just tried PUT (or POST) and then tried the other if
+ # the conflict failed, we wouldn't need to check existence.
+ # On the other hand, we may already have DONE the request, in which
+ # case we shouldn't waste time trying PUT if we know the file doesn't
+ # exist.
+ # Will need to decide how that works with checksums, though.
+ error = false
+ begin
+ dest_path = format_path.call(dest_entry) if ui
+ src_path = format_path.call(src_entry) if ui
+ if !src_entry.exists?
+ if options[:purge]
+ # If we would not have uploaded it, we will not purge it.
+ if src_entry.parent.can_have_child?(dest_entry.name, dest_entry.dir?)
+ if options[:dry_run]
+ ui.output "Would delete #{dest_path}" if ui
+ else
+ begin
+ dest_entry.delete(true)
+ ui.output "Deleted extra entry #{dest_path} (purge is on)" if ui
+ rescue Chef::ChefFS::FileSystem::NotFoundError
+ ui.output "Entry #{dest_path} does not exist. Nothing to do. (purge is on)" if ui
+ end
end
- end
- else
- ui.output ("Not deleting extra entry #{dest_path} (purge is off)") if ui
- end
- end
-
- elsif !dest_entry.exists?
- if new_dest_parent.can_have_child?(src_entry.name, src_entry.dir?)
- # If the entry can do a copy directly from filesystem, do that.
- if new_dest_parent.respond_to?(:create_child_from)
- if options[:dry_run]
- ui.output "Would create #{dest_path}" if ui
else
- new_dest_parent.create_child_from(src_entry)
- ui.output "Created #{dest_path}" if ui
+ ui.output ("Not deleting extra entry #{dest_path} (purge is off)") if ui
end
- return
end
- if src_entry.dir?
- if options[:dry_run]
- ui.output "Would create #{dest_path}" if ui
- new_dest_dir = new_dest_parent.child(src_entry.name)
- else
- new_dest_dir = new_dest_parent.create_child(src_entry.name, nil)
- ui.output "Created #{dest_path}" if ui
- end
- # Directory creation is recursive.
- if recurse_depth != 0
- parallel_do(src_entry.children) do |src_child|
- new_dest_child = new_dest_dir.child(src_child.name)
- child_error = copy_entries(src_child, new_dest_child, new_dest_dir, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
- error ||= child_error
+ elsif !dest_entry.exists?
+ if new_dest_parent.can_have_child?(src_entry.name, src_entry.dir?)
+ # If the entry can do a copy directly from filesystem, do that.
+ if new_dest_parent.respond_to?(:create_child_from)
+ if options[:dry_run]
+ ui.output "Would create #{dest_path}" if ui
+ else
+ new_dest_parent.create_child_from(src_entry)
+ ui.output "Created #{dest_path}" if ui
end
+ return
end
- else
- if options[:dry_run]
- ui.output "Would create #{dest_path}" if ui
- else
- new_dest_parent.create_child(src_entry.name, src_entry.read)
- ui.output "Created #{dest_path}" if ui
- end
- end
- end
-
- else
- # Both exist.
- # If the entry can do a copy directly, do that.
- if dest_entry.respond_to?(:copy_from)
- if options[:force] || compare(src_entry, dest_entry)[0] == false
- if options[:dry_run]
- ui.output "Would update #{dest_path}" if ui
+ if src_entry.dir?
+ if options[:dry_run]
+ ui.output "Would create #{dest_path}" if ui
+ new_dest_dir = new_dest_parent.child(src_entry.name)
+ else
+ new_dest_dir = new_dest_parent.create_child(src_entry.name, nil)
+ ui.output "Created #{dest_path}" if ui
+ end
+ # Directory creation is recursive.
+ if recurse_depth != 0
+ parallel_do(src_entry.children) do |src_child|
+ new_dest_child = new_dest_dir.child(src_child.name)
+ child_error = copy_entries(src_child, new_dest_child, new_dest_dir, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
+ error ||= child_error
+ end
+ end
else
- dest_entry.copy_from(src_entry, options)
- ui.output "Updated #{dest_path}" if ui
+ if options[:dry_run]
+ ui.output "Would create #{dest_path}" if ui
+ else
+ child = new_dest_parent.create_child(src_entry.name, src_entry.read)
+ ui.output "Created #{format_path.call(child)}" if ui
+ end
end
end
- return
- end
- # If they are different types, log an error.
- if src_entry.dir?
- if dest_entry.dir?
- # If both are directories, recurse into their children
- if recurse_depth != 0
- parallel_do(child_pairs(src_entry, dest_entry)) do |src_child, dest_child|
- child_error = copy_entries(src_child, dest_child, dest_entry, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
- error ||= child_error
+ else
+ # Both exist.
+
+ # If the entry can do a copy directly, do that.
+ if dest_entry.respond_to?(:copy_from)
+ if options[:force] || compare(src_entry, dest_entry)[0] == false
+ if options[:dry_run]
+ ui.output "Would update #{dest_path}" if ui
+ else
+ dest_entry.copy_from(src_entry, options)
+ ui.output "Updated #{dest_path}" if ui
end
end
- else
- # If they are different types.
- ui.error("File #{src_path} is a directory while file #{dest_path} is a regular file\n") if ui
return
end
- else
- if dest_entry.dir?
- ui.error("File #{src_path} is a regular file while file #{dest_path} is a directory\n") if ui
- return
- else
- # Both are files! Copy them unless we're sure they are the same.'
- if options[:diff] == false
- should_copy = false
- elsif options[:force]
- should_copy = true
- src_value = nil
+ # If they are different types, log an error.
+ if src_entry.dir?
+ if dest_entry.dir?
+ # If both are directories, recurse into their children
+ if recurse_depth != 0
+ parallel_do(child_pairs(src_entry, dest_entry)) do |src_child, dest_child|
+ child_error = copy_entries(src_child, dest_child, dest_entry, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
+ error ||= child_error
+ end
+ end
else
- are_same, src_value, _dest_value = compare(src_entry, dest_entry)
- should_copy = !are_same
+ # If they are different types.
+ ui.error("File #{src_path} is a directory while file #{dest_path} is a regular file\n") if ui
+ return
end
- if should_copy
- if options[:dry_run]
- ui.output "Would update #{dest_path}" if ui
+ else
+ if dest_entry.dir?
+ ui.error("File #{src_path} is a regular file while file #{dest_path} is a directory\n") if ui
+ return
+ else
+
+ # Both are files! Copy them unless we're sure they are the same.'
+ if options[:diff] == false
+ should_copy = false
+ elsif options[:force]
+ should_copy = true
+ src_value = nil
else
- src_value = src_entry.read if src_value.nil?
- dest_entry.write(src_value)
- ui.output "Updated #{dest_path}" if ui
+ are_same, src_value, _dest_value = compare(src_entry, dest_entry)
+ should_copy = !are_same
+ end
+ if should_copy
+ if options[:dry_run]
+ ui.output "Would update #{dest_path}" if ui
+ else
+ src_value = src_entry.read if src_value.nil?
+ dest_entry.write(src_value)
+ ui.output "Updated #{dest_path}" if ui
+ end
end
end
end
end
+ rescue RubyFileError => e
+ ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui
+ rescue DefaultEnvironmentCannotBeModifiedError => e
+ ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui
+ rescue OperationFailedError => e
+ ui.error "#{format_path.call(e.entry)} failed to #{e.operation}: #{e.message}" if ui
+ error = true
+ rescue OperationNotAllowedError => e
+ ui.error "#{format_path.call(e.entry)} #{e.reason}." if ui
+ error = true
end
- rescue DefaultEnvironmentCannotBeModifiedError => e
- ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui
- rescue OperationFailedError => e
- ui.error "#{format_path.call(e.entry)} failed to #{e.operation}: #{e.message}" if ui
- error = true
- rescue OperationNotAllowedError => e
- ui.error "#{format_path.call(e.entry)} #{e.reason}." if ui
- error = true
+ error
end
- error
- end
- def self.get_or_create_parent(entry, options, ui, format_path)
- parent = entry.parent
- if parent && !parent.exists?
- parent_path = format_path.call(parent) if ui
- parent_parent = get_or_create_parent(parent, options, ui, format_path)
- if options[:dry_run]
- ui.output "Would create #{parent_path}" if ui
- else
- parent = parent_parent.create_child(parent.name, nil)
- ui.output "Created #{parent_path}" if ui
+ def get_or_create_parent(entry, options, ui, format_path)
+ parent = entry.parent
+ if parent && !parent.exists?
+ parent_path = format_path.call(parent) if ui
+ parent_parent = get_or_create_parent(parent, options, ui, format_path)
+ if options[:dry_run]
+ ui.output "Would create #{parent_path}" if ui
+ else
+ parent = parent_parent.create_child(parent.name, nil)
+ ui.output "Created #{parent_path}" if ui
+ end
end
+ parent
end
- return parent
- end
- def self.parallel_do(enum, options = {}, &block)
- Chef::ChefFS::Parallelizer.parallel_do(enum, options, &block)
+ def parallel_do(enum, options = {}, &block)
+ Chef::ChefFS::Parallelizer.parallel_do(enum, options, &block)
+ end
end
end
end
diff --git a/lib/chef/chef_fs/file_system/base_fs_object.rb b/lib/chef/chef_fs/file_system/base_fs_object.rb
index 6abbcf343f..9767b5b1ba 100644
--- a/lib/chef/chef_fs/file_system/base_fs_object.rb
+++ b/lib/chef/chef_fs/file_system/base_fs_object.rb
@@ -40,6 +40,10 @@ class Chef
attr_reader :parent
attr_reader :path
+ alias_method :display_path, :path
+ alias_method :display_name, :name
+ alias_method :bare_name, :name
+
# Override this if you have a special comparison algorithm that can tell
# you whether this entry is the same as another--either a quicker or a
# more reliable one. Callers will use this to decide whether to upload,
diff --git a/lib/chef/chef_fs/file_system/chef_server/acl_dir.rb b/lib/chef/chef_fs/file_system/chef_server/acl_dir.rb
index 9f4d13f5a9..006098a0c9 100644
--- a/lib/chef/chef_fs/file_system/chef_server/acl_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/acl_dir.rb
@@ -35,7 +35,7 @@ class Chef
end
def can_have_child?(name, is_dir)
- name =~ /\.json$/ && !is_dir
+ !is_dir
end
def children
diff --git a/lib/chef/chef_fs/file_system/chef_server/acl_entry.rb b/lib/chef/chef_fs/file_system/chef_server/acl_entry.rb
index 68df3704bf..f4655412fa 100644
--- a/lib/chef/chef_fs/file_system/chef_server/acl_entry.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/acl_entry.rb
@@ -30,6 +30,15 @@ class Chef
"#{super}/_acl"
end
+ def display_path
+ pth = if parent.name == "acls"
+ "/acls/#{name}"
+ else
+ "/acls/#{parent.name}/#{name}"
+ end
+ File.extname(pth).empty? ? pth + ".json" : pth
+ end
+
def delete(recurse)
raise Chef::ChefFS::FileSystem::OperationNotAllowedError.new(:delete, self, nil, "ACLs cannot be deleted")
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/chef_server_root_dir.rb b/lib/chef/chef_fs/file_system/chef_server/chef_server_root_dir.rb
index 5030a0733f..63ce71ef40 100644
--- a/lib/chef/chef_fs/file_system/chef_server/chef_server_root_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/chef_server_root_dir.rb
@@ -103,11 +103,11 @@ class Chef
end
def get_json(path)
- Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key, :api_version => "0").get(path)
+ chef_rest.get(path)
end
def chef_rest
- Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key)
+ Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key, :api_version => "0")
end
def api_path
diff --git a/lib/chef/chef_fs/file_system/chef_server/cookbook_artifacts_dir.rb b/lib/chef/chef_fs/file_system/chef_server/cookbook_artifacts_dir.rb
index 0b82a64a0a..e4df7858a7 100644
--- a/lib/chef/chef_fs/file_system/chef_server/cookbook_artifacts_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/cookbook_artifacts_dir.rb
@@ -56,7 +56,7 @@ class Chef
# to make this work. So instead, we make a temporary cookbook
# symlinking back to real cookbook, and upload the proxy.
def upload_cookbook(other, options)
- cookbook_name, dash, identifier = other.name.rpartition("-")
+ cookbook_name, _, identifier = other.name.rpartition("-")
Dir.mktmpdir do |temp_cookbooks_path|
proxy_cookbook_path = "#{temp_cookbooks_path}/#{cookbook_name}"
@@ -73,7 +73,7 @@ class Chef
cookbook_to_upload.freeze_version if options[:freeze]
# Instantiate a new uploader based on the proxy loader
- uploader = Chef::CookbookUploader.new(cookbook_to_upload, force: options[:force], rest: root.chef_rest, policy_mode: true)
+ uploader = Chef::CookbookUploader.new(cookbook_to_upload, force: options[:force], rest: chef_rest, policy_mode: true)
with_actual_cookbooks_dir(temp_cookbooks_path) do
uploader.upload_cookbooks
@@ -92,6 +92,10 @@ class Chef
end
end
+ def chef_rest
+ Chef::ServerAPI.new(root.chef_rest.url, root.chef_rest.options.merge(version_class: Chef::CookbookManifestVersions))
+ end
+
def can_have_child?(name, is_dir)
is_dir && name.include?("-")
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/cookbook_dir.rb b/lib/chef/chef_fs/file_system/chef_server/cookbook_dir.rb
index 3fa5b49eb1..9b0ea18cd8 100644
--- a/lib/chef/chef_fs/file_system/chef_server/cookbook_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/cookbook_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright 2012-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -49,18 +49,6 @@ class Chef
attr_reader :cookbook_name, :version
- COOKBOOK_SEGMENT_INFO = {
- :attributes => { :ruby_only => true },
- :definitions => { :ruby_only => true },
- :recipes => { :ruby_only => true },
- :libraries => { :recursive => true },
- :templates => { :recursive => true },
- :files => { :recursive => true },
- :resources => { :ruby_only => true, :recursive => true },
- :providers => { :ruby_only => true, :recursive => true },
- :root_files => {},
- }
-
def add_child(child)
@children << child
end
@@ -73,42 +61,36 @@ class Chef
# Since we're ignoring the rules and doing a network request here,
# we need to make sure we don't rethrow the exception. (child(name)
# is not supposed to fail.)
- begin
- children.find { |child| child.name == name }
- rescue Chef::ChefFS::FileSystem::NotFoundError
- nil
- end
+
+ children.find { |child| child.name == name }
+ rescue Chef::ChefFS::FileSystem::NotFoundError
+ nil
end
def can_have_child?(name, is_dir)
- # A cookbook's root may not have directories unless they are segment directories
- return name != "root_files" && COOKBOOK_SEGMENT_INFO.keys.include?(name.to_sym) if is_dir
- return true
+ return name != "root_files" if is_dir
+ true
end
def children
if @children.nil?
@children = []
- manifest = chef_object.manifest
- COOKBOOK_SEGMENT_INFO.each do |segment, segment_info|
- next unless manifest.has_key?(segment)
-
- # Go through each file in the manifest for the segment, and
- # add cookbook subdirs and files for it.
- manifest[segment].each do |segment_file|
- parts = segment_file[:path].split("/")
+ manifest = chef_object.cookbook_manifest
+ manifest.by_parent_directory.each_value do |files|
+ files.each do |file|
+ parts = file[:path].split("/")
# Get or create the path to the file
container = self
parts[0, parts.length - 1].each do |part|
old_container = container
container = old_container.children.find { |child| part == child.name }
if !container
- container = CookbookSubdir.new(part, old_container, segment_info[:ruby_only], segment_info[:recursive])
+ container = CookbookSubdir.new(part, old_container, false, true)
old_container.add_child(container)
end
end
# Create the file itself
- container.add_child(CookbookFile.new(parts[parts.length - 1], container, segment_file))
+ container.add_child(CookbookFile.new(parts[parts.length - 1], container, file))
end
end
@children = @children.sort_by { |c| c.name }
@@ -166,7 +148,11 @@ class Chef
end
def rest
- parent.rest
+ Chef::ServerAPI.new(parent.rest.url, parent.rest.options.merge(version_class: Chef::CookbookManifestVersions))
+ end
+
+ def chef_rest
+ Chef::ServerAPI.new(parent.chef_rest.url, parent.chef_rest.options.merge(version_class: Chef::CookbookManifestVersions))
end
def chef_object
@@ -188,7 +174,7 @@ class Chef
old_retry_count = Chef::Config[:http_retry_count]
begin
Chef::Config[:http_retry_count] = 0
- @chef_object ||= Chef::CookbookVersion.from_hash(root.get_json(api_path))
+ @chef_object ||= Chef::CookbookVersion.from_hash(chef_rest.get(api_path))
ensure
Chef::Config[:http_retry_count] = old_retry_count
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/cookbook_file.rb b/lib/chef/chef_fs/file_system/chef_server/cookbook_file.rb
index 426cc62039..6b4657ae6a 100644
--- a/lib/chef/chef_fs/file_system/chef_server/cookbook_file.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/cookbook_file.rb
@@ -37,20 +37,14 @@ class Chef
end
def read
- begin
- tmpfile = rest.streaming_request(file[:url])
- rescue Timeout::Error => e
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e, "Timeout reading #{file[:url]}: #{e}")
- rescue Net::HTTPServerException => e
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e, "#{e.message} retrieving #{file[:url]}")
- end
-
- begin
- tmpfile.open
- tmpfile.read
- ensure
- tmpfile.close!
- end
+ tmpfile = rest.streaming_request(file[:url])
+ File.open(tmpfile, "rb") { |f| f.read }
+ rescue Timeout::Error => e
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e, "Timeout reading #{file[:url]}: #{e}")
+ rescue Net::HTTPServerException => e
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e, "#{e.message} retrieving #{file[:url]}")
+ rescue Errno::ENOENT
+ raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
end
def rest
diff --git a/lib/chef/chef_fs/file_system/chef_server/cookbooks_dir.rb b/lib/chef/chef_fs/file_system/chef_server/cookbooks_dir.rb
index 631562d7ef..4e8e68e364 100644
--- a/lib/chef/chef_fs/file_system/chef_server/cookbooks_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/cookbooks_dir.rb
@@ -74,13 +74,17 @@ class Chef
def upload_cookbook(other, options)
cookbook_to_upload = other.chef_object
cookbook_to_upload.freeze_version if options[:freeze]
- uploader = Chef::CookbookUploader.new(cookbook_to_upload, :force => options[:force], :rest => root.chef_rest)
+ uploader = Chef::CookbookUploader.new(cookbook_to_upload, :force => options[:force], :rest => chef_rest)
with_actual_cookbooks_dir(other.parent.file_path) do
uploader.upload_cookbooks
end
end
+ def chef_rest
+ Chef::ServerAPI.new(root.chef_rest.url, root.chef_rest.options.merge(version_class: Chef::CookbookManifestVersions))
+ end
+
# Work around the fact that CookbookUploader doesn't understand chef_repo_path (yet)
def with_actual_cookbooks_dir(actual_cookbook_path)
old_cookbook_path = Chef::Config.cookbook_path
diff --git a/lib/chef/chef_fs/file_system/chef_server/data_bag_dir.rb b/lib/chef/chef_fs/file_system/chef_server/data_bag_dir.rb
index 5ad0063807..ee0ecd3b40 100644
--- a/lib/chef/chef_fs/file_system/chef_server/data_bag_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/data_bag_dir.rb
@@ -17,6 +17,7 @@
#
require "chef/chef_fs/file_system/chef_server/rest_list_dir"
+require "chef/chef_fs/file_system/chef_server/data_bag_entry"
require "chef/chef_fs/file_system/exceptions"
require "chef/chef_fs/data_handler/data_bag_item_data_handler"
@@ -63,6 +64,11 @@ class Chef
end
end
end
+
+ def make_child_entry(name, exists = nil)
+ @children.find { |child| child.name == name } if @children
+ DataBagEntry.new(name, self, exists)
+ end
end
end
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/data_bag_entry.rb b/lib/chef/chef_fs/file_system/chef_server/data_bag_entry.rb
new file mode 100644
index 0000000000..c0093058b7
--- /dev/null
+++ b/lib/chef/chef_fs/file_system/chef_server/data_bag_entry.rb
@@ -0,0 +1,19 @@
+require "chef/chef_fs/file_system/chef_server/rest_list_entry"
+require "chef/chef_fs/data_handler/data_bag_item_data_handler"
+
+class Chef
+ module ChefFS
+ module FileSystem
+ module ChefServer
+ # /policies/NAME-REVISION.json
+ # Represents the actual data at /organizations/ORG/policies/NAME/revisions/REVISION
+ class DataBagEntry < RestListEntry
+ def display_path
+ pth = "/data_bags/#{parent.name}/#{name}"
+ File.extname(pth).empty? ? pth + ".json" : pth
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/chef_fs/file_system/chef_server/data_bags_dir.rb b/lib/chef/chef_fs/file_system/chef_server/data_bags_dir.rb
index ec382e60ef..205aa9fd86 100644
--- a/lib/chef/chef_fs/file_system/chef_server/data_bags_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/data_bags_dir.rb
@@ -30,16 +30,14 @@ class Chef
end
def children
- begin
- @children ||= root.get_json(api_path).keys.sort.map { |entry| make_child_entry(entry, true) }
- rescue Timeout::Error => e
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "Timeout getting children: #{e}")
- rescue Net::HTTPServerException => e
- if e.response.code == "404"
- raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
- else
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "HTTP error getting children: #{e}")
- end
+ @children ||= root.get_json(api_path).keys.sort.map { |entry| make_child_entry(entry, true) }
+ rescue Timeout::Error => e
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "Timeout getting children: #{e}")
+ rescue Net::HTTPServerException => e
+ if e.response.code == "404"
+ raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
+ else
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "HTTP error getting children: #{e}")
end
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/environments_dir.rb b/lib/chef/chef_fs/file_system/chef_server/environments_dir.rb
index 81ae81ac0d..e4714cf089 100644
--- a/lib/chef/chef_fs/file_system/chef_server/environments_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/environments_dir.rb
@@ -26,7 +26,7 @@ class Chef
module ChefServer
class EnvironmentsDir < RestListDir
def make_child_entry(name, exists = nil)
- if name == "_default.json"
+ if File.basename(name, ".*") == "_default"
DefaultEnvironmentEntry.new(name, self, exists)
else
super
diff --git a/lib/chef/chef_fs/file_system/chef_server/nodes_dir.rb b/lib/chef/chef_fs/file_system/chef_server/nodes_dir.rb
index 4f83b89711..c81e880744 100644
--- a/lib/chef/chef_fs/file_system/chef_server/nodes_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/nodes_dir.rb
@@ -28,18 +28,16 @@ class Chef
class NodesDir < RestListDir
# Identical to RestListDir.children, except supports environments
def children
- begin
- @children ||= root.get_json(env_api_path).keys.sort.map do |key|
- make_child_entry("#{key}.json", true)
- end
- rescue Timeout::Error => e
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "Timeout retrieving children: #{e}")
- rescue Net::HTTPServerException => e
- if $!.response.code == "404"
- raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
- else
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "HTTP error retrieving children: #{e}")
- end
+ @children ||= root.get_json(env_api_path).keys.sort.map do |key|
+ make_child_entry(key, true)
+ end
+ rescue Timeout::Error => e
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "Timeout retrieving children: #{e}")
+ rescue Net::HTTPServerException => e
+ if $!.response.code == "404"
+ raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
+ else
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "HTTP error retrieving children: #{e}")
end
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/org_entry.rb b/lib/chef/chef_fs/file_system/chef_server/org_entry.rb
index 87be36b932..7253de3449 100644
--- a/lib/chef/chef_fs/file_system/chef_server/org_entry.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/org_entry.rb
@@ -17,6 +17,10 @@ class Chef
parent.api_path
end
+ def display_path
+ "/org.json"
+ end
+
def exists?
parent.exists?
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/organization_invites_entry.rb b/lib/chef/chef_fs/file_system/chef_server/organization_invites_entry.rb
index 37b7af8b5e..adaffb99a7 100644
--- a/lib/chef/chef_fs/file_system/chef_server/organization_invites_entry.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/organization_invites_entry.rb
@@ -27,6 +27,10 @@ class Chef
File.join(parent.api_path, "association_requests")
end
+ def display_path
+ "/invitations.json"
+ end
+
def exists?
parent.exists?
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/organization_members_entry.rb b/lib/chef/chef_fs/file_system/chef_server/organization_members_entry.rb
index 2e45b74450..7e9c7141c4 100644
--- a/lib/chef/chef_fs/file_system/chef_server/organization_members_entry.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/organization_members_entry.rb
@@ -27,6 +27,10 @@ class Chef
File.join(parent.api_path, "users")
end
+ def display_path
+ "/members.json"
+ end
+
def exists?
parent.exists?
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/policies_dir.rb b/lib/chef/chef_fs/file_system/chef_server/policies_dir.rb
index 4a4be19fe4..8259db473d 100644
--- a/lib/chef/chef_fs/file_system/chef_server/policies_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/policies_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright 2012-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -66,42 +66,40 @@ class Chef
# }
# }
def children
- begin
# Grab the names of the children, append json, and make child entries
- @children ||= begin
- result = []
- data = root.get_json(api_path)
- data.keys.sort.each do |policy_name|
- data[policy_name]["revisions"].keys.each do |policy_revision|
- filename = "#{policy_name}-#{policy_revision}.json"
- result << make_child_entry(filename, true)
- end
+ @children ||= begin
+ result = []
+ data = root.get_json(api_path)
+ data.keys.sort.each do |policy_name|
+ data[policy_name]["revisions"].each_key do |policy_revision|
+ filename = "#{policy_name}-#{policy_revision}.json"
+ result << make_child_entry(filename, true)
end
- result
end
- rescue Timeout::Error => e
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "Timeout retrieving children: #{e}")
- rescue Net::HTTPServerException => e
- # 404 = NotFoundError
- if $!.response.code == "404"
- # GET /organizations/ORG/policies returned 404, but that just might be because
- # we are talking to an older version of the server that doesn't support policies.
- # Do GET /orgqanizations/ORG to find out if the org exists at all.
- # TODO use server API version instead of a second network request.
- begin
- root.get_json(parent.api_path)
- # Return empty list if the organization exists but /policies didn't work
- []
- rescue Net::HTTPServerException => e
- if e.response.code == "404"
- raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
- end
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "HTTP error retrieving children: #{e}")
+ result
+ end
+ rescue Timeout::Error => e
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "Timeout retrieving children: #{e}")
+ rescue Net::HTTPServerException => e
+ # 404 = NotFoundError
+ if $!.response.code == "404"
+ # GET /organizations/ORG/policies returned 404, but that just might be because
+ # we are talking to an older version of the server that doesn't support policies.
+ # Do GET /orgqanizations/ORG to find out if the org exists at all.
+ # TODO use server API version instead of a second network request.
+ begin
+ root.get_json(parent.api_path)
+ # Return empty list if the organization exists but /policies didn't work
+ []
+ rescue Net::HTTPServerException => e
+ if e.response.code == "404"
+ raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
end
- # Anything else is unexpected (OperationFailedError)
- else
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "HTTP error retrieving children: #{e}")
end
+ # Anything else is unexpected (OperationFailedError)
+ else
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "HTTP error retrieving children: #{e}")
end
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/policy_group_entry.rb b/lib/chef/chef_fs/file_system/chef_server/policy_group_entry.rb
index b7413c44c5..df3d393d35 100644
--- a/lib/chef/chef_fs/file_system/chef_server/policy_group_entry.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/policy_group_entry.rb
@@ -93,7 +93,7 @@ class Chef
end
begin
- existing_group = Chef::JSONCompat.parse(self.read)
+ existing_group = Chef::JSONCompat.parse(read)
rescue NotFoundError
# It's OK if the group doesn't already exist, just means no existing policies
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/policy_revision_entry.rb b/lib/chef/chef_fs/file_system/chef_server/policy_revision_entry.rb
index d083383a0e..325b18e429 100644
--- a/lib/chef/chef_fs/file_system/chef_server/policy_revision_entry.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/policy_revision_entry.rb
@@ -14,6 +14,10 @@ class Chef
"#{parent.api_path}/#{policy_name}/revisions/#{revision_id}"
end
+ def display_path
+ "/policies/#{policy_name}-#{revision_id}.json"
+ end
+
def write(file_contents)
raise OperationNotAllowedError.new(:write, self, nil, "cannot be updated: policy revisions are immutable once uploaded. If you want to change the policy, create a new revision with your changes")
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/rest_list_dir.rb b/lib/chef/chef_fs/file_system/chef_server/rest_list_dir.rb
index 276f7760b5..6ba53fab4b 100644
--- a/lib/chef/chef_fs/file_system/chef_server/rest_list_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/rest_list_dir.rb
@@ -35,7 +35,7 @@ class Chef
attr_reader :data_handler
def can_have_child?(name, is_dir)
- name =~ /\.json$/ && !is_dir
+ !is_dir
end
#
@@ -71,40 +71,38 @@ class Chef
# Children are foo.json and bar.json in this case.
#
def children
- begin
# Grab the names of the children, append json, and make child entries
- @children ||= root.get_json(api_path).keys.sort.map do |key|
- make_child_entry("#{key}.json", true)
- end
- rescue Timeout::Error => e
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "Timeout retrieving children: #{e}")
- rescue Net::HTTPServerException => e
- # 404 = NotFoundError
- if $!.response.code == "404"
-
- if parent.is_a?(ChefServerRootDir)
- # GET /organizations/ORG/<container> returned 404, but that just might be because
- # we are talking to an older version of the server that doesn't support policies.
- # Do GET /orgqanizations/ORG to find out if the org exists at all.
- # TODO use server API version instead of a second network request.
- begin
- root.get_json(parent.api_path)
- # Return empty list if the organization exists but /policies didn't work
- []
- rescue Net::HTTPServerException => e
- if e.response.code == "404"
- raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
- end
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "HTTP error retrieving children: #{e}")
+ @children ||= root.get_json(api_path).keys.sort.map do |key|
+ make_child_entry(key, true)
+ end
+ rescue Timeout::Error => e
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "Timeout retrieving children: #{e}")
+ rescue Net::HTTPServerException => e
+ # 404 = NotFoundError
+ if $!.response.code == "404"
+
+ if parent.is_a?(ChefServerRootDir)
+ # GET /organizations/ORG/<container> returned 404, but that just might be because
+ # we are talking to an older version of the server that doesn't support policies.
+ # Do GET /organizations/ORG to find out if the org exists at all.
+ # TODO use server API version instead of a second network request.
+ begin
+ root.get_json(parent.api_path)
+ # Return empty list if the organization exists but /policies didn't work
+ []
+ rescue Net::HTTPServerException => e
+ if e.response.code == "404"
+ raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
end
- else
- raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "HTTP error retrieving children: #{e}")
end
-
- # Anything else is unexpected (OperationFailedError)
else
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "HTTP error retrieving children: #{e}")
+ raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
end
+
+ # Anything else is unexpected (OperationFailedError)
+ else
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "HTTP error retrieving children: #{e}")
end
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/rest_list_entry.rb b/lib/chef/chef_fs/file_system/chef_server/rest_list_entry.rb
index 9b16bd80de..c16c5ad4d7 100644
--- a/lib/chef/chef_fs/file_system/chef_server/rest_list_entry.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/rest_list_entry.rb
@@ -30,6 +30,7 @@ class Chef
def initialize(name, parent, exists = nil)
super(name, parent)
@exists = exists
+ @this_object_cache = nil
end
def data_handler
@@ -37,16 +38,27 @@ class Chef
end
def api_child_name
- if name.length < 5 || name[-5, 5] != ".json"
- raise "Invalid name #{path}: must end in .json"
+ if %w{ .rb .json }.include? File.extname(name)
+ File.basename(name, ".*")
+ else
+ name
end
- name[0, name.length - 5]
end
def api_path
"#{parent.api_path}/#{api_child_name}"
end
+ def display_path
+ pth = api_path.start_with?("/") ? api_path : "/#{api_path}"
+ File.extname(pth).empty? ? pth + ".json" : pth
+ end
+ alias_method :path_for_printing, :display_path
+
+ def display_name
+ File.basename(display_path)
+ end
+
def org
parent.org
end
@@ -58,7 +70,14 @@ class Chef
def exists?
if @exists.nil?
begin
- @exists = parent.children.any? { |child| child.name == name }
+ @this_object_cache = rest.get(api_path)
+ @exists = true
+ rescue Net::HTTPServerException => e
+ if e.response.code == "404"
+ @exists = false
+ else
+ raise
+ end
rescue Chef::ChefFS::FileSystem::NotFoundError
@exists = false
end
@@ -67,35 +86,33 @@ class Chef
end
def delete(recurse)
- begin
- rest.delete(api_path)
- rescue Timeout::Error => e
+ # free up cache - it will be hydrated on next check for exists?
+ @this_object_cache = nil
+ rest.delete(api_path)
+ rescue Timeout::Error => e
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:delete, self, e, "Timeout deleting: #{e}")
+ rescue Net::HTTPServerException => e
+ if e.response.code == "404"
+ raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
+ else
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:delete, self, e, "Timeout deleting: #{e}")
- rescue Net::HTTPServerException => e
- if e.response.code == "404"
- raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
- else
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:delete, self, e, "Timeout deleting: #{e}")
- end
end
end
def read
+ # Minimize the value (get rid of defaults) so the results don't look terrible
Chef::JSONCompat.to_json_pretty(minimize_value(_read_json))
end
def _read_json
- begin
- # Minimize the value (get rid of defaults) so the results don't look terrible
- root.get_json(api_path)
- rescue Timeout::Error => e
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e, "Timeout reading: #{e}")
- rescue Net::HTTPServerException => e
- if $!.response.code == "404"
- raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
- else
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e, "HTTP error reading: #{e}")
- end
+ @this_object_cache ? JSON.parse(@this_object_cache) : root.get_json(api_path)
+ rescue Timeout::Error => e
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e, "Timeout reading: #{e}")
+ rescue Net::HTTPServerException => e
+ if $!.response.code == "404"
+ raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
+ else
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e, "HTTP error reading: #{e}")
end
end
@@ -137,6 +154,9 @@ class Chef
other_value = minimize_value(other_value)
other_value_json = Chef::JSONCompat.to_json_pretty(other_value)
+ # free up cache - it will be hydrated on next check for exists?
+ @this_object_cache = nil
+
[ value == other_value, value_json, other_value_json ]
end
@@ -145,6 +165,9 @@ class Chef
end
def write(file_contents)
+ # free up cache - it will be hydrated on next check for exists?
+ @this_object_cache = nil
+
begin
object = Chef::JSONCompat.parse(file_contents)
rescue Chef::Exceptions::JSON::ParseError => e
@@ -172,11 +195,9 @@ class Chef
end
def api_error_text(response)
- begin
- Chef::JSONCompat.parse(response.body)["error"].join("\n")
- rescue
- response.body
- end
+ Chef::JSONCompat.parse(response.body)["error"].join("\n")
+ rescue
+ response.body
end
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/versioned_cookbook_dir.rb b/lib/chef/chef_fs/file_system/chef_server/versioned_cookbook_dir.rb
index 269e160d43..b7c96c42e1 100644
--- a/lib/chef/chef_fs/file_system/chef_server/versioned_cookbook_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/versioned_cookbook_dir.rb
@@ -24,7 +24,7 @@ class Chef
module ChefServer
class VersionedCookbookDir < CookbookDir
# See Erchef code
- # https://github.com/opscode/chef_objects/blob/968a63344d38fd507f6ace05f73d53e9cd7fb043/src/chef_regex.erl#L94
+ # https://github.com/chef/chef_objects/blob/968a63344d38fd507f6ace05f73d53e9cd7fb043/src/chef_regex.erl#L94
VALID_VERSIONED_COOKBOOK_NAME = /^([.a-zA-Z0-9_-]+)-(\d+\.\d+\.\d+)$/
def initialize(name, parent, options = {})
diff --git a/lib/chef/chef_fs/file_system/chef_server/versioned_cookbooks_dir.rb b/lib/chef/chef_fs/file_system/chef_server/versioned_cookbooks_dir.rb
index 172405763a..8da3718136 100644
--- a/lib/chef/chef_fs/file_system/chef_server/versioned_cookbooks_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/versioned_cookbooks_dir.rb
@@ -78,7 +78,7 @@ class Chef
cookbook_to_upload.freeze_version if options[:freeze]
# Instantiate a new uploader based on the proxy loader
- uploader = Chef::CookbookUploader.new(cookbook_to_upload, :force => options[:force], :rest => root.chef_rest)
+ uploader = Chef::CookbookUploader.new(cookbook_to_upload, :force => options[:force], :rest => chef_rest)
with_actual_cookbooks_dir(temp_cookbooks_path) do
uploader.upload_cookbooks
@@ -97,6 +97,10 @@ class Chef
end
end
+ def chef_rest
+ Chef::ServerAPI.new(root.chef_rest.url, root.chef_rest.options.merge(version_class: Chef::CookbookManifestVersions))
+ end
+
def can_have_child?(name, is_dir)
is_dir && name =~ Chef::ChefFS::FileSystem::ChefServer::VersionedCookbookDir::VALID_VERSIONED_COOKBOOK_NAME
end
diff --git a/lib/chef/chef_fs/file_system/exceptions.rb b/lib/chef/chef_fs/file_system/exceptions.rb
index cf4916e4c8..2a1baba8f5 100644
--- a/lib/chef/chef_fs/file_system/exceptions.rb
+++ b/lib/chef/chef_fs/file_system/exceptions.rb
@@ -86,6 +86,13 @@ class Chef
class CookbookFrozenError < AlreadyExistsError; end
+ class RubyFileError < OperationNotAllowedError
+ def reason
+ result = super
+ result + " (can't safely update ruby files)"
+ end
+ end
+
class DefaultEnvironmentCannotBeModifiedError < OperationNotAllowedError
def reason
result = super
diff --git a/lib/chef/chef_fs/file_system/memory/memory_file.rb b/lib/chef/chef_fs/file_system/memory/memory_file.rb
index 7eabc8fcb1..6dcefc72eb 100644
--- a/lib/chef/chef_fs/file_system/memory/memory_file.rb
+++ b/lib/chef/chef_fs/file_system/memory/memory_file.rb
@@ -11,7 +11,7 @@ class Chef
end
def read
- return @value
+ @value
end
end
end
diff --git a/lib/chef/chef_fs/file_system/multiplexed_dir.rb b/lib/chef/chef_fs/file_system/multiplexed_dir.rb
index e143dde9e8..969a579532 100644
--- a/lib/chef/chef_fs/file_system/multiplexed_dir.rb
+++ b/lib/chef/chef_fs/file_system/multiplexed_dir.rb
@@ -17,22 +17,20 @@ class Chef
end
def children
- begin
- result = []
- seen = {}
+ result = []
+ seen = {}
# If multiple things have the same name, the first one wins.
- multiplexed_dirs.each do |dir|
- dir.children.each do |child|
- if seen[child.name]
- Chef::Log.warn("Child with name '#{child.name}' found in multiple directories: #{seen[child.name].path_for_printing} and #{child.path_for_printing}")
- else
- result << child
- seen[child.name] = child
- end
+ multiplexed_dirs.each do |dir|
+ dir.children.each do |child|
+ if seen[child.name]
+ Chef::Log.warn("Child with name '#{child.name}' found in multiple directories: #{seen[child.name].path_for_printing} and #{child.path_for_printing}") unless seen[child.name].path_for_printing == child.path_for_printing
+ else
+ result << child
+ seen[child.name] = child
end
end
- result
end
+ result
end
def make_child_entry(name)
@@ -41,7 +39,7 @@ class Chef
child_entry = dir.child(name)
if child_entry.exists?
if result
- Chef::Log.warn("Child with name '#{child_entry.name}' found in multiple directories: #{result.parent.path_for_printing} and #{child_entry.parent.path_for_printing}")
+ Chef::Log.trace("Child with name '#{child_entry.name}' found in multiple directories: #{result.parent.path_for_printing} and #{child_entry.parent.path_for_printing}")
else
result = child_entry
end
diff --git a/lib/chef/chef_fs/file_system/nonexistent_fs_object.rb b/lib/chef/chef_fs/file_system/nonexistent_fs_object.rb
index b78dcb6c0b..1a48d23047 100644
--- a/lib/chef/chef_fs/file_system/nonexistent_fs_object.rb
+++ b/lib/chef/chef_fs/file_system/nonexistent_fs_object.rb
@@ -23,10 +23,6 @@ class Chef
module ChefFS
module FileSystem
class NonexistentFSObject < BaseFSObject
- def initialize(name, parent)
- super
- end
-
def exists?
false
end
diff --git a/lib/chef/chef_fs/file_system/repository/acl.rb b/lib/chef/chef_fs/file_system/repository/acl.rb
index e2ba2e8771..023ae11379 100644
--- a/lib/chef/chef_fs/file_system/repository/acl.rb
+++ b/lib/chef/chef_fs/file_system/repository/acl.rb
@@ -31,6 +31,13 @@ class Chef
super
end
+ def bare_name
+ if name == "organization" && parent.kind_of?(AclDir)
+ "organization.json"
+ else
+ name
+ end
+ end
end
end
end
diff --git a/lib/chef/chef_fs/file_system/repository/acls_dir.rb b/lib/chef/chef_fs/file_system/repository/acls_dir.rb
index 619031aa70..110befdf22 100644
--- a/lib/chef/chef_fs/file_system/repository/acls_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/acls_dir.rb
@@ -28,14 +28,16 @@ class Chef
module Repository
class AclsDir < Repository::Directory
+ BARE_FILES = %w{ organization.json root }
+
def can_have_child?(name, is_dir)
- is_dir ? Chef::ChefFS::FileSystem::ChefServer::AclsDir::ENTITY_TYPES.include?(name) : name == "organization.json"
+ is_dir ? Chef::ChefFS::FileSystem::ChefServer::AclsDir::ENTITY_TYPES.include?(name) : BARE_FILES.include?(name)
end
protected
def make_child_entry(child_name)
- if child_name == "organization.json"
+ if BARE_FILES.include? child_name
Acl.new(child_name, self)
else
AclsSubDir.new(child_name, self)
diff --git a/lib/chef/chef_fs/file_system/repository/acls_sub_dir.rb b/lib/chef/chef_fs/file_system/repository/acls_sub_dir.rb
index 8eee8e1d70..70c7d77480 100644
--- a/lib/chef/chef_fs/file_system/repository/acls_sub_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/acls_sub_dir.rb
@@ -26,10 +26,6 @@ class Chef
module Repository
class AclsSubDir < Repository::Directory
- def can_have_child?(name, is_dir)
- !is_dir && File.extname(name) == ".json"
- end
-
protected
def make_child_entry(child_name)
diff --git a/lib/chef/chef_fs/file_system/repository/base_file.rb b/lib/chef/chef_fs/file_system/repository/base_file.rb
index d5ef26887e..76d242eee5 100644
--- a/lib/chef/chef_fs/file_system/repository/base_file.rb
+++ b/lib/chef/chef_fs/file_system/repository/base_file.rb
@@ -16,6 +16,8 @@
# limitations under the License.
#
+require "chef/chef_fs/file_system_cache"
+
class Chef
module ChefFS
module FileSystem
@@ -29,23 +31,48 @@ class Chef
attr_reader :file_path
attr_reader :data_handler
+ alias_method :display_path, :path
+ alias_method :display_name, :name
+
def initialize(name, parent)
@parent = parent
+
+ if %w{ .rb .json }.include? File.extname(name)
+ name = File.basename(name, ".*")
+ end
+
+ file_path = "#{parent.file_path}/#{name}"
+
+ Chef::Log.trace "BaseFile: Detecting file extension for #{name}"
+ ext = File.exist?(file_path + ".rb") ? ".rb" : ".json"
+ name += ext
+ file_path += ext
+
+ Chef::Log.trace "BaseFile: got a file path of #{file_path} for #{name}"
@name = name
@path = Chef::ChefFS::PathUtils.join(parent.path, name)
- @file_path = "#{parent.file_path}/#{name}"
+ @file_path = file_path
end
def dir?
false
end
+ # Used to compare names on disk to the API, for diffing.
+ def bare_name
+ File.basename(name, ".*")
+ end
+
def is_json_file?
- File.extname(name) == ".json"
+ File.extname(file_path) == ".json"
+ end
+
+ def is_ruby_file?
+ File.extname(file_path) == ".rb"
end
def name_valid?
- !name.start_with?(".") && is_json_file?
+ !name.start_with?(".") && (is_json_file? || is_ruby_file?)
end
def fs_entry_valid?
@@ -74,6 +101,7 @@ class Chef
end
def delete(_)
+ FileSystemCache.instance.delete!(file_path)
File.delete(file_path)
rescue Errno::ENOENT
raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
@@ -91,12 +119,19 @@ class Chef
end
def read
- File.open(file_path, "rb") { |f| f.read }
+ if is_ruby_file?
+ data_handler.from_ruby(file_path).to_json
+ else
+ File.open(file_path, "rb") { |f| f.read }
+ end
rescue Errno::ENOENT
raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
end
def write(content)
+ if is_ruby_file?
+ raise Chef::ChefFS::FileSystem::RubyFileError.new(:write, self)
+ end
if content && write_pretty_json && is_json_file?
content = minimize(content, self)
end
diff --git a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_dir.rb b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_dir.rb
index 1b640bc076..b296901dd1 100644
--- a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_dir.rb
@@ -80,18 +80,16 @@ class Chef
# Customizations of base class
def chef_object
- begin
- cb = cookbook_version
- if !cb
- Chef::Log.error("Cookbook #{file_path} empty.")
- raise "Cookbook #{file_path} empty."
- end
- cb
- rescue => e
- Chef::Log.error("Could not read #{path_for_printing} into a Chef object: #{e}")
- Chef::Log.error(e.backtrace.join("\n"))
- raise
+ cb = cookbook_version
+ if !cb
+ Chef::Log.error("Cookbook #{file_path} empty.")
+ raise "Cookbook #{file_path} empty."
end
+ cb
+ rescue => e
+ Chef::Log.error("Could not read #{path_for_printing} into a Chef object: #{e}")
+ Chef::Log.error(e.backtrace.join("\n"))
+ raise
end
def children
@@ -99,9 +97,9 @@ class Chef
end
def can_have_child?(name, is_dir)
- if is_dir
+ if is_dir && !%w{ root_files .. . }.include?(name)
# Only the given directories will be uploaded.
- return Chef::ChefFS::FileSystem::ChefServer::CookbookDir::COOKBOOK_SEGMENT_INFO.keys.include?(name.to_sym) && name != "root_files"
+ return true
elsif name == Chef::Cookbook::CookbookVersionLoader::UPLOADED_COOKBOOK_VERSION_FILE
return false
end
@@ -112,7 +110,7 @@ class Chef
def self.canonical_cookbook_name(entry_name)
name_match = Chef::ChefFS::FileSystem::ChefServer::VersionedCookbookDir::VALID_VERSIONED_COOKBOOK_NAME.match(entry_name)
return nil if name_match.nil?
- return name_match[1]
+ name_match[1]
end
def canonical_cookbook_name(entry_name)
@@ -130,8 +128,7 @@ class Chef
protected
def make_child_entry(child_name)
- segment_info = Chef::ChefFS::FileSystem::ChefServer::CookbookDir::COOKBOOK_SEGMENT_INFO[child_name.to_sym] || {}
- ChefRepositoryFileSystemCookbookEntry.new(child_name, self, nil, segment_info[:ruby_only], segment_info[:recursive])
+ ChefRepositoryFileSystemCookbookEntry.new(child_name, self, nil, false, true)
end
def cookbook_version
diff --git a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb
index 703c0fc635..90b8c88cff 100644
--- a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb
+++ b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb
@@ -37,6 +37,10 @@ class Chef
attr_reader :recursive
attr_reader :file_path
+ alias_method :display_path, :path
+ alias_method :display_name, :name
+ alias_method :bare_name, :name
+
def initialize(name, parent, file_path = nil, ruby_only = false, recursive = false)
@parent = parent
@name = name
@@ -48,14 +52,12 @@ class Chef
end
def children
- begin
- entries = Dir.entries(file_path).sort.
- map { |child_name| make_child_entry(child_name) }.
- select { |child| child && can_have_child?(child.name, child.dir?) }
- entries.select { |entry| !(entry.dir? && entry.children.size == 0 ) }
- rescue Errno::ENOENT
- raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
- end
+ entries = Dir.entries(file_path).sort.
+ map { |child_name| make_child_entry(child_name) }.
+ select { |child| child && can_have_child?(child.name, child.dir?) }
+ entries.select { |entry| !(entry.dir? && entry.children.size == 0 ) }
+ rescue Errno::ENOENT
+ raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
end
def can_have_child?(name, is_dir)
@@ -116,6 +118,7 @@ class Chef
end
def delete(recurse)
+ FileSystemCache.instance.delete!(file_path)
begin
if dir?
if !recurse
@@ -135,11 +138,9 @@ class Chef
end
def read
- begin
- File.open(file_path, "rb") { |f| f.read }
- rescue Errno::ENOENT
- raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
- end
+ File.open(file_path, "rb") { |f| f.read }
+ rescue Errno::ENOENT
+ raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
end
def write(content)
diff --git a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_root_dir.rb b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_root_dir.rb
index 1b26ced372..9ea9268ab1 100644
--- a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_root_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_root_dir.rb
@@ -44,6 +44,7 @@ require "chef/chef_fs/data_handler/role_data_handler"
require "chef/chef_fs/data_handler/user_data_handler"
require "chef/chef_fs/data_handler/group_data_handler"
require "chef/chef_fs/data_handler/container_data_handler"
+require "chef/win32/security" if Chef::Platform.windows?
class Chef
module ChefFS
@@ -109,7 +110,19 @@ class Chef
else
child_paths[name].each do |path|
begin
- Dir.mkdir(path)
+ Dir.mkdir(path, 0700)
+ if Chef::Platform.windows?
+ all_mask = Chef::ReservedNames::Win32::API::Security::GENERIC_ALL
+ administrators = Chef::ReservedNames::Win32::Security::SID.Administrators
+ owner = Chef::ReservedNames::Win32::Security::SID.default_security_object_owner
+ dacl = Chef::ReservedNames::Win32::Security::ACL.create([
+ Chef::ReservedNames::Win32::Security::ACE.access_allowed(owner, all_mask),
+ Chef::ReservedNames::Win32::Security::ACE.access_allowed(administrators, all_mask),
+ ])
+ so = Chef::ReservedNames::Win32::Security::SecurableObject.new(path)
+ so.owner = owner
+ so.set_dacl(dacl, false)
+ end
rescue Errno::EEXIST
end
end
diff --git a/lib/chef/chef_fs/file_system/repository/client_keys_sub_dir.rb b/lib/chef/chef_fs/file_system/repository/client_keys_sub_dir.rb
index d231e41e78..6aafcfe294 100644
--- a/lib/chef/chef_fs/file_system/repository/client_keys_sub_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/client_keys_sub_dir.rb
@@ -26,10 +26,6 @@ class Chef
module Repository
class ClientKeysSubDir < Repository::Directory
- def can_have_child?(name, is_dir)
- !is_dir && File.extname(name) == ".json"
- end
-
protected
def make_child_entry(child_name)
diff --git a/lib/chef/chef_fs/file_system/repository/clients_dir.rb b/lib/chef/chef_fs/file_system/repository/clients_dir.rb
index 31aa80a28f..01027f83ac 100644
--- a/lib/chef/chef_fs/file_system/repository/clients_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/clients_dir.rb
@@ -26,9 +26,6 @@ class Chef
module FileSystem
module Repository
class ClientsDir < Repository::Directory
- def can_have_child?(name, is_dir)
- !is_dir && File.extname(name) == ".json"
- end
def make_child_entry(child_name)
Client.new(child_name, self)
diff --git a/lib/chef/chef_fs/file_system/repository/containers_dir.rb b/lib/chef/chef_fs/file_system/repository/containers_dir.rb
index 0fe541417a..2af496f418 100644
--- a/lib/chef/chef_fs/file_system/repository/containers_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/containers_dir.rb
@@ -27,10 +27,6 @@ class Chef
module Repository
class ContainersDir < Repository::Directory
- def can_have_child?(name, is_dir)
- !is_dir && File.extname(name) == ".json"
- end
-
def make_child_entry(child_name)
Container.new(child_name, self)
end
diff --git a/lib/chef/chef_fs/file_system/repository/cookbooks_dir.rb b/lib/chef/chef_fs/file_system/repository/cookbooks_dir.rb
index 0fd249a2c4..86eca95ba8 100644
--- a/lib/chef/chef_fs/file_system/repository/cookbooks_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/cookbooks_dir.rb
@@ -28,7 +28,7 @@ class Chef
class CookbooksDir < Repository::Directory
def chefignore
- @chefignore ||= Chef::Cookbook::Chefignore.new(self.file_path)
+ @chefignore ||= Chef::Cookbook::Chefignore.new(file_path)
rescue Errno::EISDIR, Errno::EACCES
# Work around a bug in Chefignore when chefignore is a directory
end
diff --git a/lib/chef/chef_fs/file_system/repository/directory.rb b/lib/chef/chef_fs/file_system/repository/directory.rb
index 20f6f82108..328cf92b03 100644
--- a/lib/chef/chef_fs/file_system/repository/directory.rb
+++ b/lib/chef/chef_fs/file_system/repository/directory.rb
@@ -16,6 +16,8 @@
# limitations under the License.
#
+require "chef/chef_fs/file_system_cache"
+
class Chef
module ChefFS
module FileSystem
@@ -28,6 +30,10 @@ class Chef
attr_reader :path
attr_reader :file_path
+ alias_method :display_path, :path
+ alias_method :display_name, :name
+ alias_method :bare_name, :name
+
def initialize(name, parent, file_path = nil)
@parent = parent
@name = name
@@ -64,9 +70,11 @@ class Chef
end
def children
- dir_ls.sort.
+ return FileSystemCache.instance.children(file_path) if FileSystemCache.instance.exist?(file_path)
+ children = dir_ls.sort.
map { |child_name| make_child_entry(child_name) }.
select { |new_child| new_child.fs_entry_valid? && can_have_child?(new_child.name, new_child.dir?) }
+ FileSystemCache.instance.set_children(file_path, children)
rescue Errno::ENOENT => e
raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
end
@@ -76,6 +84,7 @@ class Chef
if child.exists?
raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, child)
end
+ FileSystemCache.instance.delete!(child.file_path)
if file_contents
child.write(file_contents)
else
@@ -114,6 +123,7 @@ class Chef
raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, self)
end
begin
+ FileSystemCache.instance.delete!(file_path)
Dir.mkdir(file_path)
rescue Errno::EEXIST
raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, self)
@@ -130,6 +140,7 @@ class Chef
raise MustDeleteRecursivelyError.new(self, $!)
end
FileUtils.rm_r(file_path)
+ FileSystemCache.instance.delete!(file_path)
else
raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
end
@@ -141,6 +152,10 @@ class Chef
protected
+ def write(data)
+ raise FileSystemError.new(self, nil, "attempted to write to a directory entry")
+ end
+
def make_child_entry(child_name)
raise "Not Implemented"
end
diff --git a/lib/chef/chef_fs/file_system/repository/environments_dir.rb b/lib/chef/chef_fs/file_system/repository/environments_dir.rb
index 2c9b31b29b..4d04348d6e 100644
--- a/lib/chef/chef_fs/file_system/repository/environments_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/environments_dir.rb
@@ -27,10 +27,6 @@ class Chef
module Repository
class EnvironmentsDir < Repository::Directory
- def can_have_child?(name, is_dir)
- !is_dir && File.extname(name) == ".json"
- end
-
def make_child_entry(child_name)
Environment.new(child_name, self)
end
diff --git a/lib/chef/chef_fs/file_system/repository/groups_dir.rb b/lib/chef/chef_fs/file_system/repository/groups_dir.rb
index e307000ad8..20728d1248 100644
--- a/lib/chef/chef_fs/file_system/repository/groups_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/groups_dir.rb
@@ -27,10 +27,6 @@ class Chef
module Repository
class GroupsDir < Repository::Directory
- def can_have_child?(name, is_dir)
- !is_dir && File.extname(name) == ".json"
- end
-
def make_child_entry(child_name)
Group.new(child_name, self)
end
diff --git a/lib/chef/chef_fs/file_system/repository/nodes_dir.rb b/lib/chef/chef_fs/file_system/repository/nodes_dir.rb
index d04bea0de2..a0dd0c9e51 100644
--- a/lib/chef/chef_fs/file_system/repository/nodes_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/nodes_dir.rb
@@ -20,6 +20,7 @@
require "chef/chef_fs/file_system/repository/node"
require "chef/chef_fs/file_system/repository/directory"
require "chef/chef_fs/file_system/exceptions"
+require "chef/win32/security" if Chef::Platform.windows?
class Chef
module ChefFS
@@ -27,13 +28,30 @@ class Chef
module Repository
class NodesDir < Repository::Directory
- def can_have_child?(name, is_dir)
- !is_dir && File.extname(name) == ".json"
- end
-
def make_child_entry(child_name)
Node.new(child_name, self)
end
+
+ def create_child(child_name, file_contents = nil)
+ child = super
+ File.chmod(0600, child.file_path)
+ if Chef::Platform.windows?
+ read_mask = Chef::ReservedNames::Win32::API::Security::GENERIC_READ
+ write_mask = Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE
+ administrators = Chef::ReservedNames::Win32::Security::SID.Administrators
+ owner = Chef::ReservedNames::Win32::Security::SID.default_security_object_owner
+ dacl = Chef::ReservedNames::Win32::Security::ACL.create([
+ Chef::ReservedNames::Win32::Security::ACE.access_allowed(owner, read_mask),
+ Chef::ReservedNames::Win32::Security::ACE.access_allowed(owner, write_mask),
+ Chef::ReservedNames::Win32::Security::ACE.access_allowed(administrators, read_mask),
+ Chef::ReservedNames::Win32::Security::ACE.access_allowed(administrators, write_mask),
+ ])
+ so = Chef::ReservedNames::Win32::Security::SecurableObject.new(child.file_path)
+ so.owner = owner
+ so.set_dacl(dacl, false)
+ end
+ child
+ end
end
end
end
diff --git a/lib/chef/chef_fs/file_system/repository/policy_groups_dir.rb b/lib/chef/chef_fs/file_system/repository/policy_groups_dir.rb
index 020464a634..4db85a93f7 100644
--- a/lib/chef/chef_fs/file_system/repository/policy_groups_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/policy_groups_dir.rb
@@ -27,10 +27,6 @@ class Chef
module Repository
class PolicyGroupsDir < Repository::Directory
- def can_have_child?(name, is_dir)
- !is_dir && File.extname(name) == ".json"
- end
-
def make_child_entry(child_name)
PolicyGroup.new(child_name, self)
end
diff --git a/lib/chef/chef_fs/file_system/repository/roles_dir.rb b/lib/chef/chef_fs/file_system/repository/roles_dir.rb
index f59af093bb..42f4376e71 100644
--- a/lib/chef/chef_fs/file_system/repository/roles_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/roles_dir.rb
@@ -27,10 +27,6 @@ class Chef
module Repository
class RolesDir < Repository::Directory
- def can_have_child?(name, is_dir)
- !is_dir && File.extname(name) == ".json"
- end
-
def make_child_entry(child_name)
Role.new(child_name, self)
end
diff --git a/lib/chef/chef_fs/file_system/repository/users_dir.rb b/lib/chef/chef_fs/file_system/repository/users_dir.rb
index 32cebf842d..9e8621575b 100644
--- a/lib/chef/chef_fs/file_system/repository/users_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/users_dir.rb
@@ -27,10 +27,6 @@ class Chef
module Repository
class UsersDir < Repository::Directory
- def can_have_child?(name, is_dir)
- !is_dir && File.extname(name) == ".json"
- end
-
def make_child_entry(child_name)
User.new(child_name, self)
end
diff --git a/lib/chef/chef_fs/file_system_cache.rb b/lib/chef/chef_fs/file_system_cache.rb
new file mode 100644
index 0000000000..7b6c4c148f
--- /dev/null
+++ b/lib/chef/chef_fs/file_system_cache.rb
@@ -0,0 +1,80 @@
+#
+# Copyright:: Copyright 2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "singleton"
+require "chef/client"
+
+class Chef
+ module ChefFS
+ class FileSystemCache
+ include Singleton
+
+ def initialize
+ @cache = {}
+
+ Chef::Client.when_run_starts do
+ FileSystemCache.instance.reset!
+ end
+ end
+
+ def reset!
+ @cache = {}
+ end
+
+ def exist?(path)
+ @cache.key?(path)
+ end
+
+ def children(path)
+ @cache[path]["children"]
+ end
+
+ def set_children(path, val)
+ @cache[path] ||= { "children" => [] }
+ @cache[path]["children"] = val
+ val
+ end
+
+ def delete!(path)
+ parent = _get_parent(path)
+ Chef::Log.trace("Deleting parent #{parent} and #{path} from FileSystemCache")
+ if @cache.key?(path)
+ @cache.delete(path)
+ end
+ if !parent.nil? && @cache.key?(parent)
+ @cache.delete(parent)
+ end
+ end
+
+ def fetch(path)
+ if @cache.key?(path)
+ @cache[path]
+ else
+ false
+ end
+ end
+
+ private
+
+ def _get_parent(path)
+ parts = ChefFS::PathUtils.split(path)
+ return nil if parts.nil? || parts.length < 2
+ ChefFS::PathUtils.join(*parts[0..-2])
+ end
+ end
+ end
+end
diff --git a/lib/chef/chef_fs/parallelizer.rb b/lib/chef/chef_fs/parallelizer.rb
index ccbf7ad96e..32de61e048 100644
--- a/lib/chef/chef_fs/parallelizer.rb
+++ b/lib/chef/chef_fs/parallelizer.rb
@@ -86,19 +86,17 @@ class Chef
private
def worker_loop
- begin
- until @stop_thread[Thread.current]
- begin
- task = @tasks.pop
- task.call
- rescue
- puts "ERROR #{$!}"
- puts $!.backtrace
- end
+ until @stop_thread[Thread.current]
+ begin
+ task = @tasks.pop
+ task.call
+ rescue
+ puts "ERROR #{$!}"
+ puts $!.backtrace
end
- ensure
- @stop_thread.delete(Thread.current)
end
+ ensure
+ @stop_thread.delete(Thread.current)
end
end
end
diff --git a/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb b/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb
index 9d02bbab78..ab578bdb7f 100644
--- a/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb
+++ b/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb
@@ -184,9 +184,7 @@ class Chef
sleep(0.01)
end
- until @unconsumed_output.empty?
- yield @unconsumed_output.pop
- end
+ yield @unconsumed_output.pop until @unconsumed_output.empty?
# If no one is working on our tasks and we're allowed to
# work on them in the main thread, process an input to
@@ -227,9 +225,7 @@ class Chef
def stop
@unconsumed_input.clear
- while @in_process.size > 0
- sleep(0.05)
- end
+ sleep(0.05) while @in_process.size > 0
@unconsumed_output.clear
end
diff --git a/lib/chef/chef_fs/path_utils.rb b/lib/chef/chef_fs/path_utils.rb
index 7b2de5e3e0..4de23f8266 100644
--- a/lib/chef/chef_fs/path_utils.rb
+++ b/lib/chef/chef_fs/path_utils.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright 2012-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -90,7 +90,7 @@ class Chef
begin
path = File.realpath(path)
break
- rescue Errno::ENOENT
+ rescue Errno::ENOENT, Errno::EINVAL
suffix << File.basename(path)
path = parent_path
parent_path = File.dirname(path)
diff --git a/lib/chef/client.rb b/lib/chef/client.rb
index 48d7c447cc..7218c3bb49 100644
--- a/lib/chef/client.rb
+++ b/lib/chef/client.rb
@@ -3,7 +3,7 @@
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Christopher Brown (<cb@chef.io>)
# Author:: Tim Hinderliter (<tim@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,6 +22,7 @@ require "chef/config"
require "chef/mixin/params_validate"
require "chef/mixin/path_sanity"
require "chef/log"
+require "chef/deprecated"
require "chef/server_api"
require "chef/api_client"
require "chef/api_client/registration"
@@ -45,6 +46,7 @@ require "chef/formatters/doc"
require "chef/formatters/minimal"
require "chef/version"
require "chef/resource_reporter"
+require "chef/data_collector"
require "chef/audit/audit_reporter"
require "chef/run_lock"
require "chef/policy_builder"
@@ -98,14 +100,6 @@ class Chef
attr_reader :rest
#
- # A rest object with validate_utf8 set to false. This will not throw exceptions
- # on non-UTF8 strings in JSON but will sanitize them so that e.g. POSTs will
- # never fail. Cannot be configured on a request-by-request basis, so we carry
- # around another rest object for it.
- #
- attr_reader :rest_clean
-
- #
# The runner used to converge.
#
# @return [Chef::Runner]
@@ -141,6 +135,7 @@ class Chef
#
attr_reader :events
+ attr_reader :logger
#
# Creates a new Chef::Client.
#
@@ -154,7 +149,9 @@ class Chef
#
def initialize(json_attribs = nil, args = {})
@json_attribs = json_attribs || {}
- @ohai = Ohai::System.new
+ @logger = args.delete(:logger) || Chef::Log.with_child
+
+ @ohai = Ohai::System.new(logger: logger)
event_handlers = configure_formatters + configure_event_loggers
event_handlers += Array(Chef::Config[:event_handlers])
@@ -255,14 +252,17 @@ class Chef
request_id = Chef::RequestID.instance.request_id
run_context = nil
events.run_start(Chef::VERSION)
- Chef::Log.info("*** Chef #{Chef::VERSION} ***")
- Chef::Log.info("Platform: #{RUBY_PLATFORM}")
- Chef::Log.info "Chef-client pid: #{Process.pid}"
- Chef::Log.debug("Chef-client request_id: #{request_id}")
+ logger.info("*** Chef #{Chef::VERSION} ***")
+ logger.info("Platform: #{RUBY_PLATFORM}")
+ logger.info "Chef-client pid: #{Process.pid}"
+ logger.debug("Chef-client request_id: #{request_id}")
enforce_path_sanity
run_ohai
- register unless Chef::Config[:solo]
+ generate_guid
+
+ register unless Chef::Config[:solo_legacy_mode]
+ register_data_collector_reporter
load_node
@@ -270,20 +270,22 @@ class Chef
run_status.run_id = request_id
run_status.start_clock
- Chef::Log.info("Starting Chef Run for #{node.name}")
+ logger.info("Starting Chef Run for #{node.name}")
run_started
do_windows_admin_check
run_context = setup_run_context
+ load_required_recipe(@rest, run_context) unless Chef::Config[:solo_legacy_mode]
+
if Chef::Config[:audit_mode] != :audit_only
converge_error = converge_and_save(run_context)
end
if Chef::Config[:why_run] == true
# why_run should probably be renamed to why_converge
- Chef::Log.debug("Not running controls in 'why-run' mode - this mode is used to see potential converge changes")
+ logger.debug("Not running controls in 'why-run' mode - this mode is used to see potential converge changes")
elsif Chef::Config[:audit_mode] != :disabled
audit_error = run_audits(run_context)
end
@@ -292,9 +294,9 @@ class Chef
raise converge_error if converge_error
run_status.stop_clock
- Chef::Log.info("Chef Run complete in #{run_status.elapsed_time} seconds")
+ logger.info("Chef Run complete in #{run_status.elapsed_time} seconds")
run_completed_successfully
- events.run_completed(node)
+ events.run_completed(node, run_status)
# keep this inside the main loop to get exception backtraces
end_profiling
@@ -303,14 +305,14 @@ class Chef
Chef::Platform::Rebooter.reboot_if_needed!(node)
rescue Exception => run_error
# CHEF-3336: Send the error first in case something goes wrong below and we don't know why
- Chef::Log.debug("Re-raising exception: #{run_error.class} - #{run_error.message}\n#{run_error.backtrace.join("\n ")}")
+ logger.trace("Re-raising exception: #{run_error.class} - #{run_error.message}\n#{run_error.backtrace.join("\n ")}")
# If we failed really early, we may not have a run_status yet. Too early for these to be of much use.
if run_status
run_status.stop_clock
run_status.exception = run_error
run_failed
end
- events.run_failed(run_error)
+ events.run_failed(run_error, run_status)
ensure
Chef::RequestID.instance.reset_request_id
@run_status = nil
@@ -369,7 +371,7 @@ class Chef
# @api private
def default_formatter
- if (STDOUT.tty? && !Chef::Config[:force_logger]) || Chef::Config[:force_formatter]
+ if !Chef::Config[:force_logger] || Chef::Config[:force_formatter]
[:doc]
else
[:null]
@@ -393,8 +395,10 @@ class Chef
end
end
- # Rest client for use by API reporters. This rest client will not fail with an exception if
- # it is fed non-UTF8 data.
+ # A rest object with validate_utf8 set to false. This will not throw exceptions
+ # on non-UTF8 strings in JSON but will sanitize them so that e.g. POSTs will
+ # never fail. Cannot be configured on a request-by-request basis, so we carry
+ # around another rest object for it.
#
# @api private
def rest_clean(client_name = node_name, config = Chef::Config)
@@ -512,6 +516,49 @@ class Chef
end
#
+ # Adds a required recipe as specified by the Chef Server
+ #
+ # @return The modified run context
+ #
+ # @api private
+ #
+ # TODO: @rest doesn't appear to be used anywhere outside
+ # of client.register except for here. If it's common practice
+ # to create your own rest client, perhaps we should do that
+ # here but it seems more appropriate to reuse one that we
+ # know is already created. for ease of testing, we'll pass
+ # the existing rest client in as a parameter
+ #
+ def load_required_recipe(rest, run_context)
+ required_recipe_contents = rest.get("required_recipe")
+ logger.info("Required Recipe found, loading it")
+ Chef::FileCache.store("required_recipe", required_recipe_contents)
+ required_recipe_file = Chef::FileCache.load("required_recipe", false)
+
+ # TODO: add integration tests with resource reporting turned on
+ # (presumably requires changes to chef-zero)
+ #
+ # Chef::Recipe.new takes a cookbook name and a recipe name along
+ # with the run context. These names are eventually used in the
+ # resource reporter, and if the cookbook name cannot be found in the
+ # cookbook collection then we will fail with an exception. Cases where
+ # we currently also fail:
+ # - specific recipes
+ # - chef-apply would fail if resource reporting was enabled
+ #
+ recipe = Chef::Recipe.new(nil, nil, run_context)
+ recipe.from_file(required_recipe_file)
+ run_context
+ rescue Net::HTTPServerException => e
+ case e.response
+ when Net::HTTPNotFound
+ logger.trace("Required Recipe not configured on the server, skipping it")
+ else
+ raise
+ end
+ end
+
+ #
# The PolicyBuilder strategy for figuring out run list and cookbooks.
#
# @return [Chef::PolicyBuilder::Policyfile, Chef::PolicyBuilder::ExpandNodeObject]
@@ -533,12 +580,12 @@ class Chef
# @api private
#
def save_updated_node
- if Chef::Config[:solo]
+ if Chef::Config[:solo_legacy_mode]
# nothing to do
elsif policy_builder.temporary_policy?
- Chef::Log.warn("Skipping final node save because override_runlist was given")
+ logger.warn("Skipping final node save because override_runlist was given")
else
- Chef::Log.debug("Saving the current state of node #{node_name}")
+ logger.debug("Saving the current state of node #{node_name}")
node.save
end
end
@@ -554,9 +601,12 @@ class Chef
# @api private
#
def run_ohai
- filter = Chef::Config[:minimal_ohai] ? %w{fqdn machinename hostname platform platform_version os os_version} : nil
+ filter = Chef::Config[:minimal_ohai] ? %w{fqdn machinename hostname platform platform_version ohai_time os os_version} : nil
ohai.all_plugins(filter)
events.ohai_completed(node)
+ rescue Ohai::Exceptions::CriticalPluginFailure => e
+ logger.error("Critical Ohai plugins failed: #{e.message}")
+ exit(false)
end
#
@@ -608,13 +658,13 @@ class Chef
def register(client_name = node_name, config = Chef::Config)
if !config[:client_key]
events.skipping_registration(client_name, config)
- Chef::Log.debug("Client key is unspecified - skipping registration")
+ logger.trace("Client key is unspecified - skipping registration")
elsif File.exists?(config[:client_key])
events.skipping_registration(client_name, config)
- Chef::Log.debug("Client key #{config[:client_key]} is present - skipping registration")
+ logger.trace("Client key #{config[:client_key]} is present - skipping registration")
else
events.registration_start(node_name, config)
- Chef::Log.info("Client key #{config[:client_key]} is not present - registering")
+ logger.info("Client key #{config[:client_key]} is not present - registering")
Chef::ApiClient::Registration.new(node_name, config[:client_key]).run
events.registration_completed
end
@@ -662,7 +712,7 @@ class Chef
catch(:end_client_run_early) do
begin
events.converge_start(run_context)
- Chef::Log.debug("Converging node #{node_name}")
+ logger.debug("Converging node #{node_name}")
@runner = Chef::Runner.new(run_context)
@runner.converge
events.converge_complete
@@ -730,7 +780,7 @@ class Chef
def run_audits(run_context)
begin
events.audit_phase_start(run_status)
- Chef::Log.info("Starting audit phase")
+ logger.info("Starting audit phase")
auditor = Chef::Audit::Runner.new(run_context)
auditor.run
if auditor.failed?
@@ -740,7 +790,7 @@ class Chef
@events.audit_phase_complete(Chef::Audit::Logger.read_buffer)
end
rescue Exception => e
- Chef::Log.error("Audit phase failed with error message: #{e.message}")
+ logger.error("Audit phase failed with error message: #{e.message}")
@events.audit_phase_failed(e, Chef::Audit::Logger.read_buffer)
audit_exception = e
end
@@ -773,19 +823,19 @@ class Chef
#
def do_windows_admin_check
if Chef::Platform.windows?
- Chef::Log.debug("Checking for administrator privileges....")
+ logger.trace("Checking for administrator privileges....")
if !has_admin_privileges?
message = "chef-client doesn't have administrator privileges on node #{node_name}."
if Chef::Config[:fatal_windows_admin_check]
- Chef::Log.fatal(message)
- Chef::Log.fatal("fatal_windows_admin_check is set to TRUE.")
+ logger.fatal(message)
+ logger.fatal("fatal_windows_admin_check is set to TRUE.")
raise Chef::Exceptions::WindowsNotAdmin, message
else
- Chef::Log.warn("#{message} This might cause unexpected resource failures.")
+ logger.warn("#{message} This might cause unexpected resource failures.")
end
else
- Chef::Log.debug("chef-client has administrator privileges on node #{node_name}.")
+ logger.trace("chef-client has administrator privileges on node #{node_name}.")
end
end
end
@@ -891,15 +941,6 @@ class Chef
#
STDERR_FD = STDERR
- #
- # Deprecated writers
- #
-
- include Chef::Mixin::Deprecation
- deprecated_attr_writer :ohai, "There is no alternative. Leave ohai alone!"
- deprecated_attr_writer :rest, "There is no alternative. Leave rest alone!"
- deprecated_attr :runner, "There is no alternative. Leave runner alone!"
-
private
attr_reader :override_runlist
@@ -924,7 +965,7 @@ class Chef
File.open(path, "w+") do |file|
RubyProf::GraphPrinter.new(RubyProf.stop).print(file, {})
end
- Chef::Log.warn("Ruby execution profile dumped to #{path}")
+ logger.warn("Ruby execution profile dumped to #{path}")
end
def empty_directory?(path)
@@ -936,19 +977,19 @@ class Chef
end
def assert_cookbook_path_not_empty(run_context)
- if Chef::Config[:solo]
+ if Chef::Config[:solo_legacy_mode]
# Check for cookbooks in the path given
# Chef::Config[:cookbook_path] can be a string or an array
# if it's an array, go through it and check each one, raise error at the last one if no files are found
cookbook_paths = Array(Chef::Config[:cookbook_path])
- Chef::Log.debug "Loading from cookbook_path: #{cookbook_paths.map { |path| File.expand_path(path) }.join(', ')}"
+ logger.trace "Loading from cookbook_path: #{cookbook_paths.map { |path| File.expand_path(path) }.join(', ')}"
if cookbook_paths.all? { |path| empty_directory?(path) }
msg = "None of the cookbook paths set in Chef::Config[:cookbook_path], #{cookbook_paths.inspect}, contain any cookbooks"
- Chef::Log.fatal(msg)
+ logger.fatal(msg)
raise Chef::Exceptions::CookbookNotFound, msg
end
else
- Chef::Log.warn("Node #{node_name} has an empty run list.") if run_context.node.run_list.empty?
+ logger.warn("Node #{node_name} has an empty run list.") if run_context.node.run_list.empty?
end
end
@@ -957,6 +998,33 @@ class Chef
Chef::ReservedNames::Win32::Security.has_admin_privileges?
end
+
+ # Ensure that we have a GUID for this node
+ # If we've got the proper configuration, we'll simply set that.
+ # If we're registed with the data collector, we'll migrate that UUID into our configuration and use that
+ # Otherwise, we'll create a new GUID and save it
+ def generate_guid
+ Chef::Config[:chef_guid] ||=
+ if File.exists?(Chef::Config[:chef_guid_path])
+ File.read(Chef::Config[:chef_guid_path])
+ else
+ uuid = UUIDFetcher.node_uuid
+ File.open(Chef::Config[:chef_guid_path], "w+") do |fh|
+ fh.write(uuid)
+ end
+ uuid
+ end
+ end
+
+ class UUIDFetcher
+ extend Chef::DataCollector::Messages::Helpers
+ end
+
+ # Register the data collector reporter to send event information to the
+ # data collector server
+ def register_data_collector_reporter
+ events.register(Chef::DataCollector::Reporter.new) if Chef::DataCollector.register_reporter?
+ end
end
end
diff --git a/lib/chef/config.rb b/lib/chef/config.rb
index 549872bfd7..f5466ae145 100644
--- a/lib/chef/config.rb
+++ b/lib/chef/config.rb
@@ -55,8 +55,7 @@ class Chef
default :event_loggers do
evt_loggers = []
- if ChefConfig.windows? && !(Chef::Platform.windows_server_2003? ||
- Chef::Platform.windows_nano_server?)
+ if ChefConfig.windows? && !Chef::Platform.windows_nano_server?
evt_loggers << :win_evt
end
evt_loggers
diff --git a/lib/chef/config_fetcher.rb b/lib/chef/config_fetcher.rb
index acd2f07f5e..ad03c845ab 100644
--- a/lib/chef/config_fetcher.rb
+++ b/lib/chef/config_fetcher.rb
@@ -25,7 +25,7 @@ class Chef
begin
Chef::JSONCompat.from_json(config_data)
rescue Chef::Exceptions::JSON::ParseError => error
- Chef::Application.fatal!("Could not parse the provided JSON file (#{config_location}): " + error.message, 2)
+ Chef::Application.fatal!("Could not parse the provided JSON file (#{config_location}): " + error.message)
end
end
@@ -40,15 +40,15 @@ class Chef
def fetch_remote_config
http.get("")
rescue SocketError, SystemCallError, Net::HTTPServerException => error
- Chef::Application.fatal!("Cannot fetch config '#{config_location}': '#{error.class}: #{error.message}", 2)
+ Chef::Application.fatal!("Cannot fetch config '#{config_location}': '#{error.class}: #{error.message}")
end
def read_local_config
::File.read(config_location)
rescue Errno::ENOENT
- Chef::Application.fatal!("Cannot load configuration from #{config_location}", 2)
+ Chef::Application.fatal!("Cannot load configuration from #{config_location}")
rescue Errno::EACCES
- Chef::Application.fatal!("Permissions are incorrect on #{config_location}. Please chmod a+r #{config_location}", 2)
+ Chef::Application.fatal!("Permissions are incorrect on #{config_location}. Please chmod a+r #{config_location}")
end
def config_missing?
@@ -58,7 +58,7 @@ class Chef
Pathname.new(config_location).realpath.to_s
false
rescue Errno::ENOENT
- return true
+ true
end
def http
diff --git a/lib/chef/constants.rb b/lib/chef/constants.rb
index f32c3e6654..d75b632173 100644
--- a/lib/chef/constants.rb
+++ b/lib/chef/constants.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser <jkeiser@chef.io>
-# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# Copyright:: Copyright 2015-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/chef/cookbook/chefignore.rb b/lib/chef/cookbook/chefignore.rb
index 71ef53c9e5..00dc7c0939 100644
--- a/lib/chef/cookbook/chefignore.rb
+++ b/lib/chef/cookbook/chefignore.rb
@@ -33,12 +33,16 @@ class Chef
@ignores = parse_ignore_file
end
+ # @param [Array] file_list the list of cookbook files
+ # @return [Array] list of cookbook files with chefignore files removed
def remove_ignores_from(file_list)
Array(file_list).inject([]) do |unignored, file|
ignored?(file) ? unignored : unignored << file
end
end
+ # @param [String] file_name the file name to check ignored status for
+ # @return [Boolean] is the file ignored or not
def ignored?(file_name)
@ignores.any? { |glob| File.fnmatch?(glob, file_name) }
end
@@ -52,7 +56,7 @@ class Chef
ignore_globs << line.strip unless line =~ COMMENTS_AND_WHITESPACE
end
else
- Chef::Log.debug("No chefignore file found at #@ignore_file no files will be ignored")
+ Chef::Log.trace("No chefignore file found at #{@ignore_file} no files will be ignored")
end
ignore_globs
end
diff --git a/lib/chef/cookbook/cookbook_collection.rb b/lib/chef/cookbook/cookbook_collection.rb
index d06b8fd042..b2a4d5884a 100644
--- a/lib/chef/cookbook/cookbook_collection.rb
+++ b/lib/chef/cookbook/cookbook_collection.rb
@@ -1,7 +1,7 @@
#--
# Author:: Tim Hinderliter (<tim@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2010-2016 Chef Software, Inc.
+# Copyright:: Copyright 2010-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -47,10 +47,10 @@ class Chef
# Currently checks chef_version and ohai_version in the cookbook metadata
# against the running Chef::VERSION and Ohai::VERSION.
#
- # @raises [Chef::Exceptions::CookbookChefVersionMismatch] if the Chef::VERSION fails validation
- # @raises [Chef::Exceptions::CookbookOhaiVersionMismatch] if the Ohai::VERSION fails validation
+ # @raise [Chef::Exceptions::CookbookChefVersionMismatch] if the Chef::VERSION fails validation
+ # @raise [Chef::Exceptions::CookbookOhaiVersionMismatch] if the Ohai::VERSION fails validation
def validate!
- each do |cookbook_name, cookbook_version|
+ each_value do |cookbook_version|
cookbook_version.metadata.validate_chef_version!
cookbook_version.metadata.validate_ohai_version!
end
diff --git a/lib/chef/cookbook/cookbook_version_loader.rb b/lib/chef/cookbook/cookbook_version_loader.rb
index d9b027f322..b139a8d489 100644
--- a/lib/chef/cookbook/cookbook_version_loader.rb
+++ b/lib/chef/cookbook/cookbook_version_loader.rb
@@ -3,25 +3,16 @@ require "chef/cookbook_version"
require "chef/cookbook/chefignore"
require "chef/cookbook/metadata"
require "chef/util/path_helper"
+require "find"
class Chef
class Cookbook
class CookbookVersionLoader
- FILETYPES_SUBJECT_TO_IGNORE = [ :attribute_filenames,
- :definition_filenames,
- :recipe_filenames,
- :template_filenames,
- :file_filenames,
- :library_filenames,
- :resource_filenames,
- :provider_filenames]
-
UPLOADED_COOKBOOK_VERSION_FILE = ".uploaded-cookbook-version.json".freeze
attr_reader :cookbook_settings
attr_reader :cookbook_paths
- attr_reader :metadata_filenames
attr_reader :frozen
attr_reader :uploaded_cookbook_version_file
@@ -43,16 +34,7 @@ class Chef
@relative_path = /#{Regexp.escape(@cookbook_path)}\/(.+)$/
@metadata_loaded = false
@cookbook_settings = {
- :all_files => {},
- :attribute_filenames => {},
- :definition_filenames => {},
- :recipe_filenames => {},
- :template_filenames => {},
- :file_filenames => {},
- :library_filenames => {},
- :resource_filenames => {},
- :provider_filenames => {},
- :root_filenames => {},
+ :all_files => {},
}
@metadata_filenames = []
@@ -83,16 +65,6 @@ class Chef
remove_ignored_files
- load_as(:attribute_filenames, "attributes", "*.rb")
- load_as(:definition_filenames, "definitions", "*.rb")
- load_as(:recipe_filenames, "recipes", "*.rb")
- load_recursively_as(:library_filenames, "libraries", "*")
- load_recursively_as(:template_filenames, "templates", "*")
- load_recursively_as(:file_filenames, "files", "*")
- load_recursively_as(:resource_filenames, "resources", "*.rb")
- load_recursively_as(:provider_filenames, "providers", "*.rb")
- load_root_files
-
if empty?
Chef::Log.warn "Found a directory #{cookbook_name} in the cookbook path, but it contains no cookbook files. skipping."
end
@@ -107,10 +79,10 @@ class Chef
@uploaded_cookbook_version_file = File.join(cookbook_path, UPLOADED_COOKBOOK_VERSION_FILE)
end
- if File.exists?(File.join(cookbook_path, "metadata.rb"))
- @metadata_filenames << File.join(cookbook_path, "metadata.rb")
- elsif File.exists?(File.join(cookbook_path, "metadata.json"))
+ if File.exists?(File.join(cookbook_path, "metadata.json"))
@metadata_filenames << File.join(cookbook_path, "metadata.json")
+ elsif File.exists?(File.join(cookbook_path, "metadata.rb"))
+ @metadata_filenames << File.join(cookbook_path, "metadata.rb")
elsif @uploaded_cookbook_version_file
@metadata_filenames << @uploaded_cookbook_version_file
end
@@ -125,16 +97,6 @@ class Chef
Chef::CookbookVersion.new(cookbook_name, *cookbook_paths).tap do |c|
c.all_files = cookbook_settings[:all_files].values
- c.attribute_filenames = cookbook_settings[:attribute_filenames].values
- c.definition_filenames = cookbook_settings[:definition_filenames].values
- c.recipe_filenames = cookbook_settings[:recipe_filenames].values
- c.template_filenames = cookbook_settings[:template_filenames].values
- c.file_filenames = cookbook_settings[:file_filenames].values
- c.library_filenames = cookbook_settings[:library_filenames].values
- c.resource_filenames = cookbook_settings[:resource_filenames].values
- c.provider_filenames = cookbook_settings[:provider_filenames].values
- c.root_filenames = cookbook_settings[:root_filenames].values
- c.metadata_filenames = metadata_filenames
c.metadata = metadata
c.freeze_version if @frozen
@@ -168,7 +130,7 @@ class Chef
when /\.json$/
apply_json_metadata(metadata_file)
else
- raise RuntimeError, "Invalid metadata file: #{metadata_file} for cookbook: #{cookbook_version}"
+ raise "Invalid metadata file: #{metadata_file} for cookbook: #{cookbook_version}"
end
end
@@ -223,76 +185,35 @@ class Chef
# however if the file is named ".uploaded-cookbook-version.json" it is
# assumed to be managed by chef-zero and not part of the cookbook.
def load_all_files
- Dir.glob(File.join(Chef::Util::PathHelper.escape_glob_dir(cookbook_path), "*"), File::FNM_DOTMATCH).each do |fs_entry|
- if File.directory?(fs_entry)
- dir_relpath = Chef::Util::PathHelper.relative_path_from(@cookbook_path, fs_entry)
-
- next if dir_relpath.to_s.start_with?(".")
-
- Dir.glob(File.join(fs_entry, "**/*"), File::FNM_DOTMATCH).each do |file|
- next if File.directory?(file)
- file = Pathname.new(file).cleanpath.to_s
- name = Chef::Util::PathHelper.relative_path_from(@cookbook_path, file)
- cookbook_settings[:all_files][name] = file
- end
- elsif File.file?(fs_entry)
- file = Pathname.new(fs_entry).cleanpath.to_s
-
- next if File.basename(file) == UPLOADED_COOKBOOK_VERSION_FILE
-
- name = Chef::Util::PathHelper.relative_path_from(@cookbook_path, file)
- cookbook_settings[:all_files][name] = file
- else # pipes, devices, other weirdness
- next
+ return unless File.exist?(cookbook_path)
+
+ # If cookbook_path is a symlink, Find on Windows Ruby 2.3 will not traverse it.
+ # Dir.entries will do so on all platforms, so we iterate the top level using
+ # Dir.entries. Since we have different behavior at the top anyway (hidden
+ # directories at the top level are not included for backcompat), this
+ # actually keeps things a bit cleaner.
+ Dir.entries(cookbook_path).each do |top_filename|
+ # Skip top-level directories starting with "."
+ top_path = File.join(cookbook_path, top_filename)
+ next if File.directory?(top_path) && top_filename.start_with?(".")
+
+ # Use Find.find because it:
+ # (a) returns any children, recursively
+ # (b) includes top_path as well
+ # (c) skips symlinks, which is backcompat (no judgement on whether it was *right*)
+ Find.find(top_path) do |path|
+ # Only add files, not directories
+ next unless File.file?(path)
+ # Don't add .uploaded-cookbook-version.json
+ next if File.basename(path) == UPLOADED_COOKBOOK_VERSION_FILE
+
+ relative_path = Chef::Util::PathHelper.relative_path_from(cookbook_path, path)
+ path = Pathname.new(path).cleanpath.to_s
+ cookbook_settings[:all_files][relative_path] = path
end
end
end
- def load_root_files
- select_files_by_glob(File.join(Chef::Util::PathHelper.escape_glob_dir(cookbook_path), "*"), File::FNM_DOTMATCH).each do |file|
- file = Chef::Util::PathHelper.cleanpath(file)
- next if File.directory?(file)
- next if File.basename(file) == UPLOADED_COOKBOOK_VERSION_FILE
- name = Chef::Util::PathHelper.relative_path_from(@cookbook_path, file)
- cookbook_settings[:root_filenames][name] = file
- end
- end
-
- def load_recursively_as(category, category_dir, glob)
- glob_pattern = File.join(Chef::Util::PathHelper.escape_glob_dir(cookbook_path, category_dir), "**", glob)
- select_files_by_glob(glob_pattern, File::FNM_DOTMATCH).each do |file|
- file = Chef::Util::PathHelper.cleanpath(file)
- name = Chef::Util::PathHelper.relative_path_from(@cookbook_path, file)
- cookbook_settings[category][name] = file
- end
- end
-
- def load_as(category, *path_glob)
- glob_pattern = File.join(Chef::Util::PathHelper.escape_glob_dir(cookbook_path), *path_glob)
- select_files_by_glob(glob_pattern).each do |file|
- file = Chef::Util::PathHelper.cleanpath(file)
- name = Chef::Util::PathHelper.relative_path_from(@cookbook_path, file)
- cookbook_settings[category][name] = file
- end
- end
-
- # Mimic Dir.glob inside a cookbook by running `File.fnmatch?` against
- # `cookbook_settings[:all_files]`.
- #
- # @param pattern [String] a glob string passed to `File.fnmatch?`
- # @param option [Integer] Option flag to control globbing behavior. These
- # are constants defined on `File`, such as `File::FNM_DOTMATCH`.
- # `File.fnmatch?` and `Dir.glob` only take one option argument, if you
- # need to combine options, you must `|` the constants together. To make
- # `File.fnmatch?` behave like `Dir.glob`, `File::FNM_PATHNAME` is
- # always enabled.
- def select_files_by_glob(pattern, option = 0)
- combined_opts = option | File::FNM_PATHNAME
- cookbook_settings[:all_files].values.select do |path|
- File.fnmatch?(pattern, path, combined_opts)
- end
- end
-
def remove_ignored_files
cookbook_settings[:all_files].reject! do |relative_path, full_path|
chefignore.ignored?(relative_path)
@@ -300,40 +221,34 @@ class Chef
end
def apply_ruby_metadata(file)
- begin
- @metadata.from_file(file)
- rescue Chef::Exceptions::JSON::ParseError
- Chef::Log.error("Error evaluating metadata.rb for #@inferred_cookbook_name in " + file)
- raise
- end
+ @metadata.from_file(file)
+ rescue Chef::Exceptions::JSON::ParseError
+ Chef::Log.error("Error evaluating metadata.rb for #{@inferred_cookbook_name} in " + file)
+ raise
end
def apply_json_metadata(file)
- begin
- @metadata.from_json(IO.read(file))
- rescue Chef::Exceptions::JSON::ParseError
- Chef::Log.error("Couldn't parse cookbook metadata JSON for #@inferred_cookbook_name in " + file)
- raise
- end
+ @metadata.from_json(IO.read(file))
+ rescue Chef::Exceptions::JSON::ParseError
+ Chef::Log.error("Couldn't parse cookbook metadata JSON for #{@inferred_cookbook_name} in " + file)
+ raise
end
def apply_json_cookbook_version_metadata(file)
- begin
- data = Chef::JSONCompat.parse(IO.read(file))
- @metadata.from_hash(data["metadata"])
- # the JSON cookbok metadata file is only used by chef-zero.
- # The Chef Server API currently does not enforce that the metadata
- # have a `name` field, but that will cause an error when attempting
- # to load the cookbook. To keep compatibility, we fake it by setting
- # the metadata name from the cookbook version object's name.
- #
- # This behavior can be removed if/when Chef Server enforces that the
- # metadata contains a name key.
- @metadata.name(data["cookbook_name"]) unless data["metadata"].key?("name")
- rescue Chef::Exceptions::JSON::ParseError
- Chef::Log.error("Couldn't parse cookbook metadata JSON for #@inferred_cookbook_name in " + file)
- raise
- end
+ data = Chef::JSONCompat.parse(IO.read(file))
+ @metadata.from_hash(data["metadata"])
+ # the JSON cookbok metadata file is only used by chef-zero.
+ # The Chef Server API currently does not enforce that the metadata
+ # have a `name` field, but that will cause an error when attempting
+ # to load the cookbook. To keep compatibility, we fake it by setting
+ # the metadata name from the cookbook version object's name.
+ #
+ # This behavior can be removed if/when Chef Server enforces that the
+ # metadata contains a name key.
+ @metadata.name(data["cookbook_name"]) unless data["metadata"].key?("name")
+ rescue Chef::Exceptions::JSON::ParseError
+ Chef::Log.error("Couldn't parse cookbook metadata JSON for #{@inferred_cookbook_name} in " + file)
+ raise
end
def set_frozen
@@ -342,7 +257,7 @@ class Chef
data = Chef::JSONCompat.parse(IO.read(uploaded_cookbook_version_file))
@frozen = data["frozen?"]
rescue Chef::Exceptions::JSON::ParseError
- Chef::Log.error("Couldn't parse cookbook metadata JSON for #@inferred_cookbook_name in #{uploaded_cookbook_version_file}")
+ Chef::Log.error("Couldn't parse cookbook metadata JSON for #{@inferred_cookbook_name} in #{uploaded_cookbook_version_file}")
raise
end
end
diff --git a/lib/chef/cookbook/file_system_file_vendor.rb b/lib/chef/cookbook/file_system_file_vendor.rb
index 91f1c9f853..1f43095ea3 100644
--- a/lib/chef/cookbook/file_system_file_vendor.rb
+++ b/lib/chef/cookbook/file_system_file_vendor.rb
@@ -35,22 +35,21 @@ class Chef
attr_reader :repo_paths
def initialize(manifest, *repo_paths)
- @cookbook_name = manifest[:cookbook_name]
+ @cookbook_name = manifest.name
@repo_paths = repo_paths.flatten
- raise ArgumentError, "You must specify at least one repo path" if @repo_paths.empty?
+ raise ArgumentError, "You must specify at least one repo path" if repo_paths.empty?
+ end
+
+ def cookbooks
+ @cookbooks ||= Chef::CookbookLoader.new(repo_paths).load_cookbooks
end
# Implements abstract base's requirement. It looks in the
# Chef::Config.cookbook_path file hierarchy for the requested
# file.
def get_filename(filename)
- location = @repo_paths.inject(nil) do |memo, basepath|
- candidate_location = File.join(basepath, @cookbook_name, filename)
- memo = candidate_location if File.exist?(candidate_location)
- memo
- end
- raise "File #{filename} does not exist for cookbook #{@cookbook_name}" unless location
-
+ location = File.join(cookbooks[cookbook_name].root_dir, filename) if cookbooks.has_key?(cookbook_name)
+ raise "File #{filename} does not exist for cookbook #{cookbook_name}" unless location && File.exist?(location)
location
end
diff --git a/lib/chef/cookbook/gem_installer.rb b/lib/chef/cookbook/gem_installer.rb
index 06e98fe3e9..deac48ff78 100644
--- a/lib/chef/cookbook/gem_installer.rb
+++ b/lib/chef/cookbook/gem_installer.rb
@@ -1,5 +1,5 @@
#--
-# Copyright:: Copyright (c) 2010-2016 Chef Software, Inc.
+# Copyright:: Copyright (c) 2010-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -36,10 +36,12 @@ class Chef
# Installs the gems into the omnibus gemset.
#
def install
- cookbook_gems = []
+ cookbook_gems = Hash.new { |h, k| h[k] = [] }
- cookbook_collection.each do |cookbook_name, cookbook_version|
- cookbook_gems += cookbook_version.metadata.gems
+ cookbook_collection.each_value do |cookbook_version|
+ cookbook_version.metadata.gems.each do |args|
+ cookbook_gems[args.first] += args[1..-1]
+ end
end
events.cookbook_gem_start(cookbook_gems)
@@ -48,17 +50,20 @@ class Chef
begin
Dir.mktmpdir("chef-gem-bundle") do |dir|
File.open("#{dir}/Gemfile", "w+") do |tf|
- tf.puts "source '#{Chef::Config[:rubygems_url]}'"
- cookbook_gems.each do |args|
- tf.puts "gem(*#{args.inspect})"
+ Array(Chef::Config[:rubygems_url] || "https://www.rubygems.org").each do |s|
+ tf.puts "source '#{s}'"
+ end
+ cookbook_gems.each do |gem_name, args|
+ tf.puts "gem(*#{([gem_name] + args).inspect})"
end
tf.close
- Chef::Log.debug("generated Gemfile contents:")
- Chef::Log.debug(IO.read(tf.path))
+ Chef::Log.trace("generated Gemfile contents:")
+ Chef::Log.trace(IO.read(tf.path))
so = shell_out!("bundle install", cwd: dir, env: { "PATH" => path_with_prepended_ruby_bin })
Chef::Log.info(so.stdout)
end
end
+ Gem.clear_paths
rescue Exception => e
events.cookbook_gem_failed(e)
raise
diff --git a/lib/chef/cookbook/manifest_v0.rb b/lib/chef/cookbook/manifest_v0.rb
new file mode 100644
index 0000000000..fd2d62a6d4
--- /dev/null
+++ b/lib/chef/cookbook/manifest_v0.rb
@@ -0,0 +1,68 @@
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "chef/json_compat"
+require "chef/mixin/versioned_api"
+
+class Chef
+ class Cookbook
+ class ManifestV0
+ extend Chef::Mixin::VersionedAPI
+
+ minimum_api_version 0
+
+ COOKBOOK_SEGMENTS = %w{ resources providers recipes definitions libraries attributes files templates root_files }
+
+ def self.from_hash(hash)
+ response = Mash.new(hash)
+ response[:all_files] = COOKBOOK_SEGMENTS.inject([]) do |memo, segment|
+ next memo if hash[segment].nil? || hash[segment].empty?
+ hash[segment].each do |file|
+ file["name"] = "#{segment}/#{file["name"]}" unless segment == "root_files"
+ memo << file
+ end
+ response.delete(segment)
+ memo
+ end
+ response
+ end
+
+ def self.to_hash(manifest)
+ result = manifest.manifest.dup
+ result.delete("all_files")
+
+ files = manifest.by_parent_directory
+ files.keys.each_with_object(result) do |parent, memo|
+ if COOKBOOK_SEGMENTS.include?(parent)
+ memo[parent] ||= []
+ files[parent].each do |file|
+ file["name"] = file["name"].split("/")[1] unless parent == "root_files"
+ file.delete("full_path")
+ memo[parent] << file
+ end
+ end
+ end
+ # Ensure all segments are set to [] if they don't exist.
+ # See https://github.com/chef/chef/issues/6044
+ COOKBOOK_SEGMENTS.each do |segment|
+ result[segment] ||= []
+ end
+
+ result.merge({ "frozen?" => manifest.frozen_version?, "chef_type" => "cookbook_version" })
+ end
+ end
+ end
+end
diff --git a/lib/chef/platform/handler_map.rb b/lib/chef/cookbook/manifest_v2.rb
index da8f84237f..2a5f1fb5e6 100644
--- a/lib/chef/platform/handler_map.rb
+++ b/lib/chef/cookbook/manifest_v2.rb
@@ -1,5 +1,3 @@
-#
-# Author:: John Keiser (<jkeiser@chef.io>)
# Copyright:: Copyright 2015-2016, Chef Software Inc.
# License:: Apache License, Version 2.0
#
@@ -14,27 +12,30 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
-require "chef/node_map"
+require "chef/json_compat"
+require "chef/mixin/versioned_api"
class Chef
- class Platform
- class HandlerMap < Chef::NodeMap
- #
- # "provides" lines with identical filters sort by class name (ascending).
- #
- def compare_matchers(key, new_matcher, matcher)
- cmp = super
- if cmp == 0
- # Sort by class name (ascending) as well, if all other properties
- # are exactly equal
- if new_matcher[:value].is_a?(Class) && !new_matcher[:override]
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:value].name }
- end
- end
- cmp
+ class Cookbook
+ class ManifestV2
+ extend Chef::Mixin::VersionedAPI
+
+ minimum_api_version 2
+
+ def self.from_hash(hash)
+ Chef::Log.trace "processing manifest: #{hash}"
+ Mash.new hash
end
+
+ def self.to_hash(manifest)
+ result = manifest.manifest.dup
+ result["all_files"].map! { |file| file.delete("full_path"); file }
+ result["frozen?"] = manifest.frozen_version?
+ result["chef_type"] = "cookbook_version"
+ result.to_hash
+ end
+
end
end
end
diff --git a/lib/chef/cookbook/metadata.rb b/lib/chef/cookbook/metadata.rb
index 603f80748c..c378cb75b6 100644
--- a/lib/chef/cookbook/metadata.rb
+++ b/lib/chef/cookbook/metadata.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: AJ Christensen (<aj@chef.io>)
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -44,13 +44,8 @@ class Chef
LICENSE = "license".freeze
PLATFORMS = "platforms".freeze
DEPENDENCIES = "dependencies".freeze
- RECOMMENDATIONS = "recommendations".freeze
- SUGGESTIONS = "suggestions".freeze
- CONFLICTING = "conflicting".freeze
PROVIDING = "providing".freeze
- REPLACING = "replacing".freeze
ATTRIBUTES = "attributes".freeze
- GROUPINGS = "groupings".freeze
RECIPES = "recipes".freeze
VERSION = "version".freeze
SOURCE_URL = "source_url".freeze
@@ -62,17 +57,12 @@ class Chef
COMPARISON_FIELDS = [ :name, :description, :long_description, :maintainer,
:maintainer_email, :license, :platforms, :dependencies,
- :recommendations, :suggestions, :conflicting, :providing,
- :replacing, :attributes, :groupings, :recipes, :version,
+ :providing, :attributes, :recipes, :version,
:source_url, :issues_url, :privacy, :chef_versions, :ohai_versions,
:gems ]
VERSION_CONSTRAINTS = { :depends => DEPENDENCIES,
- :recommends => RECOMMENDATIONS,
- :suggests => SUGGESTIONS,
- :conflicts => CONFLICTING,
:provides => PROVIDING,
- :replaces => REPLACING,
:chef_version => CHEF_VERSIONS,
:ohai_version => OHAI_VERSIONS }
@@ -81,15 +71,9 @@ class Chef
attr_reader :platforms
attr_reader :dependencies
- attr_reader :recommendations
- attr_reader :suggestions
- attr_reader :conflicting
attr_reader :providing
- attr_reader :replacing
attr_reader :attributes
- attr_reader :groupings
attr_reader :recipes
- attr_reader :version
# @return [Array<Gem::Dependency>] Array of supported Chef versions
attr_reader :chef_versions
@@ -115,18 +99,13 @@ class Chef
@long_description = ""
@license = "All rights reserved"
- @maintainer = nil
- @maintainer_email = nil
+ @maintainer = ""
+ @maintainer_email = ""
@platforms = Mash.new
@dependencies = Mash.new
- @recommendations = Mash.new
- @suggestions = Mash.new
- @conflicting = Mash.new
@providing = Mash.new
- @replacing = Mash.new
@attributes = Mash.new
- @groupings = Mash.new
@recipes = Mash.new
@version = Version.new("0.0.0")
@source_url = ""
@@ -306,7 +285,7 @@ class Chef
# versions<Array>:: Returns the list of versions for the platform
def depends(cookbook, *version_args)
if cookbook == name
- Chef::Log.warn "Ignoring self-dependency in cookbook #{name}, please remove it (in the future this will be fatal)."
+ raise "Cookbook depends on itself in cookbook #{name}, please remove the this unnecessary self-dependency"
else
version = new_args_format(:depends, cookbook, version_args)
constraint = validate_version_constraint(:depends, cookbook, version)
@@ -315,57 +294,6 @@ class Chef
@dependencies[cookbook]
end
- # Adds a recommendation for another cookbook, with version checking strings.
- #
- # === Parameters
- # cookbook<String>:: The cookbook
- # version<String>:: A version constraint of the form "OP VERSION",
- # where OP is one of < <= = > >= ~> and VERSION has
- # the form x.y.z or x.y.
- #
- # === Returns
- # versions<Array>:: Returns the list of versions for the platform
- def recommends(cookbook, *version_args)
- version = new_args_format(:recommends, cookbook, version_args)
- constraint = validate_version_constraint(:recommends, cookbook, version)
- @recommendations[cookbook] = constraint.to_s
- @recommendations[cookbook]
- end
-
- # Adds a suggestion for another cookbook, with version checking strings.
- #
- # === Parameters
- # cookbook<String>:: The cookbook
- # version<String>:: A version constraint of the form "OP VERSION",
- # where OP is one of < <= = > >= ~> and VERSION has the
- # formx.y.z or x.y.
- #
- # === Returns
- # versions<Array>:: Returns the list of versions for the platform
- def suggests(cookbook, *version_args)
- version = new_args_format(:suggests, cookbook, version_args)
- constraint = validate_version_constraint(:suggests, cookbook, version)
- @suggestions[cookbook] = constraint.to_s
- @suggestions[cookbook]
- end
-
- # Adds a conflict for another cookbook, with version checking strings.
- #
- # === Parameters
- # cookbook<String>:: The cookbook
- # version<String>:: A version constraint of the form "OP VERSION",
- # where OP is one of < <= = > >= ~> and VERSION has
- # the form x.y.z or x.y.
- #
- # === Returns
- # versions<Array>:: Returns the list of versions for the platform
- def conflicts(cookbook, *version_args)
- version = new_args_format(:conflicts, cookbook, version_args)
- constraint = validate_version_constraint(:conflicts, cookbook, version)
- @conflicting[cookbook] = constraint.to_s
- @conflicting[cookbook]
- end
-
# Adds a recipe, definition, or resource provided by this cookbook.
#
# Recipes are specified as normal
@@ -387,22 +315,6 @@ class Chef
@providing[cookbook]
end
- # Adds a cookbook that is replaced by this one, with version checking strings.
- #
- # === Parameters
- # cookbook<String>:: The cookbook we replace
- # version<String>:: A version constraint of the form "OP VERSION",
- # where OP is one of < <= = > >= ~> and VERSION has the form x.y.z or x.y.
- #
- # === Returns
- # versions<Array>:: Returns the list of versions for the platform
- def replaces(cookbook, *version_args)
- version = new_args_format(:replaces, cookbook, version_args)
- constraint = validate_version_constraint(:replaces, cookbook, version)
- @replacing[cookbook] = constraint.to_s
- @replacing[cookbook]
- end
-
# Metadata DSL to set a valid chef_version. May be declared multiple times
# with the result being 'OR'd such that if any statements match, the version
# is considered supported. Uses Gem::Requirement for its implementation.
@@ -462,7 +374,7 @@ class Chef
cookbook.fully_qualified_recipe_names.map do |recipe_name|
unqualified_name =
if recipe_name =~ /::default$/
- self.name.to_s
+ name.to_s
else
recipe_name
end
@@ -519,18 +431,6 @@ class Chef
@attributes[name]
end
- def grouping(name, options)
- validate(
- options,
- {
- :title => { :kind_of => String },
- :description => { :kind_of => String },
- }
- )
- @groupings[name] = options
- @groupings[name]
- end
-
# Convert an Array of Gem::Dependency objects (chef_version/ohai_version) to an Array.
#
# Gem::Dependencey#to_s is not useful, and there is no #to_json defined on it or its component
@@ -567,29 +467,24 @@ class Chef
def to_hash
{
- NAME => self.name,
- DESCRIPTION => self.description,
- LONG_DESCRIPTION => self.long_description,
- MAINTAINER => self.maintainer,
- MAINTAINER_EMAIL => self.maintainer_email,
- LICENSE => self.license,
- PLATFORMS => self.platforms,
- DEPENDENCIES => self.dependencies,
- RECOMMENDATIONS => self.recommendations,
- SUGGESTIONS => self.suggestions,
- CONFLICTING => self.conflicting,
- PROVIDING => self.providing,
- REPLACING => self.replacing,
- ATTRIBUTES => self.attributes,
- GROUPINGS => self.groupings,
- RECIPES => self.recipes,
- VERSION => self.version,
- SOURCE_URL => self.source_url,
- ISSUES_URL => self.issues_url,
- PRIVACY => self.privacy,
- CHEF_VERSIONS => gem_requirements_to_array(*self.chef_versions),
- OHAI_VERSIONS => gem_requirements_to_array(*self.ohai_versions),
- GEMS => self.gems,
+ NAME => name,
+ DESCRIPTION => description,
+ LONG_DESCRIPTION => long_description,
+ MAINTAINER => maintainer,
+ MAINTAINER_EMAIL => maintainer_email,
+ LICENSE => license,
+ PLATFORMS => platforms,
+ DEPENDENCIES => dependencies,
+ PROVIDING => providing,
+ ATTRIBUTES => attributes,
+ RECIPES => recipes,
+ VERSION => version,
+ SOURCE_URL => source_url,
+ ISSUES_URL => issues_url,
+ PRIVACY => privacy,
+ CHEF_VERSIONS => gem_requirements_to_array(*chef_versions),
+ OHAI_VERSIONS => gem_requirements_to_array(*ohai_versions),
+ GEMS => gems,
}
end
@@ -598,7 +493,7 @@ class Chef
end
def self.from_hash(o)
- cm = self.new()
+ cm = new()
cm.from_hash(o)
cm
end
@@ -612,13 +507,8 @@ class Chef
@license = o[LICENSE] if o.has_key?(LICENSE)
@platforms = o[PLATFORMS] if o.has_key?(PLATFORMS)
@dependencies = handle_deprecated_constraints(o[DEPENDENCIES]) if o.has_key?(DEPENDENCIES)
- @recommendations = handle_deprecated_constraints(o[RECOMMENDATIONS]) if o.has_key?(RECOMMENDATIONS)
- @suggestions = handle_deprecated_constraints(o[SUGGESTIONS]) if o.has_key?(SUGGESTIONS)
- @conflicting = handle_deprecated_constraints(o[CONFLICTING]) if o.has_key?(CONFLICTING)
@providing = o[PROVIDING] if o.has_key?(PROVIDING)
- @replacing = handle_deprecated_constraints(o[REPLACING]) if o.has_key?(REPLACING)
@attributes = o[ATTRIBUTES] if o.has_key?(ATTRIBUTES)
- @groupings = o[GROUPINGS] if o.has_key?(GROUPINGS)
@recipes = o[RECIPES] if o.has_key?(RECIPES)
@version = o[VERSION] if o.has_key?(VERSION)
@source_url = o[SOURCE_URL] if o.has_key?(SOURCE_URL)
@@ -632,7 +522,7 @@ class Chef
def self.from_json(string)
o = Chef::JSONCompat.from_json(string)
- self.from_hash(o)
+ from_hash(o)
end
def self.validate_json(json_str)
@@ -641,8 +531,8 @@ class Chef
VERSION_CONSTRAINTS.each do |dependency_type, hash_key|
if dependency_group = o[hash_key]
dependency_group.each do |cb_name, constraints|
- if metadata.respond_to?(method_name)
- metadata.public_send(method_name, cb_name, *Array(constraints))
+ if metadata.respond_to?(dependency_type)
+ metadata.public_send(dependency_type, cb_name, *Array(constraints))
end
end
end
@@ -705,7 +595,7 @@ class Chef
# Validates that the Ohai::VERSION of the running chef-client matches one of the
# configured ohai_version statements in this cookbooks metadata.
#
- # @raises [Chef::Exceptions::CookbookOhaiVersionMismatch] if the cookbook fails validation
+ # @raise [Chef::Exceptions::CookbookOhaiVersionMismatch] if the cookbook fails validation
def validate_ohai_version!
unless gem_dep_matches?("ohai", Gem::Version.new(Ohai::VERSION), *ohai_versions)
raise Exceptions::CookbookOhaiVersionMismatch.new(Ohai::VERSION, name, version, *ohai_versions)
@@ -715,14 +605,22 @@ class Chef
# Validates that the Chef::VERSION of the running chef-client matches one of the
# configured chef_version statements in this cookbooks metadata.
#
- # @raises [Chef::Exceptions::CookbookChefVersionMismatch] if the cookbook fails validation
+ # @raise [Chef::Exceptions::CookbookChefVersionMismatch] if the cookbook fails validation
def validate_chef_version!
unless gem_dep_matches?("chef", Gem::Version.new(Chef::VERSION), *chef_versions)
raise Exceptions::CookbookChefVersionMismatch.new(Chef::VERSION, name, version, *chef_versions)
end
end
- private
+ def method_missing(method, *args, &block)
+ if block_given?
+ super
+ else
+ Chef::Log.trace "ignoring method #{method} on cookbook with name #{name}, possible typo or the ghosts of metadata past or future?"
+ end
+ end
+
+ private
# Helper to match a gem style version (ohai_version/chef_version) against a set of
# Gem::Dependency version constraints. If none are present, it always matches. if
@@ -854,12 +752,12 @@ INVALID
return if !options[:choice].is_a?(Array) || options[:choice].empty?
if options[:default].is_a?(String) && options[:default] != ""
- raise ArgumentError, "Default must be one of your choice values!" if options[:choice].index(options[:default]) == nil
+ raise ArgumentError, "Default must be one of your choice values!" if options[:choice].index(options[:default]).nil?
end
if options[:default].is_a?(Array) && !options[:default].empty?
options[:default].each do |val|
- raise ArgumentError, "Default values must be a subset of your choice values!" if options[:choice].index(val) == nil
+ raise ArgumentError, "Default values must be a subset of your choice values!" if options[:choice].index(val).nil?
end
end
end
diff --git a/lib/chef/cookbook/remote_file_vendor.rb b/lib/chef/cookbook/remote_file_vendor.rb
index e63d094dc4..7db0cb8686 100644
--- a/lib/chef/cookbook/remote_file_vendor.rb
+++ b/lib/chef/cookbook/remote_file_vendor.rb
@@ -30,7 +30,7 @@ class Chef
def initialize(manifest, rest)
@manifest = manifest
- @cookbook_name = @manifest[:cookbook_name] || @manifest[:name]
+ @cookbook_name = @manifest.name
@rest = rest
end
@@ -44,8 +44,8 @@ class Chef
raise "get_filename: Cannot determine segment/filename for incoming filename #{filename}"
end
- raise "No such segment #{segment} in cookbook #{@cookbook_name}" unless @manifest[segment]
- found_manifest_record = @manifest[segment].find { |manifest_record| manifest_record[:path] == filename }
+ raise "No such segment #{segment} in cookbook #{@cookbook_name}" unless @manifest.files_for(segment)
+ found_manifest_record = @manifest.files_for(segment).find { |manifest_record| manifest_record[:path] == filename }
raise "No such file #{filename} in #{@cookbook_name}" unless found_manifest_record
cache_filename = File.join("cookbooks", @cookbook_name, found_manifest_record["path"])
@@ -65,11 +65,11 @@ class Chef
if current_checksum != found_manifest_record["checksum"]
raw_file = @rest.streaming_request(found_manifest_record[:url])
- Chef::Log.debug("Storing updated #{cache_filename} in the cache.")
+ Chef::Log.trace("Storing updated #{cache_filename} in the cache.")
Chef::FileCache.move_to(raw_file.path, cache_filename)
else
- Chef::Log.debug("Not fetching #{cache_filename}, as the cache is up to date.")
- Chef::Log.debug("Current checksum: #{current_checksum}; manifest checksum: #{found_manifest_record['checksum']})")
+ Chef::Log.trace("Not fetching #{cache_filename}, as the cache is up to date.")
+ Chef::Log.trace("Current checksum: #{current_checksum}; manifest checksum: #{found_manifest_record['checksum']})")
end
full_path_cache_filename = Chef::FileCache.load(cache_filename, false)
diff --git a/lib/chef/cookbook/synchronizer.rb b/lib/chef/cookbook/synchronizer.rb
index ec388746e9..6df39fece3 100644
--- a/lib/chef/cookbook/synchronizer.rb
+++ b/lib/chef/cookbook/synchronizer.rb
@@ -43,7 +43,7 @@ class Chef
end
def cleanup_file_cache
- unless Chef::Config[:solo] || skip_removal
+ unless Chef::Config[:solo_legacy_mode] || skip_removal
# Delete each file in the cache that we didn't encounter in the
# manifest.
cache.find(File.join(%w{cookbooks ** {*,.*}})).each do |cache_filename|
@@ -62,22 +62,17 @@ class Chef
# Synchronizes the locally cached copies of cookbooks with the files on the
# server.
class CookbookSynchronizer
- CookbookFile = Struct.new(:cookbook, :segment, :manifest_record)
+ CookbookFile = Struct.new(:cookbook, :manifest_record)
attr_accessor :remove_obsoleted_files
def initialize(cookbooks_by_name, events)
- @eager_segments = Chef::CookbookVersion::COOKBOOK_SEGMENTS.dup
- unless Chef::Config[:no_lazy_load]
- @eager_segments.delete(:files)
- @eager_segments.delete(:templates)
- end
- @eager_segments.freeze
-
@cookbooks_by_name, @events = cookbooks_by_name, events
@cookbook_full_file_paths = {}
@remove_obsoleted_files = true
+
+ @lazy_files = {}
end
def cache
@@ -101,14 +96,25 @@ class Chef
end
def cookbook_segment(cookbook_name, segment)
- @cookbooks_by_name[cookbook_name].manifest[segment]
+ @cookbooks_by_name[cookbook_name].files_for(segment)
end
def files
+ lazy = unless Chef::Config[:no_lazy_load]
+ %w{ files templates }
+ else
+ []
+ end
+
@files ||= cookbooks.inject([]) do |memo, cookbook|
- @eager_segments.each do |segment|
- cookbook.manifest[segment].each do |manifest_record|
- memo << CookbookFile.new(cookbook, segment, manifest_record)
+ cookbook.each_file do |manifest_record|
+ part = manifest_record[:name].split("/")[0]
+ if lazy.include?(part)
+ manifest_record[:lazy] = true
+ @lazy_files[cookbook] ||= []
+ @lazy_files[cookbook] << manifest_record
+ else
+ memo << CookbookFile.new(cookbook, manifest_record)
end
end
memo
@@ -142,28 +148,31 @@ class Chef
# true:: Always returns true
def sync_cookbooks
Chef::Log.info("Loading cookbooks [#{cookbooks.map { |ckbk| ckbk.name + '@' + ckbk.version }.join(', ')}]")
- Chef::Log.debug("Cookbooks detail: #{cookbooks.inspect}")
+ Chef::Log.trace("Cookbooks detail: #{cookbooks.inspect}")
clear_obsoleted_cookbooks
queue = Chef::Util::ThreadedJobQueue.new
+ Chef::Log.warn("skipping cookbook synchronization! DO NOT LEAVE THIS ENABLED IN PRODUCTION!!!") if Chef::Config[:skip_cookbook_sync]
files.each do |file|
queue << lambda do |lock|
full_file_path = sync_file(file)
- lock.synchronize {
+ lock.synchronize do
# Save the full_path of the downloaded file to be restored in the manifest later
save_full_file_path(file, full_file_path)
mark_file_synced(file)
- }
+ end
end
end
@events.cookbook_sync_start(cookbook_count)
queue.process(Chef::Config[:cookbook_sync_threads])
+ # Ensure that cookbooks know where they're rooted at, for manifest purposes.
+ ensure_cookbook_paths
# Update the full file paths in the manifest
- update_cookbook_filenames()
+ update_cookbook_filenames
rescue Exception => e
@events.cookbook_sync_failed(cookbooks, e)
@@ -176,9 +185,8 @@ class Chef
# Saves the full_path to the file of the cookbook to be updated
# in the manifest later
def save_full_file_path(file, full_path)
- @cookbook_full_file_paths[file.cookbook] ||= {}
- @cookbook_full_file_paths[file.cookbook][file.segment] ||= [ ]
- @cookbook_full_file_paths[file.cookbook][file.segment] << full_path
+ @cookbook_full_file_paths[file.cookbook] ||= []
+ @cookbook_full_file_paths[file.cookbook] << full_path
end
# remove cookbooks that are not referenced in the expanded run_list at all
@@ -229,10 +237,19 @@ class Chef
end
def update_cookbook_filenames
- @cookbook_full_file_paths.each do |cookbook, file_segments|
- file_segments.each do |segment, full_paths|
- cookbook.replace_segment_filenames(segment, full_paths)
- end
+ @cookbook_full_file_paths.each do |cookbook, full_paths|
+ cookbook.all_files = full_paths
+ end
+
+ @lazy_files.each do |cookbook, lazy_files|
+ cookbook.cookbook_manifest.add_files_to_manifest(lazy_files)
+ end
+ end
+
+ def ensure_cookbook_paths
+ cookbooks.each do |cookbook|
+ cb_dir = File.join(Chef::Config[:file_cache_path], "cookbooks", cookbook.name)
+ cookbook.root_paths = Array(cb_dir)
end
end
@@ -255,7 +272,7 @@ class Chef
download_file(file.manifest_record["url"], cache_filename)
@events.updated_cookbook_file(file.cookbook.name, cache_filename)
else
- Chef::Log.debug("Not storing #{cache_filename}, as the cache is up to date.")
+ Chef::Log.trace("Not storing #{cache_filename}, as the cache is up to date.")
end
# Load the file in the cache and return the full file path to the loaded file
@@ -263,10 +280,7 @@ class Chef
end
def cached_copy_up_to_date?(local_path, expected_checksum)
- if Chef::Config[:skip_cookbook_sync]
- Chef::Log.warn "skipping cookbook synchronization! DO NOT LEAVE THIS ENABLED IN PRODUCTION!!!"
- return true
- end
+ return true if Chef::Config[:skip_cookbook_sync]
if cache.has_key?(local_path)
current_checksum = CookbookVersion.checksum_cookbook_file(cache.load(local_path, false))
expected_checksum == current_checksum
@@ -291,7 +305,7 @@ class Chef
end
def server_api
- Chef::ServerAPI.new(Chef::Config[:chef_server_url])
+ Thread.current[:server_api] ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], keepalives: true)
end
end
diff --git a/lib/chef/cookbook/syntax_check.rb b/lib/chef/cookbook/syntax_check.rb
index f8559433dc..b94b14fe6a 100644
--- a/lib/chef/cookbook/syntax_check.rb
+++ b/lib/chef/cookbook/syntax_check.rb
@@ -110,7 +110,7 @@ class Chef
end
def remove_uninteresting_ruby_files(file_list)
- file_list.reject { |f| f =~ %r{#{cookbook_path}/(files|templates)/} }
+ file_list.reject { |f| f =~ %r{#{Regexp.quote(cookbook_path)}/(files|templates)/} }
end
def ruby_files
@@ -124,7 +124,7 @@ class Chef
def untested_ruby_files
ruby_files.reject do |file|
if validated?(file)
- Chef::Log.debug("Ruby file #{file} is unchanged, skipping syntax check")
+ Chef::Log.trace("Ruby file #{file} is unchanged, skipping syntax check")
true
else
false
@@ -139,7 +139,7 @@ class Chef
def untested_template_files
template_files.reject do |file|
if validated?(file)
- Chef::Log.debug("Template #{file} is unchanged, skipping syntax check")
+ Chef::Log.trace("Template #{file} is unchanged, skipping syntax check")
true
else
false
@@ -170,12 +170,12 @@ class Chef
end
def validate_template(erb_file)
- Chef::Log.debug("Testing template #{erb_file} for syntax errors...")
+ Chef::Log.trace("Testing template #{erb_file} for syntax errors...")
validate_erb_file_inline(erb_file)
end
def validate_ruby_file(ruby_file)
- Chef::Log.debug("Testing #{ruby_file} for syntax errors...")
+ Chef::Log.trace("Testing #{ruby_file} for syntax errors...")
validate_ruby_file_inline(ruby_file)
end
diff --git a/lib/chef/cookbook_loader.rb b/lib/chef/cookbook_loader.rb
index bff77fa13b..1cd498496a 100644
--- a/lib/chef/cookbook_loader.rb
+++ b/lib/chef/cookbook_loader.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# Copyright:: Copyright 2009-2016, Daniel DeLeo
# License:: Apache License, Version 2.0
#
@@ -78,7 +78,7 @@ class Chef
# @api private
def load_cookbooks_without_shadow_warning
preload_cookbooks
- @loaders_by_name.each do |cookbook_name, _loaders|
+ @loaders_by_name.each_key do |cookbook_name|
load_cookbook(cookbook_name)
end
@cookbooks_by_name
@@ -137,11 +137,19 @@ class Chef
alias :key? :has_key?
def each
- @cookbooks_by_name.keys.sort { |a, b| a.to_s <=> b.to_s }.each do |cname|
+ @cookbooks_by_name.keys.sort_by(&:to_s).each do |cname|
yield(cname, @cookbooks_by_name[cname])
end
end
+ def each_key(&block)
+ cookbook_names.each(&block)
+ end
+
+ def each_value(&block)
+ values.each(&block)
+ end
+
def cookbook_names
@cookbooks_by_name.keys.sort
end
diff --git a/lib/chef/cookbook_manifest.rb b/lib/chef/cookbook_manifest.rb
index d6de9dd029..526c183339 100644
--- a/lib/chef/cookbook_manifest.rb
+++ b/lib/chef/cookbook_manifest.rb
@@ -15,7 +15,10 @@
# limitations under the License.
require "forwardable"
+require "chef/mixin/versioned_api"
require "chef/util/path_helper"
+require "chef/cookbook/manifest_v0"
+require "chef/cookbook/manifest_v2"
require "chef/log"
class Chef
@@ -24,17 +27,11 @@ class Chef
# to a Chef Server.
class CookbookManifest
- # Duplicates the same constant in CookbookVersion. We cannot remove it
- # there because it is treated by other code as part of CookbookVersion's
- # public API (also used in some deprecated methods).
- COOKBOOK_SEGMENTS = [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates, :root_files ].freeze
-
extend Forwardable
attr_reader :cookbook_version
def_delegator :@cookbook_version, :root_paths
- def_delegator :@cookbook_version, :segment_filenames
def_delegator :@cookbook_version, :name
def_delegator :@cookbook_version, :identifier
def_delegator :@cookbook_version, :metadata
@@ -56,10 +53,7 @@ class Chef
# the format used by the `cookbook_artifacts` endpoint (for policyfiles).
# Setting this option also changes the behavior of #save_url and
# #force_save_url such that CookbookVersions will be uploaded to the new
- # `cookbook_artifacts` API. This endpoint is currently under active
- # development and the format is expected to change frequently, therefore
- # the result of #manifest, #to_hash, and #to_json will not be stable when
- # `policy_mode` is enabled.
+ # `cookbook_artifacts` API.
def initialize(cookbook_version, policy_mode: false)
@cookbook_version = cookbook_version
@policy_mode = !!policy_mode
@@ -126,10 +120,7 @@ class Chef
end
def to_hash
- result = manifest.dup
- result["frozen?"] = frozen_version?
- result["chef_type"] = "cookbook_version"
- result.to_hash
+ CookbookManifestVersions.to_hash(self)
end
def to_json(*a)
@@ -164,15 +155,56 @@ class Chef
# make the corresponding changes to the cookbook_version object. Required
# to provide backward compatibility with CookbookVersion#manifest= method.
def update_from(new_manifest)
- @manifest = Mash.new new_manifest
+ @manifest = Chef::CookbookManifestVersions.from_hash(new_manifest)
@checksums = extract_checksums_from_manifest(@manifest)
@manifest_records_by_path = extract_manifest_records_by_path(@manifest)
+ end
+
+ # @api private
+ # takes a list of hashes
+ def add_files_to_manifest(files)
+ manifest[:all_files].concat(Array(files))
+ @checksums = extract_checksums_from_manifest(@manifest)
+ @manifest_records_by_path = extract_manifest_records_by_path(@manifest)
+ end
- COOKBOOK_SEGMENTS.each do |segment|
- next unless @manifest.has_key?(segment)
- filenames = @manifest[segment].map { |manifest_record| manifest_record["name"] }
+ def files_for(part)
+ return root_files if part.to_s == "root_files"
+ manifest[:all_files].select do |file|
+ seg = file[:name].split("/")[0]
+ part.to_s == seg
+ end
+ end
- cookbook_version.replace_segment_filenames(segment, filenames)
+ def each_file(excluded_parts: [], &block)
+ excluded_parts = Array(excluded_parts).map { |p| p.to_s }
+
+ manifest[:all_files].each do |file|
+ seg = file[:name].split("/")[0]
+ next if excluded_parts.include?(seg)
+ yield file if block_given?
+ end
+ end
+
+ def by_parent_directory
+ @by_parent_directory ||=
+ manifest[:all_files].inject({}) do |memo, file|
+ parts = file[:name].split("/")
+ parent = if parts.length == 1
+ "root_files"
+ else
+ parts[0]
+ end
+
+ memo[parent] ||= []
+ memo[parent] << file
+ memo
+ end
+ end
+
+ def root_files
+ manifest[:all_files].select do |file|
+ file[:name].split("/").length == 1
end
end
@@ -186,15 +218,7 @@ class Chef
# See #preferred_manifest_record for a description an individual manifest record.
def generate_manifest
manifest = Mash.new({
- :recipes => Array.new,
- :definitions => Array.new,
- :libraries => Array.new,
- :attributes => Array.new,
- :files => Array.new,
- :templates => Array.new,
- :resources => Array.new,
- :providers => Array.new,
- :root_files => Array.new,
+ all_files: Array.new,
})
@checksums = {}
@@ -203,24 +227,24 @@ class Chef
raise "Cookbook #{name} does not have root_paths! Cannot generate manifest."
end
- COOKBOOK_SEGMENTS.each do |segment|
- segment_filenames(segment).each do |segment_file|
- next if File.directory?(segment_file)
+ @cookbook_version.all_files.each do |file|
+ next if File.directory?(file)
- path, specificity = parse_segment_file_from_root_paths(segment, segment_file)
- file_name = File.basename(path)
+ name, path, specificity = parse_file_from_root_paths(file)
- csum = checksum_cookbook_file(segment_file)
- @checksums[csum] = segment_file
- rs = Mash.new({
- :name => file_name,
- :path => path,
- :checksum => csum,
- :specificity => specificity,
- })
+ csum = checksum_cookbook_file(file)
+ @checksums[csum] = file
+ rs = Mash.new({
+ :name => name,
+ :path => path,
+ :checksum => csum,
+ :specificity => specificity,
+ # full_path is not a part of the normal manifest, but is very useful to keep around.
+ # uploaders should strip this out.
+ :full_path => file,
+ })
- manifest[segment] << rs
- end
+ manifest[:all_files] << rs
end
manifest[:metadata] = metadata
@@ -238,38 +262,42 @@ class Chef
@manifest = manifest
end
- def parse_segment_file_from_root_paths(segment, segment_file)
+ def parse_file_from_root_paths(file)
root_paths.each do |root_path|
- pathname = Chef::Util::PathHelper.relative_path_from(root_path, segment_file)
+ pathname = Chef::Util::PathHelper.relative_path_from(root_path, file)
parts = pathname.each_filename.take(2)
# Check if path is actually under root_path
next if parts[0] == ".."
- if segment == :templates || segment == :files
+
+ # if we have a root_file, such as metadata.rb, the first part will be "."
+ return [ pathname.to_s, pathname.to_s, "default" ] if parts.length == 1
+
+ segment = parts[0]
+
+ name = File.join(segment, pathname.basename.to_s)
+
+ if segment == "templates" || segment == "files"
# Check if pathname looks like files/foo or templates/foo (unscoped)
if pathname.each_filename.to_a.length == 2
# Use root_default in case the same path exists at root_default and default
- return [ pathname.to_s, "root_default" ]
+ return [ name, pathname.to_s, "root_default" ]
else
- return [ pathname.to_s, parts[1] ]
+ return [ name, pathname.to_s, parts[1] ]
end
else
- return [ pathname.to_s, "default" ]
+ return [ name, pathname.to_s, "default" ]
end
end
- Chef::Log.error("Cookbook file #{segment_file} not under cookbook root paths #{root_paths.inspect}.")
- raise "Cookbook file #{segment_file} not under cookbook root paths #{root_paths.inspect}."
+ Chef::Log.error("Cookbook file #{file} not under cookbook root paths #{root_paths.inspect}.")
+ raise "Cookbook file #{file} not under cookbook root paths #{root_paths.inspect}."
end
def extract_checksums_from_manifest(manifest)
- checksums = {}
- COOKBOOK_SEGMENTS.each do |segment|
- next unless manifest.has_key?(segment)
- manifest[segment].each do |manifest_record|
- checksums[manifest_record[:checksum]] = nil
- end
+ manifest[:all_files].inject({}) do |memo, manifest_record|
+ memo[manifest_record[:checksum]] = nil
+ memo
end
- checksums
end
def checksum_cookbook_file(filepath)
@@ -277,14 +305,20 @@ class Chef
end
def extract_manifest_records_by_path(manifest)
- manifest_records_by_path = {}
- COOKBOOK_SEGMENTS.each do |segment|
- next unless manifest.has_key?(segment)
- manifest[segment].each do |manifest_record|
- manifest_records_by_path[manifest_record[:path]] = manifest_record
- end
+ manifest[:all_files].inject({}) do |memo, manifest_record|
+ memo[manifest_record[:path]] = manifest_record
+ memo
end
- manifest_records_by_path
end
+
+ end
+ class CookbookManifestVersions
+
+ extend Chef::Mixin::VersionedAPIFactory
+ add_versioned_api_class Chef::Cookbook::ManifestV0
+ add_versioned_api_class Chef::Cookbook::ManifestV2
+
+ def_versioned_delegator :from_hash
+ def_versioned_delegator :to_hash
end
end
diff --git a/lib/chef/cookbook_site_streaming_uploader.rb b/lib/chef/cookbook_site_streaming_uploader.rb
index 9fb8d0d4bc..e4b45532f0 100644
--- a/lib/chef/cookbook_site_streaming_uploader.rb
+++ b/lib/chef/cookbook_site_streaming_uploader.rb
@@ -31,7 +31,7 @@ class Chef
# inspired by http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html
class CookbookSiteStreamingUploader
- DefaultHeaders = { "accept" => "application/json", "x-chef-version" => ::Chef::VERSION }
+ DefaultHeaders = { "accept" => "application/json", "x-chef-version" => ::Chef::VERSION } # rubocop:disable Naming/ConstantName
class << self
@@ -41,21 +41,19 @@ class Chef
tmp_cookbook_dir = tmp_cookbook_path.path
File.unlink(tmp_cookbook_dir)
FileUtils.mkdir_p(tmp_cookbook_dir)
- Chef::Log.debug("Staging at #{tmp_cookbook_dir}")
+ Chef::Log.trace("Staging at #{tmp_cookbook_dir}")
checksums_to_on_disk_paths = cookbook.checksums
- Chef::CookbookVersion::COOKBOOK_SEGMENTS.each do |segment|
- cookbook.manifest[segment].each do |manifest_record|
- path_in_cookbook = manifest_record[:path]
- on_disk_path = checksums_to_on_disk_paths[manifest_record[:checksum]]
- dest = File.join(tmp_cookbook_dir, cookbook.name.to_s, path_in_cookbook)
- FileUtils.mkdir_p(File.dirname(dest))
- Chef::Log.debug("Staging #{on_disk_path} to #{dest}")
- FileUtils.cp(on_disk_path, dest)
- end
+ cookbook.each_file do |manifest_record|
+ path_in_cookbook = manifest_record[:path]
+ on_disk_path = checksums_to_on_disk_paths[manifest_record[:checksum]]
+ dest = File.join(tmp_cookbook_dir, cookbook.name.to_s, path_in_cookbook)
+ FileUtils.mkdir_p(File.dirname(dest))
+ Chef::Log.trace("Staging #{on_disk_path} to #{dest}")
+ FileUtils.cp(on_disk_path, dest)
end
# First, generate metadata
- Chef::Log.debug("Generating metadata")
+ Chef::Log.trace("Generating metadata")
kcm = Chef::Knife::CookbookMetadata.new
kcm.config[:cookbook_path] = [ tmp_cookbook_dir ]
kcm.name_args = [ cookbook.name.to_s ]
@@ -149,11 +147,11 @@ class Chef
alias :to_s :body
# BUGBUG this makes the response compatible with what respsonse_steps expects to test headers (response.headers[] -> response[])
- def headers
+ def headers # rubocop:disable Lint/NestedMethodDefinition
self
end
- def status
+ def status # rubocop:disable Lint/NestedMethodDefinition
code.to_i
end
end
diff --git a/lib/chef/cookbook_uploader.rb b/lib/chef/cookbook_uploader.rb
index 95c27799ec..e79a7774eb 100644
--- a/lib/chef/cookbook_uploader.rb
+++ b/lib/chef/cookbook_uploader.rb
@@ -40,7 +40,7 @@ class Chef
def initialize(cookbooks, opts = {})
@opts = opts
@cookbooks = Array(cookbooks)
- @rest = opts[:rest] || Chef::ServerAPI.new(Chef::Config[:chef_server_url])
+ @rest = opts[:rest] || Chef::ServerAPI.new(Chef::Config[:chef_server_url], version_class: Chef::CookbookManifestVersions)
@concurrency = opts[:concurrency] || 10
@policy_mode = opts[:policy_mode] || false
end
@@ -55,7 +55,7 @@ class Chef
checksum_files.merge!(cb.checksums)
end
- checksums = checksum_files.inject({}) { |memo, elt| memo[elt.first] = nil ; memo }
+ checksums = checksum_files.inject({}) { |memo, elt| memo[elt.first] = nil; memo }
new_sandbox = rest.post("sandboxes", { :checksums => checksums })
Chef::Log.info("Uploading files")
@@ -71,14 +71,14 @@ class Chef
Chef::Log.info("Uploading #{checksum_files[checksum]} (checksum hex = #{checksum}) to #{info['url']}")
queue << uploader_function_for(checksum_files[checksum], checksum, info["url"], checksums_to_upload)
else
- Chef::Log.debug("#{checksum_files[checksum]} has not changed")
+ Chef::Log.trace("#{checksum_files[checksum]} has not changed")
end
end
queue.process(@concurrency)
sandbox_url = new_sandbox["uri"]
- Chef::Log.debug("Committing sandbox")
+ Chef::Log.trace("Committing sandbox")
# Retry if S3 is claims a checksum doesn't exist (the eventual
# in eventual consistency)
retries = 0
@@ -145,7 +145,6 @@ class Chef
Chef::Log.info("Validating templates")
exit(1) unless syntax_checker.validate_templates
Chef::Log.info("Syntax OK")
- true
end
end
diff --git a/lib/chef/cookbook_version.rb b/lib/chef/cookbook_version.rb
index 1e903608b5..7bb3f3385f 100644
--- a/lib/chef/cookbook_version.rb
+++ b/lib/chef/cookbook_version.rb
@@ -4,7 +4,7 @@
# Author:: Tim Hinderliter (<tim@chef.io>)
# Author:: Seth Falcon (<seth@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -37,50 +37,23 @@ class Chef
class CookbookVersion
include Comparable
+ extend Forwardable
+
+ def_delegator :@cookbook_manifest, :files_for
+ def_delegator :@cookbook_manifest, :each_file
COOKBOOK_SEGMENTS = [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates, :root_files ]
- attr_accessor :all_files
+ attr_reader :all_files
attr_accessor :root_paths
- attr_accessor :definition_filenames
- attr_accessor :template_filenames
- attr_accessor :file_filenames
- attr_accessor :library_filenames
- attr_accessor :resource_filenames
- attr_accessor :provider_filenames
- attr_accessor :root_filenames
attr_accessor :name
- attr_accessor :metadata_filenames
-
- def status=(new_status)
- Chef.log_deprecation("Deprecated method `status' called. This method will be removed.")
- @status = new_status
- end
-
- def status
- Chef.log_deprecation("Deprecated method `status' called. This method will be removed.")
- @status
- end
# A Chef::Cookbook::Metadata object. It has a setter that fixes up the
# metadata to add descriptions of the recipes contained in this
# CookbookVersion.
attr_reader :metadata
- # attribute_filenames also has a setter that has non-default
- # functionality.
- attr_reader :attribute_filenames
-
- # recipe_filenames also has a setter that has non-default
- # functionality.
- attr_reader :recipe_filenames
-
- attr_reader :recipe_filenames_by_name
- attr_reader :attribute_filenames_by_short_filename
-
- attr_accessor :chef_server_rest
-
# The `identifier` field is used for cookbook_artifacts, which are
# organized on the chef server according to their content. If the
# policy_mode option to CookbookManifest is set to true it will include
@@ -96,12 +69,17 @@ class Chef
root_paths[0]
end
+ def all_files=(files)
+ @all_files = Array(files)
+ cookbook_manifest.reset!
+ end
+
# This is the one and only method that knows how cookbook files'
# checksums are generated.
def self.checksum_cookbook_file(filepath)
Chef::Digester.generate_md5_checksum_for_file(filepath)
rescue Errno::ENOENT
- Chef::Log.debug("File #{filepath} does not exist, so there is no checksum to generate")
+ Chef::Log.trace("File #{filepath} does not exist, so there is no checksum to generate")
nil
end
@@ -118,23 +96,10 @@ class Chef
@root_paths = root_paths
@frozen = false
- @attribute_filenames = Array.new
- @definition_filenames = Array.new
- @template_filenames = Array.new
- @file_filenames = Array.new
- @recipe_filenames = Array.new
- @recipe_filenames_by_name = Hash.new
- @library_filenames = Array.new
- @resource_filenames = Array.new
- @provider_filenames = Array.new
- @metadata_filenames = Array.new
- @root_filenames = Array.new
-
@all_files = Array.new
- # deprecated
- @status = :ready
@file_vendor = nil
+ @cookbook_manifest = Chef::CookbookManifest.new(self)
@metadata = Chef::Cookbook::Metadata.new
@chef_server_rest = chef_server_rest
end
@@ -163,26 +128,40 @@ class Chef
"#{name}-#{version}"
end
- def attribute_filenames=(*filenames)
- @attribute_filenames = filenames.flatten
- @attribute_filenames_by_short_filename = filenames_by_name(attribute_filenames)
- attribute_filenames
+ def attribute_filenames_by_short_filename
+ @attribute_filenames_by_short_filename ||= begin
+ name_map = filenames_by_name(files_for("attributes"))
+ root_alias = cookbook_manifest.root_files.find { |record| record[:name] == "attributes.rb" }
+ name_map["default"] = root_alias[:full_path] if root_alias
+ name_map
+ end
+ end
+
+ def recipe_filenames_by_name
+ @recipe_filenames_by_name ||= begin
+ name_map = filenames_by_name(files_for("recipes"))
+ root_alias = cookbook_manifest.root_files.find { |record| record[:name] == "recipe.rb" }
+ if root_alias
+ Chef::Log.error("Cookbook #{name} contains both recipe.rb and and recipes/default.rb, ignoring recipes/default.rb") if name_map["default"]
+ name_map["default"] = root_alias[:full_path]
+ end
+ name_map
+ end
end
def metadata=(metadata)
@metadata = metadata
@metadata.recipes_from_cookbook_version(self)
- @metadata
end
- ## BACKCOMPAT/DEPRECATED - Remove these and fix breakage before release [DAN - 5/20/2010]##
- alias :attribute_files :attribute_filenames
- alias :attribute_files= :attribute_filenames=
-
def manifest
cookbook_manifest.manifest
end
+ def manifest=(new_manifest)
+ cookbook_manifest.update_from(new_manifest)
+ end
+
# Returns a hash of checksums to either nil or the on disk path (which is
# done by generate_manifest).
def checksums
@@ -193,36 +172,23 @@ class Chef
cookbook_manifest.manifest_records_by_path
end
- def manifest=(new_manifest)
- cookbook_manifest.update_from(new_manifest)
- end
-
# Return recipe names in the form of cookbook_name::recipe_name
def fully_qualified_recipe_names
- results = Array.new
- recipe_filenames_by_name.each_key do |rname|
- results << "#{name}::#{rname}"
+ files_for("recipes").inject([]) do |memo, recipe|
+ rname = recipe[:name].split("/")[1]
+ rname = File.basename(rname, ".rb")
+ memo << "#{name}::#{rname}"
+ memo
end
- results
- end
-
- def recipe_filenames=(*filenames)
- @recipe_filenames = filenames.flatten
- @recipe_filenames_by_name = filenames_by_name(recipe_filenames)
- recipe_filenames
end
- ## BACKCOMPAT/DEPRECATED - Remove these and fix breakage before release [DAN - 5/20/2010]##
- alias :recipe_files :recipe_filenames
- alias :recipe_files= :recipe_filenames=
-
# called from DSL
def load_recipe(recipe_name, run_context)
unless recipe_filenames_by_name.has_key?(recipe_name)
raise Chef::Exceptions::RecipeNotFound, "could not find recipe #{recipe_name} for cookbook #{name}"
end
- Chef::Log.debug("Found recipe #{recipe_name} in cookbook #{name}")
+ Chef::Log.trace("Found recipe #{recipe_name} in cookbook #{name}")
recipe = Chef::Recipe.new(name, recipe_name, run_context)
recipe_filename = recipe_filenames_by_name[recipe_name]
@@ -235,41 +201,7 @@ class Chef
end
def segment_filenames(segment)
- unless COOKBOOK_SEGMENTS.include?(segment)
- raise ArgumentError, "invalid segment #{segment}: must be one of #{COOKBOOK_SEGMENTS.join(', ')}"
- end
-
- case segment.to_sym
- when :resources
- @resource_filenames
- when :providers
- @provider_filenames
- when :recipes
- @recipe_filenames
- when :libraries
- @library_filenames
- when :definitions
- @definition_filenames
- when :attributes
- @attribute_filenames
- when :files
- @file_filenames
- when :templates
- @template_filenames
- when :root_files
- @root_filenames
- end
- end
-
- def replace_segment_filenames(segment, filenames)
- case segment.to_sym
- when :recipes
- self.recipe_filenames = filenames
- when :attributes
- self.attribute_filenames = filenames
- else
- segment_filenames(segment).replace(filenames)
- end
+ files_for(segment).map { |f| f["full_path"] || File.join(root_dir, f["path"]) }
end
# Query whether a template file +template_filename+ is available. File
@@ -316,13 +248,13 @@ class Chef
error_message << error_locations.join("\n")
existing_files = segment_filenames(segment)
# Strip the root_dir prefix off all files for readability
- pretty_existing_files = existing_files.map { |path|
+ pretty_existing_files = existing_files.map do |path|
if root_dir
path[root_dir.length + 1..-1]
else
path
end
- }
+ end
# Show the files that the cookbook does have. If the user made a typo,
# hopefully they'll see it here.
unless pretty_existing_files.empty?
@@ -349,7 +281,7 @@ class Chef
filenames_by_pref = Hash.new
preferences.each { |pref| filenames_by_pref[pref] = Array.new }
- manifest[segment].each do |manifest_record|
+ files_for(segment).each do |manifest_record|
manifest_record_path = manifest_record[:path]
# find the NON SPECIFIC filenames, but prefer them by filespecificity.
@@ -389,7 +321,7 @@ class Chef
records_by_pref = Hash.new
preferences.each { |pref| records_by_pref[pref] = Array.new }
- manifest[segment].each do |manifest_record|
+ files_for(segment).each do |manifest_record|
manifest_record_path = manifest_record[:path]
# extract the preference part from the path.
@@ -469,12 +401,22 @@ class Chef
end
private :preferences_for_path
+ def display
+ output = Mash.new
+ output["cookbook_name"] = name
+ output["name"] = full_name
+ output["frozen?"] = frozen_version?
+ output["metadata"] = metadata.to_hash
+ output["version"] = version
+ output.merge(cookbook_manifest.by_parent_directory)
+ end
+
def self.from_hash(o)
cookbook_version = new(o["cookbook_name"] || o["name"])
# We want the Chef::Cookbook::Metadata object to always be inflated
- cookbook_version.metadata = Chef::Cookbook::Metadata.from_hash(o["metadata"])
cookbook_version.manifest = o
+ cookbook_version.metadata = Chef::Cookbook::Metadata.from_hash(o["metadata"])
cookbook_version.identifier = o["identifier"] if o.key?("identifier")
# We don't need the following step when we decide to stop supporting deprecated operators in the metadata (e.g. <<, >>)
@@ -484,42 +426,10 @@ class Chef
cookbook_version
end
- def self.json_create(o)
- Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please use Chef::CookbookVersion#from_hash")
- from_hash(o)
- end
-
def self.from_cb_artifact_data(o)
from_hash(o)
end
- # @deprecated This method was used by the Ruby Chef Server and is no longer
- # needed. There is no replacement.
- def generate_manifest_with_urls
- Chef.log_deprecation("Deprecated method #generate_manifest_with_urls.")
-
- rendered_manifest = manifest.dup
- COOKBOOK_SEGMENTS.each do |segment|
- if rendered_manifest.has_key?(segment)
- rendered_manifest[segment].each do |manifest_record|
- url_options = { :cookbook_name => name.to_s, :cookbook_version => version, :checksum => manifest_record["checksum"] }
- manifest_record["url"] = yield(url_options)
- end
- end
- end
- rendered_manifest
- end
-
- def to_hash
- # TODO: this should become deprecated when the API for CookbookManifest becomes stable
- cookbook_manifest.to_hash
- end
-
- def to_json(*a)
- # TODO: this should become deprecated when the API for CookbookManifest becomes stable
- cookbook_manifest.to_json
- end
-
def metadata_json_file
File.join(root_paths[0], "metadata.json")
end
@@ -538,22 +448,12 @@ class Chef
# REST API
##
- def save_url
- # TODO: this should become deprecated when the API for CookbookManifest becomes stable
- cookbook_manifest.save_url
- end
-
- def force_save_url
- # TODO: this should become deprecated when the API for CookbookManifest becomes stable
- cookbook_manifest.force_save_url
- end
-
def chef_server_rest
- @chef_server_rest ||= self.chef_server_rest
+ @chef_server_rest ||= chef_server_rest
end
def self.chef_server_rest
- Chef::ServerAPI.new(Chef::Config[:chef_server_url])
+ Chef::ServerAPI.new(Chef::Config[:chef_server_url], { version_class: Chef::CookbookManifestVersions })
end
def destroy
@@ -599,20 +499,20 @@ class Chef
end
end
- def <=>(o)
- raise Chef::Exceptions::CookbookVersionNameMismatch if self.name != o.name
+ def <=>(other)
+ raise Chef::Exceptions::CookbookVersionNameMismatch if name != other.name
# FIXME: can we change the interface to the Metadata class such
# that metadata.version returns a Chef::Version instance instead
# of a string?
- Chef::Version.new(self.version) <=> Chef::Version.new(o.version)
+ Chef::Version.new(version) <=> Chef::Version.new(other.version)
end
- private
-
def cookbook_manifest
@cookbook_manifest ||= CookbookManifest.new(self)
end
+ private
+
def find_preferred_manifest_record(node, segment, filename)
preferences = preferences_for_path(node, segment, filename)
@@ -620,15 +520,21 @@ class Chef
preferences.find { |preferred_filename| manifest_records_by_path[preferred_filename] }
end
- # For each filename, produce a mapping of base filename (i.e. recipe name
+ # For each manifest record, produce a mapping of base filename (i.e. recipe name
+ # or attribute file) to on disk location
+ def relative_paths_by_name(records)
+ records.select { |record| record[:name] =~ /\.rb$/ }.inject({}) { |memo, record| memo[File.basename(record[:name], ".rb")] = record[:path]; memo }
+ end
+
+ # For each manifest record, produce a mapping of base filename (i.e. recipe name
# or attribute file) to on disk location
- def filenames_by_name(filenames)
- filenames.select { |filename| filename =~ /\.rb$/ }.inject({}) { |memo, filename| memo[File.basename(filename, ".rb")] = filename ; memo }
+ def filenames_by_name(records)
+ records.select { |record| record[:name] =~ /\.rb$/ }.inject({}) { |memo, record| memo[File.basename(record[:name], ".rb")] = record[:full_path]; memo }
end
def file_vendor
unless @file_vendor
- @file_vendor = Chef::Cookbook::FileVendor.create_from_manifest(manifest)
+ @file_vendor = Chef::Cookbook::FileVendor.create_from_manifest(cookbook_manifest)
end
@file_vendor
end
diff --git a/lib/chef/data_bag.rb b/lib/chef/data_bag.rb
index 0444e2abbe..c5e2b38c94 100644
--- a/lib/chef/data_bag.rb
+++ b/lib/chef/data_bag.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Nuo Yan (<nuo@chef.io>)
# Author:: Christopher Brown (<cb@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,13 +33,15 @@ class Chef
include Chef::Mixin::ParamsValidate
VALID_NAME = /^[\.\-[:alnum:]_]+$/
-
- attr_accessor :chef_server_rest
+ RESERVED_NAMES = /^(node|role|environment|client)$/
def self.validate_name!(name)
unless name =~ VALID_NAME
raise Exceptions::InvalidDataBagName, "DataBags must have a name matching #{VALID_NAME.inspect}, you gave #{name.inspect}"
end
+ if name =~ RESERVED_NAMES
+ raise Exceptions::InvalidDataBagName, "DataBags may not have a name matching #{RESERVED_NAMES.inspect}, you gave #{name.inspect}"
+ end
end
# Create a new Chef::DataBag
@@ -78,12 +80,6 @@ class Chef
Chef::ServerAPI.new(Chef::Config[:chef_server_url])
end
- # Create a Chef::Role from JSON
- def self.json_create(o)
- Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please use Chef::DataBag#from_hash")
- from_hash(o)
- end
-
def self.from_hash(o)
bag = new
bag.name(o["name"])
@@ -91,7 +87,7 @@ class Chef
end
def self.list(inflate = false)
- if Chef::Config[:solo]
+ if Chef::Config[:solo_legacy_mode]
paths = Array(Chef::Config[:data_bag_path])
names = []
paths.each do |path|
@@ -118,7 +114,7 @@ class Chef
# Load a Data Bag by name via either the RESTful API or local data_bag_path if run in solo mode
def self.load(name)
- if Chef::Config[:solo]
+ if Chef::Config[:solo_legacy_mode]
paths = Array(Chef::Config[:data_bag_path])
data_bag = {}
paths.each do |path|
@@ -137,7 +133,7 @@ class Chef
end
end
end
- return data_bag
+ data_bag
else
Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("data/#{name}")
end
diff --git a/lib/chef/data_bag_item.rb b/lib/chef/data_bag_item.rb
index d9ad771202..388da78dad 100644
--- a/lib/chef/data_bag_item.rb
+++ b/lib/chef/data_bag_item.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Nuo Yan (<nuo@chef.io>)
# Author:: Christopher Brown (<cb@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software, Inc.
+# Copyright:: Copyright 2009-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -38,8 +38,6 @@ class Chef
VALID_ID = /^[\.\-[:alnum:]_]+$/
- attr_accessor :chef_server_rest
-
def self.validate_id!(id_str)
if id_str.nil? || ( id_str !~ VALID_ID )
raise Exceptions::InvalidDataBagItemID, "Data Bag items must have an id matching #{VALID_ID.inspect}, you gave: #{id_str.inspect}"
@@ -66,15 +64,12 @@ class Chef
Chef::ServerAPI.new(Chef::Config[:chef_server_url])
end
- def raw_data
- @raw_data
- end
-
def validate_id!(id_str)
self.class.validate_id!(id_str)
end
def raw_data=(new_data)
+ new_data = Mash.new(new_data)
unless new_data.respond_to?(:[]) && new_data.respond_to?(:keys)
raise Exceptions::ValidationFailed, "Data Bag Items must contain a Hash or Mash!"
end
@@ -107,9 +102,9 @@ class Chef
end
def to_hash
- result = self.raw_data.dup
+ result = raw_data.dup
result["chef_type"] = "data_bag_item"
- result["data_bag"] = self.data_bag.to_s
+ result["data_bag"] = data_bag.to_s
result
end
@@ -132,22 +127,16 @@ class Chef
item = new
item.data_bag(h.delete("data_bag")) if h.key?("data_bag")
if h.key?("raw_data")
- item.raw_data = Mash.new(h["raw_data"])
+ item.raw_data = h["raw_data"]
else
item.raw_data = h
end
item
end
- # Create a Chef::DataBagItem from JSON
- def self.json_create(o)
- Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please use Chef::DataBagItem#from_hash")
- from_hash(o)
- end
-
# Load a Data Bag Item by name via either the RESTful API or local data_bag_path if run in solo mode
def self.load(data_bag, name)
- if Chef::Config[:solo]
+ if Chef::Config[:solo_legacy_mode]
bag = Chef::DataBag.load(data_bag)
raise Exceptions::InvalidDataBagItemID, "Item #{name} not found in data bag #{data_bag}. Other items found: #{bag.keys.join(", ")}" unless bag.include?(name)
item = bag[name]
@@ -207,7 +196,7 @@ class Chef
end
def pretty_print(pretty_printer)
- pretty_printer.pp({ "data_bag_item('#{data_bag}', '#{id}')" => self.to_hash })
+ pretty_printer.pp({ "data_bag_item('#{data_bag}', '#{id}')" => to_hash })
end
def id
diff --git a/lib/chef/data_collector.rb b/lib/chef/data_collector.rb
new file mode 100644
index 0000000000..5cc92a34e4
--- /dev/null
+++ b/lib/chef/data_collector.rb
@@ -0,0 +1,565 @@
+#
+# Author:: Adam Leff (<adamleff@chef.io>)
+# Author:: Ryan Cragun (<ryan@chef.io>)
+#
+# Copyright:: Copyright 2012-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "uri"
+require "chef/server_api"
+require "chef/http/simple_json"
+require "chef/event_dispatch/base"
+require "chef/data_collector/messages"
+require "chef/data_collector/resource_report"
+require "ostruct"
+require "set"
+
+class Chef
+
+ # == Chef::DataCollector
+ # Provides methods for determinine whether a reporter should be registered.
+ class DataCollector
+
+ # Whether or not to enable data collection:
+ # * always disabled for why run mode
+ # * disabled when the user sets `Chef::Config[:data_collector][:mode]` to a
+ # value that excludes the mode (client or solo) that we are running as
+ # * disabled in solo mode if the user did not configure the auth token
+ # * disabled if `Chef::Config[:data_collector][:server_url]` is set to a
+ # falsey value
+ def self.register_reporter?
+ if why_run?
+ Chef::Log.trace("data collector is disabled for why run mode")
+ return false
+ end
+ unless reporter_enabled_for_current_mode?
+ Chef::Log.trace("data collector is configured to only run in " \
+ "#{Chef::Config[:data_collector][:mode].inspect} modes, disabling it")
+ return false
+ end
+ unless data_collector_url_configured? || data_collector_output_locations_configured?
+ Chef::Log.trace("Neither data collector URL or output locations have been configured, disabling data collector")
+ return false
+ end
+ if solo? && !token_auth_configured?
+ Chef::Log.trace("Data collector token must be configured to use Chef Automate data collector with Chef Solo")
+ end
+ if !solo? && token_auth_configured?
+ Chef::Log.warn("Data collector token authentication is not recommended for client-server mode" \
+ "Please upgrade Chef Server to 12.11.0 and remove the token from your config file " \
+ "to use key based authentication instead")
+ end
+ true
+ end
+
+ def self.data_collector_url_configured?
+ !!Chef::Config[:data_collector][:server_url]
+ end
+
+ def self.data_collector_output_locations_configured?
+ !!Chef::Config[:data_collector][:output_locations]
+ end
+
+ def self.why_run?
+ !!Chef::Config[:why_run]
+ end
+
+ def self.token_auth_configured?
+ !!Chef::Config[:data_collector][:token]
+ end
+
+ def self.solo?
+ !!Chef::Config[:solo] || !!Chef::Config[:local_mode]
+ end
+
+ def self.reporter_enabled_for_current_mode?
+ if Chef::Config[:solo] || Chef::Config[:local_mode]
+ acceptable_modes = [:solo, :both]
+ else
+ acceptable_modes = [:client, :both]
+ end
+
+ acceptable_modes.include?(Chef::Config[:data_collector][:mode])
+ end
+
+ # == Chef::DataCollector::Reporter
+ # Provides an event handler that can be registered to report on Chef
+ # run data. Unlike the existing Chef::ResourceReporter event handler,
+ # the DataCollector handler is not tied to a Chef Server / Chef Reporting
+ # and exports its data through a webhook-like mechanism to a configured
+ # endpoint.
+ class Reporter < EventDispatch::Base
+ attr_reader :all_resource_reports, :status, :exception, :error_descriptions,
+ :expanded_run_list, :run_context, :run_status, :http,
+ :current_resource_report, :enabled, :deprecations
+
+ def initialize
+ validate_data_collector_server_url!
+ validate_data_collector_output_locations! if data_collector_output_locations
+ @all_resource_reports = []
+ @current_resource_loaded = nil
+ @error_descriptions = {}
+ @expanded_run_list = {}
+ @deprecations = Set.new
+ @enabled = true
+
+ @http = setup_http_client(data_collector_server_url)
+ if data_collector_output_locations
+ @http_output_locations = setup_http_output_locations if data_collector_output_locations[:urls]
+ end
+ end
+
+ # see EventDispatch::Base#run_started
+ # Upon receipt, we will send our run start message to the
+ # configured DataCollector endpoint. Depending on whether
+ # the user has configured raise_on_failure, if we cannot
+ # send the message, we will either disable the DataCollector
+ # Reporter for the duration of this run, or we'll raise an
+ # exception.
+ def run_started(current_run_status)
+ update_run_status(current_run_status)
+
+ message = Chef::DataCollector::Messages.run_start_message(current_run_status)
+ disable_reporter_on_error do
+ send_to_data_collector(message)
+ end
+ send_to_output_locations(message) if data_collector_output_locations
+ end
+
+ # see EventDispatch::Base#run_completed
+ # Upon receipt, we will send our run completion message to the
+ # configured DataCollector endpoint.
+ def run_completed(node)
+ send_run_completion(status: "success")
+ end
+
+ # see EventDispatch::Base#run_failed
+ def run_failed(exception)
+ send_run_completion(status: "failure")
+ end
+
+ # see EventDispatch::Base#converge_start
+ # Upon receipt, we stash the run_context for use at the
+ # end of the run in order to determine what resource+action
+ # combinations have not yet fired so we can report on
+ # unprocessed resources.
+ def converge_start(run_context)
+ @run_context = run_context
+ end
+
+ # see EventDispatch::Base#converge_complete
+ # At the end of the converge, we add any unprocessed resources
+ # to our report list.
+ def converge_complete
+ detect_unprocessed_resources
+ end
+
+ # see EventDispatch::Base#converge_failed
+ # At the end of the converge, we add any unprocessed resources
+ # to our report list
+ def converge_failed(exception)
+ detect_unprocessed_resources
+ end
+
+ # see EventDispatch::Base#resource_current_state_loaded
+ # Create a new ResourceReport instance that we'll use to track
+ # the state of this resource during the run. Nested resources are
+ # ignored as they are assumed to be an inline resource of a custom
+ # resource, and we only care about tracking top-level resources.
+ def resource_current_state_loaded(new_resource, action, current_resource)
+ return if nested_resource?(new_resource)
+ initialize_resource_report_if_needed(new_resource, action, current_resource)
+ end
+
+ # see EventDispatch::Base#resource_up_to_date
+ # Mark our ResourceReport status accordingly
+ def resource_up_to_date(new_resource, action)
+ initialize_resource_report_if_needed(new_resource, action)
+ current_resource_report.up_to_date unless nested_resource?(new_resource)
+ end
+
+ # see EventDispatch::Base#resource_skipped
+ # If this is a top-level resource, we create a ResourceReport
+ # instance (because a skipped resource does not trigger the
+ # resource_current_state_loaded event), and flag it as skipped.
+ def resource_skipped(new_resource, action, conditional)
+ return if nested_resource?(new_resource)
+
+ initialize_resource_report_if_needed(new_resource, action)
+ current_resource_report.skipped(conditional)
+ end
+
+ # see EventDispatch::Base#resource_updated
+ # Flag the current ResourceReport instance as updated (as long as it's
+ # a top-level resource).
+ def resource_updated(new_resource, action)
+ initialize_resource_report_if_needed(new_resource, action)
+ current_resource_report.updated unless nested_resource?(new_resource)
+ end
+
+ # see EventDispatch::Base#resource_failed
+ # Flag the current ResourceReport as failed and supply the exception as
+ # long as it's a top-level resource, and update the run error text
+ # with the proper Formatter.
+ def resource_failed(new_resource, action, exception)
+ initialize_resource_report_if_needed(new_resource, action)
+ current_resource_report.failed(exception) unless nested_resource?(new_resource)
+ update_error_description(
+ Formatters::ErrorMapper.resource_failed(
+ new_resource,
+ action,
+ exception
+ ).for_json
+ )
+ end
+
+ # see EventDispatch::Base#resource_completed
+ # Mark the ResourceReport instance as finished (for timing details).
+ # This marks the end of this resource during this run.
+ def resource_completed(new_resource)
+ if current_resource_report && !nested_resource?(new_resource)
+ current_resource_report.finish
+ add_resource_report(current_resource_report)
+ clear_current_resource_report
+ end
+ end
+
+ # see EventDispatch::Base#run_list_expanded
+ # The expanded run list is stored for later use by the run_completed
+ # event and message.
+ def run_list_expanded(run_list_expansion)
+ @expanded_run_list = run_list_expansion
+ end
+
+ # see EventDispatch::Base#run_list_expand_failed
+ # The run error text is updated with the output of the appropriate
+ # formatter.
+ def run_list_expand_failed(node, exception)
+ update_error_description(
+ Formatters::ErrorMapper.run_list_expand_failed(
+ node,
+ exception
+ ).for_json
+ )
+ end
+
+ # see EventDispatch::Base#cookbook_resolution_failed
+ # The run error text is updated with the output of the appropriate
+ # formatter.
+ def cookbook_resolution_failed(expanded_run_list, exception)
+ update_error_description(
+ Formatters::ErrorMapper.cookbook_resolution_failed(
+ expanded_run_list,
+ exception
+ ).for_json
+ )
+ end
+
+ # see EventDispatch::Base#cookbook_sync_failed
+ # The run error text is updated with the output of the appropriate
+ # formatter.
+ def cookbook_sync_failed(cookbooks, exception)
+ update_error_description(
+ Formatters::ErrorMapper.cookbook_sync_failed(
+ cookbooks,
+ exception
+ ).for_json
+ )
+ end
+
+ # see EventDispatch::Base#deprecation
+ # Append a received deprecation to the list of deprecations
+ def deprecation(message, location = caller(2..2)[0])
+ add_deprecation(message.message, message.url, location)
+ end
+
+ private
+
+ # Selects the type of HTTP client to use based on whether we are using
+ # token-based or signed header authentication. Token authentication is
+ # intended to be used primarily for Chef Solo in which case no signing
+ # key will be available (in which case `Chef::ServerAPI.new()` would
+ # raise an exception.
+ def setup_http_client(url)
+ if data_collector_token.nil?
+ Chef::ServerAPI.new(url, validate_utf8: false)
+ else
+ Chef::HTTP::SimpleJSON.new(url, validate_utf8: false)
+ end
+ end
+
+ def setup_http_output_locations
+ Chef::Config[:data_collector][:output_locations][:urls].each_with_object({}) do |location_url, http_output_locations|
+ http_output_locations[location_url] = setup_http_client(location_url)
+ end
+ end
+
+ #
+ # Yields to the passed-in block (which is expected to be some interaction
+ # with the DataCollector endpoint). If some communication failure occurs,
+ # either disable any future communications to the DataCollector endpoint, or
+ # raise an exception (if the user has set
+ # Chef::Config.data_collector.raise_on_failure to true.)
+ #
+ # @param block [Proc] A ruby block to run. Ignored if a command is given.
+ #
+ def disable_reporter_on_error
+ yield
+ rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
+ Errno::ECONNREFUSED, EOFError, Net::HTTPBadResponse,
+ Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError,
+ Errno::EHOSTDOWN => e
+ # Do not disable data collector reporter if additional output_locations have been specified
+ disable_data_collector_reporter unless data_collector_output_locations
+ code = if e.respond_to?(:response) && e.response.code
+ e.response.code.to_s
+ else
+ "Exception Code Empty"
+ end
+
+ msg = "Error while reporting run start to Data Collector. " \
+ "URL: #{data_collector_server_url} " \
+ "Exception: #{code} -- #{e.message} "
+
+ if Chef::Config[:data_collector][:raise_on_failure]
+ Chef::Log.error(msg)
+ raise
+ else
+ # Make the message non-scary for folks who don't have automate:
+ msg << " (This is normal if you do not have Chef Automate)"
+ Chef::Log.info(msg)
+ end
+ end
+
+ def send_to_data_collector(message)
+ return unless data_collector_accessible?
+ http.post(nil, message, headers) if data_collector_server_url
+ end
+
+ def send_to_output_locations(message)
+ data_collector_output_locations.each do |type, location_list|
+ location_list.each do |l|
+ handle_output_location(type, l, message)
+ end
+ end
+ end
+
+ def handle_output_location(type, loc, message)
+ type == :urls ? send_to_http_location(loc, message) : send_to_file_location(loc, message)
+ end
+
+ def send_to_file_location(file_name, message)
+ open(file_name, "a") { |f| f.puts message }
+ end
+
+ def send_to_http_location(http_url, message)
+ @http_output_locations[http_url].post(nil, message, headers) if @http_output_locations[http_url]
+ rescue
+ Chef::Log.trace("Data collector failed to send to URL location #{http_url}. Please check your configured data_collector.output_locations")
+ end
+
+ #
+ # Send any messages to the DataCollector endpoint that are necessary to
+ # indicate the run has completed. Currently, two messages are sent:
+ #
+ # - An "action" message with the node object indicating it's been updated
+ # - An "run_converge" (i.e. RunEnd) message with details about the run,
+ # what resources were modified/up-to-date/skipped, etc.
+ #
+ # @param opts [Hash] Additional details about the run, such as its success/failure.
+ #
+ def send_run_completion(opts)
+ # If run_status is nil we probably failed before the client triggered
+ # the run_started callback. In this case we'll skip updating because
+ # we have nothing to report.
+ return unless run_status
+
+ message = Chef::DataCollector::Messages.run_end_message(
+ run_status: run_status,
+ expanded_run_list: expanded_run_list,
+ resources: all_resource_reports,
+ status: opts[:status],
+ error_descriptions: error_descriptions,
+ deprecations: deprecations.to_a
+ )
+ disable_reporter_on_error do
+ send_to_data_collector(message)
+ end
+ send_to_output_locations(message) if data_collector_output_locations
+ end
+
+ def headers
+ headers = { "Content-Type" => "application/json" }
+
+ unless data_collector_token.nil?
+ headers["x-data-collector-token"] = data_collector_token
+ headers["x-data-collector-auth"] = "version=1.0"
+ end
+
+ headers
+ end
+
+ def data_collector_server_url
+ Chef::Config[:data_collector][:server_url]
+ end
+
+ def data_collector_output_locations
+ Chef::Config[:data_collector][:output_locations]
+ end
+
+ def data_collector_token
+ Chef::Config[:data_collector][:token]
+ end
+
+ def add_resource_report(resource_report)
+ @all_resource_reports << OpenStruct.new(
+ resource: resource_report.new_resource,
+ action: resource_report.action,
+ report_data: resource_report.to_hash
+ )
+ end
+
+ def disable_data_collector_reporter
+ @enabled = false
+ end
+
+ def data_collector_accessible?
+ @enabled
+ end
+
+ def update_run_status(run_status)
+ @run_status = run_status
+ end
+
+ def update_error_description(discription_hash)
+ @error_descriptions = discription_hash
+ end
+
+ def add_deprecation(message, url, location)
+ @deprecations << { message: message, url: url, location: location }
+ end
+
+ def initialize_resource_report_if_needed(new_resource, action, current_resource = nil)
+ return unless current_resource_report.nil?
+ @current_resource_report = create_resource_report(new_resource, action, current_resource)
+ end
+
+ def create_resource_report(new_resource, action, current_resource = nil)
+ Chef::DataCollector::ResourceReport.new(
+ new_resource,
+ action,
+ current_resource
+ )
+ end
+
+ def clear_current_resource_report
+ @current_resource_report = nil
+ end
+
+ def detect_unprocessed_resources
+ # create a Hash (for performance reasons, rather than an Array) containing all
+ # resource+action combinations from the Resource Collection
+ #
+ # We use the object ID instead of the resource itself in the Hash key because
+ # we currently allow users to create a property called "hash" which creates
+ # a #hash instance method on the resource. Ruby expects that to be a Fixnum,
+ # so bad things happen when adding an object to an Array or a Hash if it's not.
+ collection_resources = {}
+ run_context.resource_collection.all_resources.each do |resource|
+ Array(resource.action).each do |action|
+ collection_resources[[resource.__id__, action]] = resource
+ end
+ end
+
+ # Delete from the Hash any resource+action combination we have
+ # already processed.
+ all_resource_reports.each do |report|
+ collection_resources.delete([report.resource.__id__, report.action])
+ end
+
+ # The items remaining in the Hash are unprocessed resource+actions,
+ # so we'll create new resource reports for them which default to
+ # a state of "unprocessed".
+ collection_resources.each do |key, resource|
+ # The Hash key is an array of the Resource's object ID and the action.
+ # We need to pluck out the action.
+ add_resource_report(create_resource_report(resource, key[1]))
+ end
+ end
+
+ # If we are getting messages about a resource while we are in the middle of
+ # another resource's update, we assume that the nested resource is just the
+ # implementation of a provider, and we want to hide it from the reporting
+ # output.
+ def nested_resource?(new_resource)
+ @current_resource_report && @current_resource_report.new_resource != new_resource
+ end
+
+ def validate_and_return_uri(uri)
+ URI(uri)
+ rescue URI::InvalidURIError
+ nil
+ end
+
+ def validate_and_create_file(file)
+ send_to_file_location(file, "")
+ true
+ # Rescue exceptions raised by the file path being non-existent or not writeable and re-raise them to the user
+ # with clearer explanatory text.
+ rescue Errno::ENOENT
+ raise Chef::Exceptions::ConfigurationError,
+ "Chef::Config[:data_collector][:output_locations][:files] contains the location #{file}, which is a non existent file path."
+ rescue Errno::EACCES
+ raise Chef::Exceptions::ConfigurationError,
+ "Chef::Config[:data_collector][:output_locations][:files] contains the location #{file}, which cannnot be written to by Chef."
+ end
+
+ def validate_data_collector_server_url!
+ unless !data_collector_server_url && data_collector_output_locations
+ uri = validate_and_return_uri(data_collector_server_url)
+ unless uri
+ raise Chef::Exceptions::ConfigurationError, "Chef::Config[:data_collector][:server_url] (#{data_collector_server_url}) is not a valid URI."
+ end
+
+ if uri.host.nil?
+ raise Chef::Exceptions::ConfigurationError,
+ "Chef::Config[:data_collector][:server_url] (#{data_collector_server_url}) is a URI with no host. Please supply a valid URL."
+ end
+ end
+ end
+
+ def handle_type(type, loc)
+ type == :urls ? validate_and_return_uri(loc) : validate_and_create_file(loc)
+ end
+
+ def validate_data_collector_output_locations!
+ if data_collector_output_locations.empty?
+ raise Chef::Exceptions::ConfigurationError,
+ "Chef::Config[:data_collector][:output_locations] is empty. Please supply an hash of valid URLs and / or local file paths."
+ end
+
+ data_collector_output_locations.each do |type, locations|
+ locations.each do |l|
+ unless handle_type(type, l)
+ raise Chef::Exceptions::ConfigurationError,
+ "Chef::Config[:data_collector][:output_locations] contains the location #{l} which is not valid."
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/data_collector/messages.rb b/lib/chef/data_collector/messages.rb
new file mode 100644
index 0000000000..b4e30e8b5a
--- /dev/null
+++ b/lib/chef/data_collector/messages.rb
@@ -0,0 +1,100 @@
+#
+# Author:: Adam Leff (<adamleff@chef.io)
+# Author:: Ryan Cragun (<ryan@chef.io>)
+#
+# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "securerandom"
+require_relative "messages/helpers"
+
+class Chef
+ class DataCollector
+ module Messages
+ extend Helpers
+
+ #
+ # Message payload that is sent to the DataCollector server at the
+ # start of a Chef run.
+ #
+ # @param run_status [Chef::RunStatus] The RunStatus instance for this node/run.
+ #
+ # @return [Hash] A hash containing the run start message data.
+ #
+ def self.run_start_message(run_status)
+ {
+ "chef_server_fqdn" => chef_server_fqdn,
+ "entity_uuid" => node_uuid,
+ "id" => run_status.run_id,
+ "message_version" => "1.0.0",
+ "message_type" => "run_start",
+ "node_name" => run_status.node.name,
+ "organization_name" => organization,
+ "run_id" => run_status.run_id,
+ "source" => collector_source,
+ "start_time" => run_status.start_time.utc.iso8601,
+ }
+ end
+
+ #
+ # Message payload that is sent to the DataCollector server at the
+ # end of a Chef run.
+ #
+ # @param reporter_data [Hash] Data supplied by the Reporter, such as run_status, resource counts, etc.
+ #
+ # @return [Hash] A hash containing the run end message data.
+ #
+ def self.run_end_message(reporter_data)
+ run_status = reporter_data[:run_status]
+
+ message = {
+ "chef_server_fqdn" => chef_server_fqdn,
+ "entity_uuid" => node_uuid,
+ "expanded_run_list" => reporter_data[:expanded_run_list],
+ "id" => run_status.run_id,
+ "message_version" => "1.1.0",
+ "message_type" => "run_converge",
+ "node" => run_status.node,
+ "node_name" => run_status.node.name,
+ "organization_name" => organization,
+ "resources" => reporter_data[:resources].map(&:report_data),
+ "run_id" => run_status.run_id,
+ "run_list" => run_status.node.run_list.for_json,
+ "policy_name" => run_status.node.policy_name,
+ "policy_group" => run_status.node.policy_group,
+ "start_time" => run_status.start_time.utc.iso8601,
+ "end_time" => run_status.end_time.utc.iso8601,
+ "source" => collector_source,
+ "status" => reporter_data[:status],
+ "total_resource_count" => reporter_data[:resources].count,
+ "updated_resource_count" => reporter_data[:resources].select { |r| r.report_data["status"] == "updated" }.count,
+ "deprecations" => reporter_data[:deprecations],
+ }
+
+ if run_status.exception
+ message["error"] = {
+ "class" => run_status.exception.class,
+ "message" => run_status.exception.message,
+ "backtrace" => run_status.exception.backtrace,
+ "description" => reporter_data[:error_descriptions],
+ }
+ end
+
+ message
+ end
+ end
+ end
+end
diff --git a/lib/chef/data_collector/messages/helpers.rb b/lib/chef/data_collector/messages/helpers.rb
new file mode 100644
index 0000000000..e4eda5ebb2
--- /dev/null
+++ b/lib/chef/data_collector/messages/helpers.rb
@@ -0,0 +1,159 @@
+#
+# Author:: Adam Leff (<adamleff@chef.io)
+# Author:: Ryan Cragun (<ryan@chef.io>)
+#
+# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ class DataCollector
+ module Messages
+ module Helpers
+ #
+ # Fully-qualified domain name of the Chef Server configured in Chef::Config
+ # If the chef_server_url cannot be parsed as a URI, the node["fqdn"] attribute
+ # will be returned, or "localhost" if the run_status is unavailable to us.
+ #
+ # @return [String] FQDN of the configured Chef Server, or node/localhost if not found.
+ #
+ def chef_server_fqdn
+ if !Chef::Config[:chef_server_url].nil?
+ URI(Chef::Config[:chef_server_url]).host
+ elsif !Chef::Config[:node_name].nil?
+ Chef::Config[:node_name]
+ else
+ "localhost"
+ end
+ end
+
+ #
+ # The organization name the node is associated with. For Chef Solo runs, a
+ # user-configured organization string is returned, or the string "chef_solo"
+ # if such a string is not configured.
+ #
+ # @return [String] Organization to which the node is associated
+ #
+ def organization
+ solo_run? ? data_collector_organization : chef_server_organization
+ end
+
+ #
+ # Returns the user-configured organization, or "chef_solo" if none is configured.
+ #
+ # This is only used when Chef is run in Solo mode.
+ #
+ # @return [String] Data-collector-specific organization used when running in Chef Solo
+ #
+ def data_collector_organization
+ Chef::Config[:data_collector][:organization] || "chef_solo"
+ end
+
+ #
+ # Return the organization assumed by the configured chef_server_url.
+ #
+ # We must parse this from the Chef::Config[:chef_server_url] because a node
+ # has no knowledge of an organization or to which organization is belongs.
+ #
+ # If we cannot determine the organization, we return "unknown_organization"
+ #
+ # @return [String] shortname of the Chef Server organization
+ #
+ def chef_server_organization
+ return "unknown_organization" unless Chef::Config[:chef_server_url]
+
+ Chef::Config[:chef_server_url].match(%r{/+organizations/+([a-z0-9][a-z0-9_-]{0,254})}).nil? ? "unknown_organization" : $1
+ end
+
+ #
+ # The source of the data collecting during this run, used by the
+ # DataCollector endpoint to determine if Chef was in Solo mode or not.
+ #
+ # @return [String] "chef_solo" if in Solo mode, "chef_client" if in Client mode
+ #
+ def collector_source
+ solo_run? ? "chef_solo" : "chef_client"
+ end
+
+ #
+ # If we're running in Solo (legacy) mode, or in Solo (formerly
+ # "Chef Client Local Mode"), we're considered to be in a "solo run".
+ #
+ # @return [Boolean] Whether we're in a solo run or not
+ #
+ def solo_run?
+ Chef::Config[:solo] || Chef::Config[:local_mode]
+ end
+
+ #
+ # Returns a UUID that uniquely identifies this node for reporting reasons.
+ #
+ # The node is read in from disk if it exists, or it's generated if it does
+ # does not exist.
+ #
+ # @return [String] UUID for the node
+ #
+ def node_uuid
+ Chef::Config[:chef_guid] || read_node_uuid || generate_node_uuid
+ end
+
+ #
+ # Generates a UUID for the node via SecureRandom.uuid and writes out
+ # metadata file so the UUID persists between runs.
+ #
+ # @return [String] UUID for the node
+ #
+ def generate_node_uuid
+ uuid = SecureRandom.uuid
+ update_metadata("node_uuid", uuid)
+
+ uuid
+ end
+
+ #
+ # Reads in the node UUID from the node metadata file
+ #
+ # @return [String] UUID for the node
+ #
+ def read_node_uuid
+ metadata["node_uuid"]
+ end
+
+ #
+ # Returns the DataCollector metadata for this node
+ #
+ # If the metadata file does not exist in the file cache path,
+ # an empty hash will be returned.
+ #
+ # @return [Hash] DataCollector metadata for this node
+ #
+ def metadata
+ Chef::JSONCompat.parse(Chef::FileCache.load(metadata_filename))
+ rescue Chef::Exceptions::FileNotFound
+ {}
+ end
+
+ def update_metadata(key, value)
+ updated_metadata = metadata.tap { |x| x[key] = value }
+ Chef::FileCache.store(metadata_filename, Chef::JSONCompat.to_json(updated_metadata), 0644)
+ end
+
+ def metadata_filename
+ "data_collector_metadata.json"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/data_collector/resource_report.rb b/lib/chef/data_collector/resource_report.rb
new file mode 100644
index 0000000000..01651d5460
--- /dev/null
+++ b/lib/chef/data_collector/resource_report.rb
@@ -0,0 +1,123 @@
+#
+# Author:: Adam Leff (<adamleff@chef.io>)
+# Author:: Ryan Cragun (<ryan@chef.io>)
+#
+# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/exceptions"
+
+class Chef
+ class DataCollector
+ class ResourceReport
+
+ attr_reader :action, :elapsed_time, :new_resource, :status
+ attr_accessor :conditional, :current_resource, :exception
+
+ def initialize(new_resource, action, current_resource = nil)
+ @new_resource = new_resource
+ @action = action
+ @current_resource = current_resource
+ @status = "unprocessed"
+ end
+
+ def skipped(conditional)
+ @status = "skipped"
+ @conditional = conditional
+ end
+
+ def updated
+ @status = "updated"
+ end
+
+ def failed(exception)
+ @current_resource = nil
+ @status = "failed"
+ @exception = exception
+ end
+
+ def up_to_date
+ @status = "up-to-date"
+ end
+
+ def finish
+ @elapsed_time = new_resource.elapsed_time
+ end
+
+ def elapsed_time_in_milliseconds
+ elapsed_time.nil? ? nil : (elapsed_time * 1000).to_i
+ end
+
+ def potentially_changed?
+ %w{updated failed}.include?(status)
+ end
+
+ def to_hash
+ hash = {
+ "type" => new_resource.resource_name.to_sym,
+ "name" => new_resource.name.to_s,
+ "id" => resource_identity,
+ "after" => new_resource_state_reporter,
+ "before" => current_resource_state_reporter,
+ "duration" => elapsed_time_in_milliseconds.to_s,
+ "delta" => new_resource.respond_to?(:diff) && potentially_changed? ? new_resource.diff : "",
+ "ignore_failure" => new_resource.ignore_failure,
+ "result" => action.to_s,
+ "status" => status,
+ }
+
+ if new_resource.cookbook_name
+ hash["cookbook_name"] = new_resource.cookbook_name
+ hash["cookbook_version"] = new_resource.cookbook_version.version
+ hash["recipe_name"] = new_resource.recipe_name
+ end
+
+ hash["conditional"] = conditional.to_text if status == "skipped"
+ hash["error_message"] = exception.message unless exception.nil?
+
+ hash
+ end
+ alias :to_h :to_hash
+ alias :for_json :to_hash
+
+ # We should be able to call the identity of a resource safely, but there
+ # is an edge case where resources that have a lazy property that is both
+ # the name_property and the identity property, it will thow a validation
+ # exception causing the chef-client run to fail. We are not fixing this
+ # case since Chef is actually doing the right thing but we are making the
+ # ResourceReporter smarter so that it detects the failure and sends a
+ # message to the data collector containing a static resource identity
+ # since we were unable to generate a proper one.
+ def resource_identity
+ new_resource.identity.to_s
+ rescue => e
+ "unknown identity (due to #{e.class})"
+ end
+
+ def new_resource_state_reporter
+ new_resource.state_for_resource_reporter
+ rescue
+ {}
+ end
+
+ def current_resource_state_reporter
+ current_resource ? current_resource.state_for_resource_reporter : {}
+ rescue
+ {}
+ end
+ end
+ end
+end
diff --git a/lib/chef/decorator.rb b/lib/chef/decorator.rb
new file mode 100644
index 0000000000..ac0f8fdfbb
--- /dev/null
+++ b/lib/chef/decorator.rb
@@ -0,0 +1,81 @@
+#--
+# Copyright:: Copyright 2016 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "delegate"
+
+class Chef
+ class Decorator < SimpleDelegator
+ NULL = ::Object.new
+
+ def initialize(obj = NULL)
+ @__defined_methods__ = []
+ super unless obj.equal?(NULL)
+ end
+
+ # if we wrap a nil then decorator.nil? should be true
+ def nil?
+ __getobj__.nil?
+ end
+
+ # if we wrap a Hash then decorator.is_a?(Hash) should be true
+ def is_a?(klass)
+ __getobj__.is_a?(klass) || super
+ end
+
+ # if we wrap a Hash then decorator.kind_of?(Hash) should be true
+ def kind_of?(klass)
+ __getobj__.kind_of?(klass) || super
+ end
+
+ # reset our methods on the instance if the object changes under us (this also
+ # clears out the closure over the target we create in method_missing below)
+ def __setobj__(obj)
+ __reset_methods__
+ super
+ end
+
+ # this is the ruby 2.2/2.3 implementation of Delegator#method_missing() with
+ # adding the define_singleton_method call and @__defined_methods__ tracking
+ def method_missing(m, *args, &block)
+ r = true
+ target = __getobj__ { r = false }
+
+ if r && target.respond_to?(m)
+ # these next 4 lines are the patched code
+ define_singleton_method(m) do |*args, &block|
+ target.__send__(m, *args, &block)
+ end
+ @__defined_methods__.push(m)
+ target.__send__(m, *args, &block)
+ elsif ::Kernel.respond_to?(m, true)
+ ::Kernel.instance_method(m).bind(self).call(*args, &block)
+ else
+ super(m, *args, &block)
+ end
+ end
+
+ private
+
+ # used by __setobj__ to clear the methods we've built on the instance.
+ def __reset_methods__
+ @__defined_methods__.each do |m|
+ singleton_class.send(:undef_method, m)
+ end
+ @__defined_methods__ = []
+ end
+ end
+end
diff --git a/lib/chef/decorator/lazy.rb b/lib/chef/decorator/lazy.rb
new file mode 100644
index 0000000000..71a2151d65
--- /dev/null
+++ b/lib/chef/decorator/lazy.rb
@@ -0,0 +1,60 @@
+#--
+# Copyright:: Copyright 2016 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/decorator"
+
+class Chef
+ class Decorator
+ # Lazy wrapper to delay construction of an object until a method is
+ # called against the object.
+ #
+ # @example
+ #
+ # def foo
+ # puts "allocated"
+ # "value"
+ # end
+ #
+ # a = Chef::Decorator::Lazy.new { foo }
+ #
+ # puts "started"
+ # a
+ # puts "still lazy"
+ # puts a
+ #
+ # outputs:
+ #
+ # started
+ # still lazy
+ # allocated
+ # value
+ #
+ # @since 12.10.x
+ class Lazy < Decorator
+ def initialize(&block)
+ super
+ @block = block
+ end
+
+ def __getobj__
+ __setobj__(@block.call) unless defined?(@delegate_sd_obj)
+ super
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/decorator/lazy_array.rb b/lib/chef/decorator/lazy_array.rb
new file mode 100644
index 0000000000..dc8ea832ee
--- /dev/null
+++ b/lib/chef/decorator/lazy_array.rb
@@ -0,0 +1,59 @@
+#--
+# Copyright:: Copyright 2016 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/decorator/lazy"
+
+class Chef
+ class Decorator
+ # Lazy Array around Lazy Objects
+ #
+ # This only lazys access through `#[]`. In order to implement #each we need to
+ # know how many items we have and what their indexes are, so we'd have to evalute
+ # the proc which makes that impossible. You can call methods like #each and the
+ # decorator will forward the method, but item access will not be lazy.
+ #
+ # #at() and #fetch() are not implemented but technically could be.
+ #
+ # @example
+ # def foo
+ # puts "allocated"
+ # "value"
+ # end
+ #
+ # a = Chef::Decorator::LazyArray.new { [ foo ] }
+ #
+ # puts "started"
+ # a[0]
+ # puts "still lazy"
+ # puts a[0]
+ #
+ # outputs:
+ #
+ # started
+ # still lazy
+ # allocated
+ # value
+ #
+ # @since 12.10.x
+ class LazyArray < Lazy
+ def [](idx)
+ block = @block
+ Lazy.new { block.call[idx] }
+ end
+ end
+ end
+end
diff --git a/lib/chef/decorator/unchain.rb b/lib/chef/decorator/unchain.rb
new file mode 100644
index 0000000000..30179d4e63
--- /dev/null
+++ b/lib/chef/decorator/unchain.rb
@@ -0,0 +1,43 @@
+class Chef
+ class Decorator < SimpleDelegator
+ #
+ # This decorator unchains method call chains and turns them into method calls
+ # with variable args. So this:
+ #
+ # node.set_unless["foo"]["bar"] = "baz"
+ #
+ # Can become:
+ #
+ # node.set_unless("foo", "bar", "baz")
+ #
+ # While this is a decorator it is not a Decorator and does not inherit because
+ # it deliberately does not need or want the method_missing magic. It is not legal
+ # to call anything on the intermediate values and only supports method chaining with
+ # #[] until the chain comes to an end with #[]=, so does not behave like a hash or
+ # array... e.g.
+ #
+ # node.default['foo'].keys is legal
+ # node.set_unless['foo'].keys is not legal now or ever
+ #
+ class Unchain
+ attr_accessor :__path__
+ attr_accessor :__method__
+
+ def initialize(obj, method)
+ @__path__ = []
+ @__method__ = method
+ @delegate_sd_obj = obj
+ end
+
+ def [](key)
+ __path__.push(key)
+ self
+ end
+
+ def []=(key, value)
+ __path__.push(key)
+ @delegate_sd_obj.public_send(__method__, *__path__, value)
+ end
+ end
+ end
+end
diff --git a/lib/chef/deprecated.rb b/lib/chef/deprecated.rb
new file mode 100644
index 0000000000..792df69f71
--- /dev/null
+++ b/lib/chef/deprecated.rb
@@ -0,0 +1,301 @@
+#--
+# Copyright:: Copyright 2016-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/mixin/convert_to_class_name"
+
+# Structured deprecations have a unique URL associated with them, which must exist before the deprecation is merged.
+class Chef
+ class Deprecated
+
+ class << self
+ include Chef::Mixin::ConvertToClassName
+
+ def create(type, message = nil, location = nil)
+ Chef::Deprecated.const_get(convert_to_class_name(type.to_s)).send(:new, message, location)
+ end
+ end
+
+ class Base
+ BASE_URL = "https://docs.chef.io/deprecations_"
+
+ attr_accessor :message, :location
+
+ def initialize(msg = nil, location = nil)
+ @message = msg if msg
+ @location = location if location
+ end
+
+ def link
+ "Please see #{url} for further details and information on how to correct this problem."
+ end
+
+ def url
+ "#{BASE_URL}#{target}"
+ end
+
+ # We know that the only time this gets called is by Chef::Log.deprecation,
+ # so special case
+ def <<(location)
+ @location = location
+ end
+
+ def inspect
+ "#{message} (CHEF-#{id})#{location}.\n#{link}"
+ end
+
+ def id
+ raise NotImplementedError, "subclasses of Chef::Deprecated::Base should define #id with a unique number"
+ end
+
+ def target
+ raise NotImplementedError, "subclasses of Chef::Deprecated::Base should define #target"
+ end
+ end
+
+ class InternalApi < Base
+ def id
+ 0
+ end
+
+ def target
+ "internal_api.html"
+ end
+ end
+
+ class JsonAutoInflate < Base
+ def id
+ 1
+ end
+
+ def target
+ "json_auto_inflate.html"
+ end
+ end
+
+ class ExitCode < Base
+ def id
+ 2
+ end
+
+ def target
+ "exit_code.html"
+ end
+ end
+
+ # id 3 has been deleted
+
+ class Attributes < Base
+ def id
+ 4
+ end
+
+ def target
+ "attributes.html"
+ end
+ end
+
+ class CustomResource < Base
+ def id
+ 5
+ end
+
+ def target
+ "custom_resource_cleanups.html"
+ end
+ end
+
+ class EasyInstall < Base
+ def id
+ 6
+ end
+
+ def target
+ "easy_install.html"
+ end
+ end
+
+ class VerifyFile < Base
+ def id
+ 7
+ end
+
+ def target
+ "verify_file.html"
+ end
+ end
+
+ class SupportsProperty < Base
+ def id
+ 8
+ end
+
+ def target
+ "supports_property.html"
+ end
+ end
+
+ class ChefRest < Base
+ def id
+ 9
+ end
+
+ def target
+ "chef_rest.html"
+ end
+ end
+
+ class DnfPackageAllowDowngrade < Base
+ def id
+ 10
+ end
+
+ def target
+ "dnf_package_allow_downgrade.html"
+ end
+ end
+
+ class PropertyNameCollision < Base
+ def id
+ 11
+ end
+
+ def target
+ "property_name_collision.html"
+ end
+ end
+
+ class LaunchdHashProperty < Base
+ def id
+ 12
+ end
+
+ def target
+ "launchd_hash_property.html"
+ end
+ end
+
+ class ChefPlatformMethods < Base
+ def id
+ 13
+ end
+
+ def target
+ "chef_platform_methods.html"
+ end
+ end
+
+ class RunCommand < Base
+ def id
+ 14
+ end
+
+ def target
+ "run_command.html"
+ end
+ end
+
+ class PackageMisc < Base
+ def id
+ 15
+ end
+
+ def target
+ "package_misc.html"
+ end
+ end
+
+ class MultiresourceMatch < Base
+ def id
+ 16
+ end
+
+ def target
+ "multiresource_match.html"
+ end
+ end
+
+ class UseInlineResources < Base
+ def id
+ 17
+ end
+
+ def target
+ "use_inline_resources.html"
+ end
+ end
+
+ class LocalListen < Base
+ def id
+ 18
+ end
+
+ def target
+ "local_listen.html"
+ end
+ end
+
+ class NamespaceCollisions < Base
+ def id
+ 19
+ end
+
+ def target
+ "namespace_collisions.html"
+ end
+ end
+
+ class DeployResource < Base
+ def id
+ 21
+ end
+
+ def target
+ "deploy_resource.html"
+ end
+ end
+
+ class ErlResource < Base
+ def id
+ 22
+ end
+
+ def target
+ "erl_resource.html"
+ end
+ end
+
+ # id 3694 was deleted
+
+ # Returned when using the deprecated option on a property
+ class Property < Base
+ def inspect
+ "#{message}\n#{location}"
+ end
+ end
+
+ class Generic < Base
+ def url
+ "https://docs.chef.io/chef_deprecations_client.html"
+ end
+
+ def inspect
+ "#{message}\nThis is a generic error message and should be updated to have a proper deprecation class. #{location}\nPlease see #{url} for an overview of Chef deprecations."
+ end
+ end
+
+ end
+end
diff --git a/lib/chef/deprecation/mixin/template.rb b/lib/chef/deprecation/mixin/template.rb
deleted file mode 100644
index 0c902123cf..0000000000
--- a/lib/chef/deprecation/mixin/template.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "tempfile"
-require "erubis"
-
-class Chef
- module Deprecation
- module Mixin
- # == Deprecation::Provider::Mixin::Template
- # This module contains the deprecated functions of
- # Chef::Mixin::Template. These functions are refactored to different
- # components. They are frozen and will be removed in Chef 13.
- #
-
- module Template
- def render_template(template, context)
- begin
- eruby = Erubis::Eruby.new(template)
- output = eruby.evaluate(context)
- rescue Object => e
- raise TemplateError.new(e, template, context)
- end
- Tempfile.open("chef-rendered-template") do |tempfile|
- tempfile.print(output)
- tempfile.close
- yield tempfile
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/deprecation/provider/cookbook_file.rb b/lib/chef/deprecation/provider/cookbook_file.rb
deleted file mode 100644
index d6e8a7566e..0000000000
--- a/lib/chef/deprecation/provider/cookbook_file.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-#
-# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-class Chef
- module Deprecation
- module Provider
-
- # == Deprecation::Provider::CookbookFile
- # This module contains the deprecated functions of
- # Chef::Provider::CookbookFile. These functions are refactored to
- # different components. They are frozen and will be removed in Chef 13.
- #
- module CookbookFile
-
- def file_cache_location
- @file_cache_location ||= begin
- cookbook = run_context.cookbook_collection[resource_cookbook]
- cookbook.preferred_filename_on_disk_location(node, :files, @new_resource.source, @new_resource.path)
- end
- end
-
- def resource_cookbook
- @new_resource.cookbook || @new_resource.cookbook_name
- end
-
- def content_stale?
- ( ! ::File.exist?(@new_resource.path)) || ( ! compare_content)
- end
-
- def backup_new_resource
- if ::File.exists?(@new_resource.path)
- backup @new_resource.path
- end
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/deprecation/provider/file.rb b/lib/chef/deprecation/provider/file.rb
deleted file mode 100644
index edb0052fdf..0000000000
--- a/lib/chef/deprecation/provider/file.rb
+++ /dev/null
@@ -1,198 +0,0 @@
-#
-# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/util/path_helper"
-
-class Chef
- module Deprecation
- module Provider
-
- # == Deprecation::Provider::File
- # This module contains the deprecated functions of
- # Chef::Provider::File. These functions are refactored to different
- # components. They are frozen and will be removed in Chef 13.
- #
- module File
-
- def diff_current_from_content(new_content)
- result = nil
- Tempfile.open("chef-diff") do |file|
- file.write new_content
- file.close
- result = diff_current file.path
- end
- result
- end
-
- def is_binary?(path)
- ::File.open(path) do |file|
-
- buff = file.read(Chef::Config[:diff_filesize_threshold])
- buff = "" if buff.nil?
- return buff !~ /^[\r[:print:]]*$/
- end
- end
-
- def diff_current(temp_path)
- suppress_resource_reporting = false
-
- return [ "(diff output suppressed by config)" ] if Chef::Config[:diff_disabled]
- return [ "(no temp file with new content, diff output suppressed)" ] unless ::File.exists?(temp_path) # should never happen?
-
- # solaris does not support diff -N, so create tempfile to diff against if we are creating a new file
- target_path = if ::File.exists?(@current_resource.path)
- @current_resource.path
- else
- suppress_resource_reporting = true # suppress big diffs going to resource reporting service
- tempfile = Tempfile.new("chef-tempfile")
- tempfile.path
- end
-
- diff_filesize_threshold = Chef::Config[:diff_filesize_threshold]
- diff_output_threshold = Chef::Config[:diff_output_threshold]
-
- if ::File.size(target_path) > diff_filesize_threshold || ::File.size(temp_path) > diff_filesize_threshold
- return [ "(file sizes exceed #{diff_filesize_threshold} bytes, diff output suppressed)" ]
- end
-
- # MacOSX(BSD?) diff will *sometimes* happily spit out nasty binary diffs
- return [ "(current file is binary, diff output suppressed)"] if is_binary?(target_path)
- return [ "(new content is binary, diff output suppressed)"] if is_binary?(temp_path)
-
- begin
- # -u: Unified diff format
- result = shell_out("diff -u #{target_path} #{temp_path}" )
- rescue Exception => e
- # Should *not* receive this, but in some circumstances it seems that
- # an exception can be thrown even using shell_out instead of shell_out!
- return [ "Could not determine diff. Error: #{e.message}" ]
- end
-
- # diff will set a non-zero return code even when there's
- # valid stdout results, if it encounters something unexpected
- # So as long as we have output, we'll show it.
- if not result.stdout.empty?
- if result.stdout.length > diff_output_threshold
- [ "(long diff of over #{diff_output_threshold} characters, diff output suppressed)" ]
- else
- val = result.stdout.split("\n")
- val.delete("\\ No newline at end of file")
- @new_resource.diff(val.join("\\n")) unless suppress_resource_reporting
- val
- end
- elsif not result.stderr.empty?
- [ "Could not determine diff. Error: #{result.stderr}" ]
- else
- [ "(no diff)" ]
- end
- end
-
- def setup_acl
- return if Chef::Platform.windows?
- acl_scanner = ScanAccessControl.new(@new_resource, @current_resource)
- acl_scanner.set_all!
- end
-
- def compare_content
- checksum(@current_resource.path) == new_resource_content_checksum
- end
-
- def set_content
- unless compare_content
- description = []
- description << "update content in file #{@new_resource.path} from #{short_cksum(@current_resource.checksum)} to #{short_cksum(new_resource_content_checksum)}"
- description << diff_current_from_content(@new_resource.content)
- converge_by(description) do
- backup @new_resource.path if ::File.exists?(@new_resource.path)
- ::File.open(@new_resource.path, "w") { |f| f.write @new_resource.content }
- Chef::Log.info("#{@new_resource} contents updated")
- end
- end
- end
-
- def update_new_file_state(path = @new_resource.path)
- if !::File.directory?(path)
- @new_resource.checksum(checksum(path))
- end
-
- if Chef::Platform.windows?
- # TODO: To work around CHEF-3554, add support for Windows
- # equivalent, or implicit resource reporting won't work for
- # Windows.
- return
- end
-
- acl_scanner = ScanAccessControl.new(@new_resource, @new_resource)
- acl_scanner.set_all!
- end
-
- def set_all_access_controls
- if access_controls.requires_changes?
- converge_by(access_controls.describe_changes) do
- access_controls.set_all
- #Update file state with new access values
- update_new_file_state
- end
- end
- end
-
- def deploy_tempfile
- Tempfile.open(::File.basename(@new_resource.name)) do |tempfile|
- yield tempfile
-
- temp_res = Chef::Resource::CookbookFile.new(@new_resource.name)
- temp_res.path(tempfile.path)
- ac = Chef::FileAccessControl.new(temp_res, @new_resource, self)
- ac.set_all!
- FileUtils.mv(tempfile.path, @new_resource.path)
- end
- end
-
- def backup(file = nil)
- file ||= @new_resource.path
- if @new_resource.backup != false && @new_resource.backup > 0 && ::File.exist?(file)
- time = Time.now
- savetime = time.strftime("%Y%m%d%H%M%S")
- backup_filename = "#{@new_resource.path}.chef-#{savetime}"
- backup_filename = backup_filename.sub(/^([A-Za-z]:)/, "") #strip drive letter on Windows
- # if :file_backup_path is nil, we fallback to the old behavior of
- # keeping the backup in the same directory. We also need to to_s it
- # so we don't get a type error around implicit to_str conversions.
- prefix = Chef::Config[:file_backup_path].to_s
- backup_path = ::File.join(prefix, backup_filename)
- FileUtils.mkdir_p(::File.dirname(backup_path)) if Chef::Config[:file_backup_path]
- FileUtils.cp(file, backup_path, :preserve => true)
- Chef::Log.info("#{@new_resource} backed up to #{backup_path}")
-
- # Clean up after the number of backups
- slice_number = @new_resource.backup
- backup_files = Dir[Chef::Util::PathHelper.escape_glob_dir(prefix, ".#{@new_resource.path}") + ".chef-*"].sort { |a, b| b <=> a }
- if backup_files.length >= @new_resource.backup
- remainder = backup_files.slice(slice_number..-1)
- remainder.each do |backup_to_delete|
- FileUtils.rm(backup_to_delete)
- Chef::Log.info("#{@new_resource} removed backup at #{backup_to_delete}")
- end
- end
- end
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/deprecation/provider/remote_directory.rb b/lib/chef/deprecation/provider/remote_directory.rb
deleted file mode 100644
index 5e5188f28b..0000000000
--- a/lib/chef/deprecation/provider/remote_directory.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-class Chef
- module Deprecation
- module Provider
- module RemoteDirectory
-
- def directory_root_in_cookbook_cache
- Chef.log_deprecation "the Chef::Provider::RemoteDirectory#directory_root_in_cookbook_cache method is deprecated"
-
- @directory_root_in_cookbook_cache ||=
- begin
- cookbook = run_context.cookbook_collection[resource_cookbook]
- cookbook.preferred_filename_on_disk_location(node, :files, source, path)
- end
- end
-
- # List all excluding . and ..
- def ls(path)
- files = Dir.glob(::File.join(Chef::Util::PathHelper.escape_glob_dir(path), "**", "*"),
- ::File::FNM_DOTMATCH)
-
- # Remove current directory and previous directory
- files = files.reject do |name|
- basename = Pathname.new(name).basename().to_s
- [".", ".."].include?(basename)
- end
-
- # Clean all the paths... this is required because of the join
- files.map { |f| Chef::Util::PathHelper.cleanpath(f) }
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/deprecation/provider/remote_file.rb b/lib/chef/deprecation/provider/remote_file.rb
deleted file mode 100644
index b469609ffe..0000000000
--- a/lib/chef/deprecation/provider/remote_file.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-#
-# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-class Chef
- module Deprecation
- module Provider
-
- # == Deprecation::Provider::RemoteFile
- # This module contains the deprecated functions of
- # Chef::Provider::RemoteFile. These functions are refactored to different
- # components. They are frozen and will be removed in Chef 13.
- #
- module RemoteFile
-
- def current_resource_matches_target_checksum?
- @new_resource.checksum && @current_resource.checksum && @current_resource.checksum =~ /^#{Regexp.escape(@new_resource.checksum)}/
- end
-
- def matches_current_checksum?(candidate_file)
- Chef::Log.debug "#{@new_resource} checking for file existence of #{@new_resource.path}"
- if ::File.exists?(@new_resource.path)
- Chef::Log.debug "#{@new_resource} file exists at #{@new_resource.path}"
- @new_resource.checksum(checksum(candidate_file.path))
- Chef::Log.debug "#{@new_resource} target checksum: #{@current_resource.checksum}"
- Chef::Log.debug "#{@new_resource} source checksum: #{@new_resource.checksum}"
-
- @new_resource.checksum == @current_resource.checksum
- else
- Chef::Log.debug "#{@new_resource} creating #{@new_resource.path}"
- false
- end
- end
-
- def backup_new_resource
- if ::File.exists?(@new_resource.path)
- Chef::Log.debug "#{@new_resource} checksum changed from #{@current_resource.checksum} to #{@new_resource.checksum}"
- backup @new_resource.path
- end
- end
-
- def source_file(source, current_checksum, &block)
- if absolute_uri?(source)
- fetch_from_uri(source, &block)
- elsif !Chef::Config[:solo]
- fetch_from_chef_server(source, current_checksum, &block)
- else
- fetch_from_local_cookbook(source, &block)
- end
- end
-
- def http_client_opts(source)
- opts = {}
- # CHEF-3140
- # 1. If it's already compressed, trying to compress it more will
- # probably be counter-productive.
- # 2. Some servers are misconfigured so that you GET $URL/file.tgz but
- # they respond with content type of tar and content encoding of gzip,
- # which tricks Chef::REST into decompressing the response body. In this
- # case you'd end up with a tar archive (no gzip) named, e.g., foo.tgz,
- # which is not what you wanted.
- if @new_resource.path =~ /gz$/ || source =~ /gz$/
- opts[:disable_gzip] = true
- end
- opts
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/deprecation/provider/template.rb b/lib/chef/deprecation/provider/template.rb
deleted file mode 100644
index ea5a880798..0000000000
--- a/lib/chef/deprecation/provider/template.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-#
-# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/deprecation/mixin/template"
-
-class Chef
- module Deprecation
- module Provider
-
- # == Deprecation::Provider::Template
- # This module contains the deprecated functions of
- # Chef::Provider::Template. These functions are refactored to different
- # components. They are frozen and will be removed in Chef 13.
- #
- module Template
-
- include Chef::Deprecation::Mixin::Template
-
- def template_finder
- @template_finder ||= begin
- Chef::Provider::TemplateFinder.new(run_context, cookbook_name, node)
- end
- end
-
- def template_location
- @template_file_cache_location ||= begin
- template_finder.find(@new_resource.source, :local => @new_resource.local, :cookbook => @new_resource.cookbook)
- end
- end
-
- def resource_cookbook
- @new_resource.cookbook || @new_resource.cookbook_name
- end
-
- def rendered(rendered_template)
- @new_resource.checksum(checksum(rendered_template.path))
- Chef::Log.debug("Current content's checksum: #{@current_resource.checksum}")
- Chef::Log.debug("Rendered content's checksum: #{@new_resource.checksum}")
- end
-
- def content_matches?
- @current_resource.checksum == @new_resource.checksum
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/deprecation/warnings.rb b/lib/chef/deprecation/warnings.rb
index 411e95ea39..c227739a8d 100644
--- a/lib/chef/deprecation/warnings.rb
+++ b/lib/chef/deprecation/warnings.rb
@@ -20,13 +20,14 @@ class Chef
module Deprecation
module Warnings
+ require "chef/version"
+
def add_deprecation_warnings_for(method_names)
method_names.each do |name|
define_method(name) do |*args|
- message = []
- message << "Method '#{name}' of '#{self.class}' is deprecated. It will be removed in Chef 13."
- message << "Please update your cookbooks accordingly."
- Chef.log_deprecation(message)
+ message = "Method '#{name}' of '#{self.class}' is deprecated. It will be removed in Chef #{Chef::VERSION.to_i.next}."
+ message << " Please update your cookbooks accordingly."
+ Chef.deprecated(:internal_api, message)
super(*args)
end
end
diff --git a/lib/chef/dsl/audit.rb b/lib/chef/dsl/audit.rb
index 98271dc5cb..9ef798df91 100644
--- a/lib/chef/dsl/audit.rb
+++ b/lib/chef/dsl/audit.rb
@@ -38,8 +38,8 @@ class Chef
cookbook_name = self.cookbook_name
metadata = {
cookbook_name: cookbook_name,
- cookbook_version: self.run_context.cookbook_collection[cookbook_name].version,
- recipe_name: self.recipe_name,
+ cookbook_version: run_context.cookbook_collection[cookbook_name].version,
+ recipe_name: recipe_name,
line_number: block.source_location[1],
}
diff --git a/lib/chef/dsl/cheffish.rb b/lib/chef/dsl/cheffish.rb
index de052bbe5c..03290b3674 100644
--- a/lib/chef/dsl/cheffish.rb
+++ b/lib/chef/dsl/cheffish.rb
@@ -27,6 +27,7 @@ class Chef
chef_acl
chef_client
chef_container
+ chef_data_bag_item
chef_data_bag
chef_environment
chef_group
diff --git a/lib/chef/dsl/core.rb b/lib/chef/dsl/core.rb
index c2dbea7628..d7c5b6a006 100644
--- a/lib/chef/dsl/core.rb
+++ b/lib/chef/dsl/core.rb
@@ -1,7 +1,7 @@
#--
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2008-2016, 2009-2015 Chef Software, Inc.
+# Copyright:: Copyright 2008-2016 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,27 +18,35 @@
#
require "chef/dsl/declare_resource"
+require "chef/dsl/universal"
require "chef/mixin/notifying_block"
-require "chef/mixin/powershell_out"
-require "chef/mixin/shell_out"
+require "chef/mixin/lazy_module_include"
class Chef
module DSL
- # This is the "Core DSL" with various bits of Sugar that are mixed into core providers as well
- # as user LWRPs. This module deliberately does not mixin the Resources or Defintions DSL bits
- # so that cookbooks are not injeting random things into the namespace of core providers.
+ # Part of a family of DSL mixins.
#
- # - If you are writing cookbooks: you have come to the wrong place, please inject things into
- # Chef::DSL::Recipe instead.
+ # Chef::DSL::Recipe mixes into Recipes and LWRP Providers.
+ # - this does not target core chef resources and providers.
+ # - this is restricted to recipe/resource/provider context where a resource collection exists.
+ # - cookbook authors should typically include modules into here.
#
- # - If you are writing core chef: you have come to the right place, please drop your DSL modules
- # into here.
+ # Chef::DSL::Core mixes into Recipes, LWRP Providers and Core Providers
+ # - this adds cores providers on top of the Recipe DSL.
+ # - this is restricted to recipe/resource/provider context where a resource collection exists.
+ # - core chef authors should typically include modules into here.
+ #
+ # Chef::DSL::Universal mixes into Recipes, LWRP Resources+Providers, Core Resources+Providers, and Attributes files.
+ # - this adds resources and attributes files.
+ # - do not add helpers which manipulate the resource collection.
+ # - this is for general-purpose stuff that is useful nearly everywhere.
+ # - it also pollutes the namespace of nearly every context, watch out.
#
module Core
+ include Chef::DSL::Universal
include Chef::DSL::DeclareResource
include Chef::Mixin::NotifyingBlock
- include Chef::Mixin::PowershellOut
- include Chef::Mixin::ShellOut
+ extend Chef::Mixin::LazyModuleInclude
end
end
end
diff --git a/lib/chef/dsl/data_query.rb b/lib/chef/dsl/data_query.rb
index b966885724..8ec466ea35 100644
--- a/lib/chef/dsl/data_query.rb
+++ b/lib/chef/dsl/data_query.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -83,7 +83,3 @@ class Chef
end
end
end
-
-# **DEPRECATED**
-# This used to be part of chef/mixin/language. Load the file to activate the deprecation code.
-require "chef/mixin/language"
diff --git a/lib/chef/dsl/declare_resource.rb b/lib/chef/dsl/declare_resource.rb
index 8d76ddfb31..dc95489d52 100644
--- a/lib/chef/dsl/declare_resource.rb
+++ b/lib/chef/dsl/declare_resource.rb
@@ -1,7 +1,7 @@
#--
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Christopher Walters
-# Copyright:: Copyright 2008-2016, 2009-2015 Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -47,7 +47,7 @@ class Chef
when Chef::RunContext
rc
when :root
- Chef.run_context
+ run_context.root_run_context
when :parent
run_context.parent_run_context
else
@@ -71,7 +71,15 @@ class Chef
# delete_resource!(:template, '/x/y.txy')
#
def delete_resource!(type, name, run_context: self.run_context)
- run_context.resource_collection.delete("#{type}[#{name}]")
+ run_context.resource_collection.delete("#{type}[#{name}]").tap do |resource|
+ # Purge any pending notifications too. This will not raise an exception
+ # if there are no notifications.
+ if resource
+ run_context.before_notification_collection.delete(resource.declared_key)
+ run_context.immediate_notification_collection.delete(resource.declared_key)
+ run_context.delayed_notification_collection.delete(resource.declared_key)
+ end
+ end
end
# Lookup a resource in the resource collection by name and delete it. Returns
@@ -109,9 +117,15 @@ class Chef
# cookbook_name: cookbook_name
# end
#
- def edit_resource!(type, name, created_at = nil, run_context: self.run_context, &resource_attrs_block)
+ def edit_resource!(type, name, created_at: nil, run_context: self.run_context, &resource_attrs_block)
resource = find_resource!(type, name, run_context: run_context)
- resource.instance_eval(&resource_attrs_block) if block_given?
+ if resource_attrs_block
+ if defined?(new_resource)
+ resource.instance_exec(new_resource, &resource_attrs_block)
+ else
+ resource.instance_exec(&resource_attrs_block)
+ end
+ end
resource
end
@@ -138,10 +152,39 @@ class Chef
# end
# resource.variables.merge!({ home: "/home/klowns" })
#
- def edit_resource(type, name, created_at = nil, run_context: self.run_context, &resource_attrs_block)
- edit_resource!(type, name, created_at, run_context: run_context, &resource_attrs_block)
+ def edit_resource(type, name, created_at: nil, run_context: self.run_context, &resource_attrs_block)
+ edit_resource!(type, name, created_at: created_at, run_context: run_context, &resource_attrs_block)
rescue Chef::Exceptions::ResourceNotFound
- declare_resource(type, name, created_at, run_context: run_context, &resource_attrs_block)
+ resource = declare_resource(type, name, created_at: created_at, run_context: run_context)
+ if resource_attrs_block
+ if defined?(new_resource)
+ resource.instance_exec(new_resource, &resource_attrs_block)
+ else
+ resource.instance_exec(&resource_attrs_block)
+ end
+ end
+ resource
+ end
+
+ # Find existing resources by searching the list of existing resources. Possible
+ # forms are:
+ #
+ # find(:file => "foobar")
+ # find(:file => [ "foobar", "baz" ])
+ # find("file[foobar]", "file[baz]")
+ # find("file[foobar,baz]")
+ #
+ # Calls `run_context.resource_collection.find(*args)`
+ #
+ # The is backcompat API, the use of find_resource, below, is encouraged.
+ #
+ # @return the matching resource, or an Array of matching resources.
+ #
+ # @raise ArgumentError if you feed it bad lookup information
+ # @raise RuntimeError if it can't find the resources you are looking for.
+ #
+ def resources(*args)
+ run_context.resource_collection.find(*args)
end
# Lookup a resource in the resource collection by name. If the resource is not
@@ -192,8 +235,8 @@ class Chef
def find_resource(type, name, created_at: nil, run_context: self.run_context, &resource_attrs_block)
find_resource!(type, name, run_context: run_context)
rescue Chef::Exceptions::ResourceNotFound
- if block_given?
- declare_resource(type, name, created_at, run_context: run_context, &resource_attrs_block)
+ if resource_attrs_block
+ declare_resource(type, name, created_at: created_at, run_context: run_context, &resource_attrs_block)
end # returns nil otherwise
end
@@ -222,18 +265,12 @@ class Chef
# action :delete
# end
#
- def declare_resource(type, name, created_at = nil, run_context: self.run_context, create_if_missing: false, &resource_attrs_block)
+ def declare_resource(type, name, created_at: nil, run_context: self.run_context, &resource_attrs_block)
created_at ||= caller[0]
- if create_if_missing
- Chef::Log.deprecation "build_resource with a create_if_missing flag is deprecated, use edit_resource instead"
- # midly goofy since we call edit_resource only to re-call ourselves, but that's why its deprecated...
- return edit_resource(type, name, created_at, run_context: run_context, &resource_attrs_block)
- end
+ resource = build_resource(type, name, created_at: created_at, &resource_attrs_block)
- resource = build_resource(type, name, created_at, &resource_attrs_block)
-
- run_context.resource_collection.insert(resource, resource_type: type, instance_name: name)
+ run_context.resource_collection.insert(resource, resource_type: resource.declared_type, instance_name: resource.name)
resource
end
@@ -258,7 +295,7 @@ class Chef
# action :delete
# end
#
- def build_resource(type, name, created_at = nil, run_context: self.run_context, &resource_attrs_block)
+ def build_resource(type, name, created_at: nil, run_context: self.run_context, &resource_attrs_block)
created_at ||= caller[0]
# this needs to be lazy in order to avoid circular dependencies since ResourceBuilder
@@ -273,9 +310,10 @@ class Chef
run_context: run_context,
cookbook_name: cookbook_name,
recipe_name: recipe_name,
- enclosing_provider: self.is_a?(Chef::Provider) ? self : nil
+ enclosing_provider: is_a?(Chef::Provider) ? self : nil
).build(&resource_attrs_block)
end
+
end
end
end
diff --git a/lib/chef/dsl/include_attribute.rb b/lib/chef/dsl/include_attribute.rb
index 6d27fefc25..c81b37a963 100644
--- a/lib/chef/dsl/include_attribute.rb
+++ b/lib/chef/dsl/include_attribute.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,9 +31,9 @@ class Chef
attr_file_specs.flatten.each do |attr_file_spec|
cookbook_name, attr_file = parse_attribute_file_spec(attr_file_spec)
if run_context.loaded_fully_qualified_attribute?(cookbook_name, attr_file)
- Chef::Log.debug("I am not loading attribute file #{cookbook_name}::#{attr_file}, because I have already seen it.")
+ Chef::Log.trace("I am not loading attribute file #{cookbook_name}::#{attr_file}, because I have already seen it.")
else
- Chef::Log.debug("Loading Attribute #{cookbook_name}::#{attr_file}")
+ Chef::Log.trace("Loading Attribute #{cookbook_name}::#{attr_file}")
run_context.loaded_attribute(cookbook_name, attr_file)
attr_file_path = run_context.resolve_attribute(cookbook_name, attr_file)
node.from_file(attr_file_path)
@@ -55,7 +55,3 @@ class Chef
end
end
end
-
-# **DEPRECATED**
-# This used to be part of chef/mixin/language_include_attribute. Load the file to activate the deprecation code.
-require "chef/mixin/language_include_attribute"
diff --git a/lib/chef/dsl/include_recipe.rb b/lib/chef/dsl/include_recipe.rb
index 9abd7d135b..d217c91d4b 100644
--- a/lib/chef/dsl/include_recipe.rb
+++ b/lib/chef/dsl/include_recipe.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -38,7 +38,3 @@ class Chef
end
end
end
-
-# **DEPRECATED**
-# This used to be part of chef/mixin/language_include_recipe. Load the file to activate the deprecation code.
-require "chef/mixin/language_include_recipe"
diff --git a/lib/chef/dsl/method_missing.rb b/lib/chef/dsl/method_missing.rb
deleted file mode 100644
index 0d7ded30f3..0000000000
--- a/lib/chef/dsl/method_missing.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-#--
-# Copyright:: Copyright 2008-2016, 2009-2015 Chef Software, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-class Chef
- module DSL
- # @deprecated scheduled to die in a Chef 13 fire
- module MethodMissing
- def describe_self_for_error
- if respond_to?(:name)
- %Q{`#{self.class} "#{name}"'}
- elsif respond_to?(:recipe_name)
- %Q{`#{self.class} "#{recipe_name}"'}
- else
- to_s
- end
- end
-
- # DEPRECATED:
- # method_missing must live for backcompat purposes until Chef 13.
- def method_missing(method_symbol, *args, &block)
- #
- # If there is already DSL for this, someone must have called
- # method_missing manually. Not a fan. Not. A. Fan.
- #
- if respond_to?(method_symbol)
- Chef.log_deprecation("Calling method_missing(#{method_symbol.inspect}) directly is deprecated in Chef 12 and will be removed in Chef 13. Use public_send() or send() instead.")
- return send(method_symbol, *args, &block)
- end
-
- #
- # If a definition exists, then Chef::DSL::Definitions.add_definition was
- # never called. DEPRECATED.
- #
- if run_context.definitions.has_key?(method_symbol.to_sym)
- Chef.log_deprecation("Definition #{method_symbol} (#{run_context.definitions[method_symbol.to_sym]}) was added to the run_context without calling Chef::DSL::Definitions.add_definition(#{method_symbol.to_sym.inspect}). This will become required in Chef 13.")
- Chef::DSL::Definitions.add_definition(method_symbol)
- return send(method_symbol, *args, &block)
- end
-
- #
- # See if the resource exists anyway. If the user had set
- # Chef::Resource::Blah = <resource>, a deprecation warning will be
- # emitted and the DSL method 'blah' will be added to the DSL.
- #
- resource_class = Chef::ResourceResolver.resolve(method_symbol, node: run_context ? run_context.node : nil)
- if resource_class
- Chef::DSL::Resources.add_resource_dsl(method_symbol)
- return send(method_symbol, *args, &block)
- end
-
- begin
- super
- rescue NoMethodError
- raise NoMethodError, "No resource or method named `#{method_symbol}' for #{describe_self_for_error}"
- rescue NameError
- raise NameError, "No resource, method, or local variable named `#{method_symbol}' for #{describe_self_for_error}"
- end
- end
- end
- end
-end
diff --git a/lib/chef/dsl/platform_introspection.rb b/lib/chef/dsl/platform_introspection.rb
index 276a03af63..ac36dbd1dd 100644
--- a/lib/chef/dsl/platform_introspection.rb
+++ b/lib/chef/dsl/platform_introspection.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -68,41 +68,39 @@ class Chef
private
def match_versions(node)
- begin
- platform, version = node[:platform].to_s, node[:platform_version].to_s
- return nil unless @values.key?(platform)
- node_version = Chef::Version::Platform.new(version)
- key_matches = []
- keys = @values[platform].keys
- keys.each do |k|
- begin
- if Chef::VersionConstraint::Platform.new(k).include?(node_version)
- key_matches << k
- end
- rescue Chef::Exceptions::InvalidVersionConstraint => e
- Chef::Log.debug "Caught InvalidVersionConstraint. This means that a key in value_for_platform cannot be interpreted as a Chef::VersionConstraint::Platform."
- Chef::Log.debug(e)
+ platform, version = node[:platform].to_s, node[:platform_version].to_s
+ return nil unless @values.key?(platform)
+ node_version = Chef::Version::Platform.new(version)
+ key_matches = []
+ keys = @values[platform].keys
+ keys.each do |k|
+ begin
+ if Chef::VersionConstraint::Platform.new(k).include?(node_version)
+ key_matches << k
end
+ rescue Chef::Exceptions::InvalidVersionConstraint => e
+ Chef::Log.trace "Caught InvalidVersionConstraint. This means that a key in value_for_platform cannot be interpreted as a Chef::VersionConstraint::Platform."
+ Chef::Log.trace(e)
end
- return @values[platform][version] if key_matches.include?(version)
- case key_matches.length
- when 0
- return nil
- when 1
- return @values[platform][key_matches.first]
- else
- raise "Multiple matches detected for #{platform} with values #{@values}. The matches are: #{key_matches}"
- end
- rescue Chef::Exceptions::InvalidCookbookVersion => e
- # Lets not break because someone passes a weird string like 'default' :)
- Chef::Log.debug(e)
- Chef::Log.debug "InvalidCookbookVersion exceptions are common and expected here: the generic constraint matcher attempted to match something which is not a constraint. Moving on to next version or constraint"
- return nil
- rescue Chef::Exceptions::InvalidPlatformVersion => e
- Chef::Log.debug "Caught InvalidPlatformVersion, this means that Chef::Version::Platform does not know how to turn #{node_version} into an x.y.z format"
- Chef::Log.debug(e)
+ end
+ return @values[platform][version] if key_matches.include?(version)
+ case key_matches.length
+ when 0
return nil
+ when 1
+ return @values[platform][key_matches.first]
+ else
+ raise "Multiple matches detected for #{platform} with values #{@values}. The matches are: #{key_matches}"
end
+ rescue Chef::Exceptions::InvalidCookbookVersion => e
+ # Lets not break because someone passes a weird string like 'default' :)
+ Chef::Log.trace(e)
+ Chef::Log.trace "InvalidCookbookVersion exceptions are common and expected here: the generic constraint matcher attempted to match something which is not a constraint. Moving on to next version or constraint"
+ nil
+ rescue Chef::Exceptions::InvalidPlatformVersion => e
+ Chef::Log.trace "Caught InvalidPlatformVersion, this means that Chef::Version::Platform does not know how to turn #{node_version} into an x.y.z format"
+ Chef::Log.trace(e)
+ nil
end
def set(platforms, value)
@@ -245,10 +243,22 @@ class Chef
end
end
+ # Shamelessly stolen from https://github.com/sethvargo/chef-sugar/blob/master/lib/chef/sugar/docker.rb
+ # Given a node object, returns whether the node is a docker container.
+ #
+ # === Parameters
+ # node:: [Chef::Node] The node to check.
+ #
+ # === Returns
+ # true:: if the current node is a docker container
+ # false:: if the current node is not a docker container
+ def docker?(node = run_context.nil? ? nil : run_context.node)
+ # Using "File.exist?('/.dockerinit') || File.exist?('/.dockerenv')" makes Travis sad,
+ # and that makes us sad too.
+ !!(node && node[:virtualization] && node[:virtualization][:systems] &&
+ node[:virtualization][:systems][:docker] && node[:virtualization][:systems][:docker] == "guest")
+ end
+
end
end
end
-
-# **DEPRECATED**
-# This used to be part of chef/mixin/language. Load the file to activate the deprecation code.
-require "chef/mixin/language"
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/dsl/powershell.rb
index 1a900af6f6..7dc7a9a0f6 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/dsl/powershell.rb
@@ -21,7 +21,7 @@ require "chef/util/powershell/ps_credential"
class Chef
module DSL
module Powershell
- def ps_credential(username = "placeholder", password)
+ def ps_credential(username = "placeholder", password) # rubocop:disable Style/OptionalArguments
Chef::Util::Powershell::PSCredential.new(username, password)
end
end
diff --git a/lib/chef/dsl/reboot_pending.rb b/lib/chef/dsl/reboot_pending.rb
index e8982d290e..2ebad7e039 100644
--- a/lib/chef/dsl/reboot_pending.rb
+++ b/lib/chef/dsl/reboot_pending.rb
@@ -30,7 +30,7 @@ class Chef
# Note that we will silently miss any other platform-specific reboot notices besides Windows+Ubuntu.
def reboot_pending?
# don't break when used as a mixin in contexts without #node (e.g. specs).
- if self.respond_to?(:node, true) && node.run_context.reboot_requested?
+ if respond_to?(:node, true) && node.run_context.reboot_requested?
true
elsif platform?("windows")
# PendingFileRenameOperations contains pairs (REG_MULTI_SZ) of filenames that cannot be updated
@@ -44,14 +44,7 @@ class Chef
registry_key_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired') ||
# Vista + Server 2008 and newer may have reboots pending from CBS
- registry_key_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending') ||
-
- # The mere existence of the UpdateExeVolatile key should indicate a pending restart for certain updates
- # http://support.microsoft.com/kb/832475
- Chef::Platform.windows_server_2003? &&
- (registry_key_exists?('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile') &&
- !registry_get_values('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').select { |v| v[:name] == "Flags" }[0].nil? &&
- [1, 2, 3].include?(registry_get_values('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').select { |v| v[:name] == "Flags" }[0][:data]))
+ registry_key_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending')
elsif platform?("ubuntu")
# This should work for Debian as well if update-notifier-common happens to be installed. We need an API for that.
File.exists?("/var/run/reboot-required")
diff --git a/lib/chef/dsl/recipe.rb b/lib/chef/dsl/recipe.rb
index c8b1aed74d..9da812cec0 100644
--- a/lib/chef/dsl/recipe.rb
+++ b/lib/chef/dsl/recipe.rb
@@ -1,7 +1,7 @@
-#--
+#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2008-2016, 2009-2015 Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,32 +21,37 @@ require "chef/exceptions"
require "chef/dsl/resources"
require "chef/dsl/definitions"
require "chef/dsl/data_query"
-require "chef/dsl/platform_introspection"
require "chef/dsl/include_recipe"
require "chef/dsl/registry_helper"
require "chef/dsl/reboot_pending"
require "chef/dsl/audit"
require "chef/dsl/powershell"
require "chef/dsl/core"
-require "chef/dsl/method_missing"
require "chef/mixin/lazy_module_include"
class Chef
module DSL
- # This is the "Recipe DSL" which is all the sugar, plus all the resources and definitions
- # which are mixed into user LWRPs/Resources/Providers.
+ # Part of a family of DSL mixins.
+ #
+ # Chef::DSL::Recipe mixes into Recipes and LWRP Providers.
+ # - this does not target core chef resources and providers.
+ # - this is restricted to recipe/resource/provider context where a resource collection exists.
+ # - cookbook authors should typically include modules into here.
#
- # - If you are writing cookbooks: you have come to the right place, please inject things
- # into here if you want to make them available to all recipe and non-core provider code.
+ # Chef::DSL::Core mixes into Recipes, LWRP Providers and Core Providers
+ # - this adds cores providers on top of the Recipe DSL.
+ # - this is restricted to recipe/resource/provider context where a resource collection exists.
+ # - core chef authors should typically include modules into here.
#
- # - If you are writing core chef: you have likely come to the wrong place, please consider
- # dropping your DSL modules into Chef::DSL::Core instead so that we can use them in core
- # providers (unless they are *only* intended for recipe code).
+ # Chef::DSL::Universal mixes into Recipes, LWRP Resources+Providers, Core Resources+Providers, and Attributes files.
+ # - this adds resources and attributes files.
+ # - do not add helpers which manipulate the resource collection.
+ # - this is for general-purpose stuff that is useful nearly everywhere.
+ # - it also pollutes the namespace of nearly every context, watch out.
#
module Recipe
include Chef::DSL::Core
include Chef::DSL::DataQuery
- include Chef::DSL::PlatformIntrospection
include Chef::DSL::IncludeRecipe
include Chef::DSL::RegistryHelper
include Chef::DSL::RebootPending
@@ -54,8 +59,6 @@ class Chef
include Chef::DSL::Powershell
include Chef::DSL::Resources
include Chef::DSL::Definitions
- # method_missing will disappear in Chef 13
- include Chef::DSL::MethodMissing
extend Chef::Mixin::LazyModuleInclude
def resource_class_for(snake_case_name)
@@ -71,19 +74,9 @@ class Chef
def exec(args)
raise Chef::Exceptions::ResourceNotFound, "exec was called, but you probably meant to use an execute resource. If not, please call Kernel#exec explicitly. The exec block called was \"#{args}\""
end
-
- # @deprecated Use Chef::DSL::Recipe instead, will be removed in Chef 13
- module FullDSL
- include Chef::DSL::Recipe
- extend Chef::Mixin::LazyModuleInclude
- end
end
end
end
# Avoid circular references for things that are only used in instance methods
require "chef/resource"
-
-# **DEPRECATED**
-# This used to be part of chef/mixin/recipe_definition_dsl_core. Load the file to activate the deprecation code.
-require "chef/mixin/recipe_definition_dsl_core"
diff --git a/lib/chef/dsl/resources.rb b/lib/chef/dsl/resources.rb
index 7bbfeb2914..638e626ae2 100644
--- a/lib/chef/dsl/resources.rb
+++ b/lib/chef/dsl/resources.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser <jkeiser@chef.io>
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2015-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,20 +33,11 @@ class Chef
include Chef::DSL::ChefProvisioning
def self.add_resource_dsl(dsl_name)
- begin
- module_eval(<<-EOM, __FILE__, __LINE__ + 1)
- def #{dsl_name}(*args, &block)
- Chef.log_deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (\#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: \#{args}") if args.size > 1
- declare_resource(#{dsl_name.inspect}, args[0], caller[0], &block)
+ module_eval(<<-EOM, __FILE__, __LINE__ + 1)
+ def #{dsl_name}(args = nil, &block)
+ declare_resource(#{dsl_name.inspect}, args, created_at: caller[0], &block)
end
EOM
- rescue SyntaxError
- # Handle the case where dsl_name has spaces, etc.
- define_method(dsl_name.to_sym) do |*args, &block|
- Chef.log_deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: #{args}") if args.size > 1
- declare_resource(dsl_name, args[0], caller[0], &block)
- end
- end
end
def self.remove_resource_dsl(dsl_name)
diff --git a/lib/chef/dsl/universal.rb b/lib/chef/dsl/universal.rb
new file mode 100644
index 0000000000..f3b79b1d60
--- /dev/null
+++ b/lib/chef/dsl/universal.rb
@@ -0,0 +1,52 @@
+#--
+# Author:: Adam Jacob (<adam@chef.io>)
+# Author:: Christopher Walters (<cw@chef.io>)
+# Copyright:: Copyright 2008-2016 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/dsl/platform_introspection"
+require "chef/mixin/powershell_exec"
+require "chef/mixin/powershell_out"
+require "chef/mixin/shell_out"
+
+class Chef
+ module DSL
+ # Part of a family of DSL mixins.
+ #
+ # Chef::DSL::Recipe mixes into Recipes and LWRP Providers.
+ # - this does not target core chef resources and providers.
+ # - this is restricted to recipe/resource/provider context where a resource collection exists.
+ # - cookbook authors should typically include modules into here.
+ #
+ # Chef::DSL::Core mixes into Recipes, LWRP Providers and Core Providers
+ # - this adds cores providers on top of the Recipe DSL.
+ # - this is restricted to recipe/resource/provider context where a resource collection exists.
+ # - core chef authors should typically include modules into here.
+ #
+ # Chef::DSL::Universal mixes into Recipes, LWRP Resources+Providers, Core Resources+Providers, and Attributes files.
+ # - this adds resources and attributes files.
+ # - do not add helpers which manipulate the resource collection.
+ # - this is for general-purpose stuff that is useful nearly everywhere.
+ # - it also pollutes the namespace of nearly every context, watch out.
+ #
+ module Universal
+ include Chef::DSL::PlatformIntrospection
+ include Chef::Mixin::PowershellExec
+ include Chef::Mixin::PowershellOut
+ include Chef::Mixin::ShellOut
+ end
+ end
+end
diff --git a/lib/chef/encrypted_data_bag_item.rb b/lib/chef/encrypted_data_bag_item.rb
index e696199c63..c7c6145f61 100644
--- a/lib/chef/encrypted_data_bag_item.rb
+++ b/lib/chef/encrypted_data_bag_item.rb
@@ -121,8 +121,8 @@ class Chef::EncryptedDataBagItem
#
def self.load(data_bag, name, secret = nil)
raw_hash = Chef::DataBagItem.load(data_bag, name)
- secret = secret || self.load_secret
- self.new(raw_hash, secret)
+ secret ||= load_secret
+ new(raw_hash, secret)
end
def self.load_secret(path = nil)
diff --git a/lib/chef/encrypted_data_bag_item/decryptor.rb b/lib/chef/encrypted_data_bag_item/decryptor.rb
index 773ff4e154..f35611d185 100644
--- a/lib/chef/encrypted_data_bag_item/decryptor.rb
+++ b/lib/chef/encrypted_data_bag_item/decryptor.rb
@@ -93,7 +93,7 @@ class Chef::EncryptedDataBagItem
plaintext << openssl_decryptor.final
rescue OpenSSL::Cipher::CipherError => e
# if the key length is less than 255 characters, and it contains slashes, we think it may be a path.
- raise DecryptionFailure, "Error decrypting data bag value: '#{e.message}'. Most likely the provided key is incorrect. #{ (@key.length < 255 && @key.include?('/')) ? 'You may need to use --secret-file rather than --secret.' : '' }"
+ raise DecryptionFailure, "Error decrypting data bag value: '#{e.message}'. Most likely the provided key is incorrect. #{(@key.length < 255 && @key.include?('/')) ? 'You may need to use --secret-file rather than --secret.' : ''}"
end
end
@@ -144,7 +144,7 @@ class Chef::EncryptedDataBagItem
plaintext << openssl_decryptor.final
rescue OpenSSL::Cipher::CipherError => e
# if the key length is less than 255 characters, and it contains slashes, we think it may be a path.
- raise DecryptionFailure, "Error decrypting data bag value: '#{e.message}'. Most likely the provided key is incorrect. #{ ( @key.length < 255 && @key.include?('/')) ? 'You may need to use --secret-file rather than --secret.' : '' }"
+ raise DecryptionFailure, "Error decrypting data bag value: '#{e.message}'. Most likely the provided key is incorrect. #{( @key.length < 255 && @key.include?('/')) ? 'You may need to use --secret-file rather than --secret.' : ''}"
end
end
diff --git a/lib/chef/environment.rb b/lib/chef/environment.rb
index f6d22843d4..be05b58989 100644
--- a/lib/chef/environment.rb
+++ b/lib/chef/environment.rb
@@ -3,7 +3,7 @@
# Author:: Seth Falcon (<seth@chef.io>)
# Author:: John Keiser (<jkeiser@ospcode.com>)
# Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright 2010-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,8 +34,6 @@ class Chef
include Chef::Mixin::ParamsValidate
include Chef::Mixin::FromFile
- attr_accessor :chef_server_rest
-
COMBINED_COOKBOOK_CONSTRAINT = /(.+)(?:[\s]+)((?:#{Chef::VersionConstraint::OPS.join('|')})(?:[\s]+).+)$/
def initialize(chef_server_rest: nil)
@@ -171,7 +169,7 @@ class Chef
unless params[:cookbook_version].nil?
params[:cookbook_version].each do |index, cookbook_constraint_spec|
unless cookbook_constraint_spec.nil? || cookbook_constraint_spec.size == 0
- valid = valid && update_cookbook_constraint_from_param(index, cookbook_constraint_spec)
+ valid &&= update_cookbook_constraint_from_param(index, cookbook_constraint_spec)
end
end
end
@@ -216,11 +214,6 @@ class Chef
end
end
- def self.json_create(o)
- Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please use Chef::Environment#from_hash")
- from_hash(o)
- end
-
def self.from_hash(o)
environment = new
environment.name(o["name"])
@@ -244,10 +237,10 @@ class Chef
end
def self.load(name)
- if Chef::Config[:solo]
+ if Chef::Config[:solo_legacy_mode]
load_from_file(name)
else
- self.from_hash(chef_server_rest.get("environments/#{name}"))
+ from_hash(chef_server_rest.get("environments/#{name}"))
end
end
@@ -302,24 +295,22 @@ class Chef
def self.validate_cookbook_versions(cv)
return false unless cv.kind_of?(Hash)
- cv.each do |cookbook, version|
+ cv.each_value do |version|
return false unless Chef::Environment.validate_cookbook_version(version)
end
true
end
def self.validate_cookbook_version(version)
- begin
- if Chef::Config[:solo]
- raise Chef::Exceptions::IllegalVersionConstraint,
- "Environment cookbook version constraints not allowed in chef-solo"
- else
- Chef::VersionConstraint.new version
- true
- end
- rescue ArgumentError
- false
+ if Chef::Config[:solo_legacy_mode]
+ raise Chef::Exceptions::IllegalVersionConstraint,
+ "Environment cookbook version constraints not allowed in chef-solo"
+ else
+ Chef::VersionConstraint.new version
+ true
end
+ rescue ArgumentError
+ false
end
end
diff --git a/lib/chef/event_dispatch/base.rb b/lib/chef/event_dispatch/base.rb
index 6c6b2fa3bb..0886d63152 100644
--- a/lib/chef/event_dispatch/base.rb
+++ b/lib/chef/event_dispatch/base.rb
@@ -36,11 +36,11 @@ class Chef
end
# Called at the end a successful Chef run.
- def run_completed(node)
+ def run_completed(node, run_status)
end
# Called at the end of a failed Chef run.
- def run_failed(exception)
+ def run_failed(exception, run_status)
end
# Called right after ohai runs.
@@ -195,6 +195,22 @@ class Chef
def lwrp_load_complete
end
+ # Called when an ohai plugin file loading starts
+ def ohai_plugin_load_start(file_count)
+ end
+
+ # Called when an ohai plugin file has been loaded
+ def ohai_plugin_file_loaded(path)
+ end
+
+ # Called when an ohai plugin file has an error on load.
+ def ohai_plugin_file_load_failed(path, exception)
+ end
+
+ # Called when an ohai plugin file loading has finished
+ def ohai_plugin_load_complete
+ end
+
# Called before attribute files are loaded
def attribute_load_start(attribute_file_count)
end
@@ -232,11 +248,11 @@ class Chef
end
# Called after the recipe has been loaded
- def recipe_file_loaded(path)
+ def recipe_file_loaded(path, recipe)
end
# Called after a recipe file fails to load
- def recipe_file_load_failed(path, exception)
+ def recipe_file_load_failed(path, exception, recipe)
end
# Called when a recipe cannot be resolved
@@ -421,6 +437,9 @@ class Chef
def msg(message)
end
+ # Called when an attribute is changed by simple assignment
+ def attribute_changed(precedence, keys, value)
+ end
end
end
end
diff --git a/lib/chef/event_dispatch/dsl.rb b/lib/chef/event_dispatch/dsl.rb
index 999d536fbe..0a9c082fc1 100644
--- a/lib/chef/event_dispatch/dsl.rb
+++ b/lib/chef/event_dispatch/dsl.rb
@@ -35,10 +35,10 @@ class Chef
# and associated event dispatcher is set, else fallback to
# Chef::Config[:event_handlers]
if Chef.run_context && Chef.run_context.events
- Chef::Log.debug("Registering handler '#{name}' using events api")
+ Chef::Log.trace("Registering handler '#{name}' using events api")
Chef.run_context.events.register(handler)
else
- Chef::Log.debug("Registering handler '#{name}' using global config")
+ Chef::Log.trace("Registering handler '#{name}' using global config")
Chef::Config[:event_handlers] << handler
end
end
diff --git a/lib/chef/event_loggers/windows_eventlog.rb b/lib/chef/event_loggers/windows_eventlog.rb
index f8c3d346c5..1ff87945e7 100644
--- a/lib/chef/event_loggers/windows_eventlog.rb
+++ b/lib/chef/event_loggers/windows_eventlog.rb
@@ -38,7 +38,7 @@ class Chef
SOURCE = "Chef"
def self.available?
- return Chef::Platform.windows?
+ Chef::Platform.windows?
end
def initialize
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index 6afcc9c51e..1ed71d2a55 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Seth Falcon (<seth@chef.io>)
# Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -42,9 +42,12 @@ class Chef
end
class Application < RuntimeError; end
+ class SigInt < RuntimeError; end
+ class SigTerm < RuntimeError; end
class Cron < RuntimeError; end
- class Env < RuntimeError; end
+ class WindowsEnv < RuntimeError; end
class Exec < RuntimeError; end
+ class Execute < RuntimeError; end
class ErlCall < RuntimeError; end
class FileNotFound < RuntimeError; end
class Package < RuntimeError; end
@@ -66,6 +69,10 @@ class Chef
class Group < RuntimeError; end
class Link < RuntimeError; end
class Mount < RuntimeError; end
+ class Reboot < Exception; end # rubocop:disable Lint/InheritException
+ class RebootPending < Exception; end # rubocop:disable Lint/InheritException
+ class RebootFailed < Mixlib::ShellOut::ShellCommandFailed; end
+ class ClientUpgraded < Exception; end # rubocop:disable Lint/InheritException
class PrivateKeyMissing < RuntimeError; end
class CannotWritePrivateKey < RuntimeError; end
class RoleNotFound < RuntimeError; end
@@ -93,7 +100,12 @@ class Chef
# for back compat, need to raise an error that inherits from ArgumentError
class CookbookNotFoundInRepo < ArgumentError; end
class RecipeNotFound < ArgumentError; end
+ # AttributeNotFound really means the attribute file could not be found
class AttributeNotFound < RuntimeError; end
+ # NoSuchAttribute is raised on access by node.read!("foo", "bar") when node["foo"]["bar"] does not exist.
+ class NoSuchAttribute < RuntimeError; end
+ # AttributeTypeMismatch is raised by node.write!("foo", "bar", "baz") when e.g. node["foo"] = "bar" (overwriting String with Hash)
+ class AttributeTypeMismatch < RuntimeError; end
class MissingCookbookDependency < StandardError; end # CHEF-5120
class InvalidCommandOption < RuntimeError; end
class CommandTimeout < RuntimeError; end
@@ -140,7 +152,7 @@ class Chef
# Thrown when Win32 API layer binds to non-existent Win32 function. Occurs
# when older versions of Windows don't support newer Win32 API functions.
- class Win32APIFunctionNotImplemented < NotImplementedError; end
+ class Win32APIFunctionNotImplemented < NotImplementedError; end # rubocop:disable Lint/InheritException
# Attempting to run windows code on a not-windows node
class Win32NotWindows < RuntimeError; end
class WindowsNotAdmin < RuntimeError; end
@@ -166,7 +178,7 @@ class Chef
# A different version of a cookbook was added to a
# VersionedRecipeList than the one already there.
- class CookbookVersionConflict < ArgumentError ; end
+ class CookbookVersionConflict < ArgumentError; end
# does not follow X.Y.Z format. ArgumentError?
class InvalidPlatformVersion < ArgumentError; end
@@ -177,7 +189,7 @@ class Chef
class InvalidVersionConstraint < ArgumentError; end
# Version constraints are not allowed in chef-solo
- class IllegalVersionConstraint < NotImplementedError; end
+ class IllegalVersionConstraint < NotImplementedError; end # rubocop:disable Lint/InheritException
class MetadataNotValid < StandardError; end
class MetadataNotFound < StandardError
@@ -227,6 +239,10 @@ class Chef
class Win32RegBadValueSize < ArgumentError; end
class Win32RegTypesMismatch < ArgumentError; end
+ # incorrect input for registry_key create action throws following error
+ class RegKeyValuesTypeMissing < ArgumentError; end
+ class RegKeyValuesDataMissing < ArgumentError; end
+
class InvalidEnvironmentPath < ArgumentError; end
class EnvironmentNotFound < RuntimeError; end
@@ -239,7 +255,7 @@ class Chef
class ChildConvergeError < RuntimeError; end
- class DeprecatedFeatureError < RuntimeError;
+ class DeprecatedFeatureError < RuntimeError
def initalize(message)
super("#{message} (raising error due to treat_deprecation_warnings_as_errors being set)")
end
@@ -292,7 +308,7 @@ class Chef
def raise!
unless empty?
- raise self.for_raise
+ raise for_raise
end
end
@@ -426,18 +442,20 @@ This error is most often caused by network issues (proxies, etc) outside of chef
end
end
- class AuditControlGroupDuplicate < RuntimeError
+ class AuditError < RuntimeError; end
+
+ class AuditControlGroupDuplicate < AuditError
def initialize(name)
super "Control group with name '#{name}' has already been defined"
end
end
- class AuditNameMissing < RuntimeError; end
- class NoAuditsProvided < RuntimeError
+ class AuditNameMissing < AuditError; end
+ class NoAuditsProvided < AuditError
def initialize
super "You must provide a block with controls"
end
end
- class AuditsFailed < RuntimeError
+ class AuditsFailed < AuditError
def initialize(num_failed, num_total)
super "Audit phase found failures - #{num_failed}/#{num_total} controls failed"
end
@@ -497,8 +515,11 @@ This error is most often caused by network issues (proxies, etc) outside of chef
"Resource #{r['Name']} is a binary resource"
end
end
- super "Found multiple matching resources. #{matches_info.join("\n")}"
+ super "Found multiple resources matching #{matches_info[0]["Module"]["Name"]}:\n#{(matches_info.map { |f| f["Module"]["Version"] }).uniq.join("\n")}"
end
end
+
+ # exception specific to invalid usage of 'dsc_resource' resource
+ class DSCModuleNameMissing < ArgumentError; end
end
end
diff --git a/lib/chef/file_access_control/unix.rb b/lib/chef/file_access_control/unix.rb
index 1746db44d3..5d674c2c32 100644
--- a/lib/chef/file_access_control/unix.rb
+++ b/lib/chef/file_access_control/unix.rb
@@ -79,20 +79,20 @@ class Chef
def should_update_owner?
if target_uid.nil?
# the user has not specified a permission on the new resource, so we never manage it with FAC
- Chef::Log.debug("Found target_uid == nil, so no owner was specified on resource, not managing owner")
- return false
+ Chef::Log.trace("Found target_uid == nil, so no owner was specified on resource, not managing owner")
+ false
elsif current_uid.nil?
# the user has specified a permission, and we are creating a file, so always enforce permissions
- Chef::Log.debug("Found current_uid == nil, so we are creating a new file, updating owner")
- return true
+ Chef::Log.trace("Found current_uid == nil, so we are creating a new file, updating owner")
+ true
elsif target_uid != current_uid
# the user has specified a permission, and it does not match the file, so fix the permission
- Chef::Log.debug("Found target_uid != current_uid, updating owner")
- return true
+ Chef::Log.trace("Found target_uid != current_uid, updating owner")
+ true
else
- Chef::Log.debug("Found target_uid == current_uid, not updating owner")
+ Chef::Log.trace("Found target_uid == current_uid, not updating owner")
# the user has specified a permission, but it matches the file, so behave idempotently
- return false
+ false
end
end
@@ -117,13 +117,13 @@ class Chef
end
def gid_from_resource(resource)
- return nil if resource == nil || resource.group.nil?
+ return nil if resource.nil? || resource.group.nil?
if resource.group.kind_of?(String)
diminished_radix_complement( Etc.getgrnam(resource.group).gid )
elsif resource.group.kind_of?(Integer)
resource.group
else
- Chef::Log.error("The `group` parameter of the #@resource resource is set to an invalid value (#{resource.owner.inspect})")
+ Chef::Log.error("The `group` parameter of the #{@resource} resource is set to an invalid value (#{resource.owner.inspect})")
raise ArgumentError, "cannot resolve #{resource.group.inspect} to gid, group must be a string or integer"
end
rescue ArgumentError
@@ -132,26 +132,26 @@ class Chef
a.failure_message(Chef::Exceptions::GroupIDNotFound, "cannot determine group id for '#{resource.group}', does the group exist on this system?")
a.whyrun("Assuming group #{resource.group} would have been created")
end
- return nil
+ nil
end
def should_update_group?
if target_gid.nil?
# the user has not specified a permission on the new resource, so we never manage it with FAC
- Chef::Log.debug("Found target_gid == nil, so no group was specified on resource, not managing group")
- return false
+ Chef::Log.trace("Found target_gid == nil, so no group was specified on resource, not managing group")
+ false
elsif current_gid.nil?
# the user has specified a permission, and we are creating a file, so always enforce permissions
- Chef::Log.debug("Found current_gid == nil, so we are creating a new file, updating group")
- return true
+ Chef::Log.trace("Found current_gid == nil, so we are creating a new file, updating group")
+ true
elsif target_gid != current_gid
# the user has specified a permission, and it does not match the file, so fix the permission
- Chef::Log.debug("Found target_gid != current_gid, updating group")
- return true
+ Chef::Log.trace("Found target_gid != current_gid, updating group")
+ true
else
- Chef::Log.debug("Found target_gid == current_gid, not updating group")
+ Chef::Log.trace("Found target_gid == current_gid, not updating group")
# the user has specified a permission, but it matches the file, so behave idempotently
- return false
+ false
end
end
@@ -168,7 +168,7 @@ class Chef
end
def mode_from_resource(res)
- return nil if res == nil || res.mode.nil?
+ return nil if res.nil? || res.mode.nil?
(res.mode.respond_to?(:oct) ? res.mode.oct : res.mode.to_i) & 007777
end
@@ -187,22 +187,22 @@ class Chef
def should_update_mode?
if target_mode.nil?
# the user has not specified a permission on the new resource, so we never manage it with FAC
- Chef::Log.debug("Found target_mode == nil, so no mode was specified on resource, not managing mode")
- return false
+ Chef::Log.trace("Found target_mode == nil, so no mode was specified on resource, not managing mode")
+ false
elsif current_mode.nil?
# the user has specified a permission, and we are creating a file, so always enforce permissions
- Chef::Log.debug("Found current_mode == nil, so we are creating a new file, updating mode")
- return true
+ Chef::Log.trace("Found current_mode == nil, so we are creating a new file, updating mode")
+ true
elsif target_mode != current_mode
# the user has specified a permission, and it does not match the file, so fix the permission
- Chef::Log.debug("Found target_mode != current_mode, updating mode")
- return true
+ Chef::Log.trace("Found target_mode != current_mode, updating mode")
+ true
elsif suid_bit_set? && (should_update_group? || should_update_owner?)
- return true
+ true
else
- Chef::Log.debug("Found target_mode == current_mode, not updating mode")
+ Chef::Log.trace("Found target_mode == current_mode, not updating mode")
# the user has specified a permission, but it matches the file, so behave idempotently
- return false
+ false
end
end
@@ -264,13 +264,13 @@ class Chef
end
def uid_from_resource(resource)
- return nil if resource == nil || resource.owner.nil?
+ return nil if resource.nil? || resource.owner.nil?
if resource.owner.kind_of?(String)
diminished_radix_complement( Etc.getpwnam(resource.owner).uid )
elsif resource.owner.kind_of?(Integer)
resource.owner
else
- Chef::Log.error("The `owner` parameter of the #@resource resource is set to an invalid value (#{resource.owner.inspect})")
+ Chef::Log.error("The `owner` parameter of the #{@resource} resource is set to an invalid value (#{resource.owner.inspect})")
raise ArgumentError, "cannot resolve #{resource.owner.inspect} to uid, owner must be a string or integer"
end
rescue ArgumentError
@@ -279,11 +279,11 @@ class Chef
a.failure_message(Chef::Exceptions::UserIDNotFound, "cannot determine user id for '#{resource.owner}', does the user exist on this system?")
a.whyrun("Assuming user #{resource.owner} would have been created")
end
- return nil
+ nil
end
def suid_bit_set?
- return target_mode & 04000 > 0
+ target_mode & 04000 > 0
end
end
end
diff --git a/lib/chef/file_access_control/windows.rb b/lib/chef/file_access_control/windows.rb
index 6b7184bbd3..2c6b69c257 100644
--- a/lib/chef/file_access_control/windows.rb
+++ b/lib/chef/file_access_control/windows.rb
@@ -100,7 +100,7 @@ class Chef
new_target_acl << children_ace
end
end
- return actual_acl == new_target_acl
+ actual_acl == new_target_acl
end
def existing_descriptor
@@ -128,7 +128,7 @@ class Chef
end
def should_update_dacl?
- return true unless ::File.exists?(file)
+ return true unless ::File.exists?(file) || ::File.symlink?(file)
dacl = target_dacl
existing_dacl = existing_descriptor.dacl
inherits = target_inherits
@@ -161,7 +161,7 @@ class Chef
end
def should_update_group?
- return true unless ::File.exists?(file)
+ return true unless ::File.exists?(file) || ::File.symlink?(file)
(group = target_group) && (group != existing_descriptor.group)
end
@@ -180,7 +180,7 @@ class Chef
end
def should_update_owner?
- return true unless ::File.exists?(file)
+ return true unless ::File.exists?(file) || ::File.symlink?(file)
(owner = target_owner) && (owner != existing_descriptor.owner)
end
diff --git a/lib/chef/file_cache.rb b/lib/chef/file_cache.rb
index cefc9da1eb..8e9bb1e3e4 100644
--- a/lib/chef/file_cache.rb
+++ b/lib/chef/file_cache.rb
@@ -85,7 +85,7 @@ class Chef
File.join(create_cache_path(File.join(file_path_array), true), file_name)
)
else
- raise RuntimeError, "Cannot move #{file} to #{path}!"
+ raise "Cannot move #{file} to #{path}!"
end
end
diff --git a/lib/chef/file_content_management/content_base.rb b/lib/chef/file_content_management/content_base.rb
index 6080e37180..d5593579b0 100644
--- a/lib/chef/file_content_management/content_base.rb
+++ b/lib/chef/file_content_management/content_base.rb
@@ -23,12 +23,14 @@ class Chef
attr_reader :run_context
attr_reader :new_resource
attr_reader :current_resource
+ attr_reader :logger
- def initialize(new_resource, current_resource, run_context)
+ def initialize(new_resource, current_resource, run_context, logger = Chef::Log.with_child)
@new_resource = new_resource
@current_resource = current_resource
@run_context = run_context
@tempfile_loaded = false
+ @logger = logger
end
def tempfile
diff --git a/lib/chef/file_content_management/deploy/cp.rb b/lib/chef/file_content_management/deploy/cp.rb
index 14dde85d13..8d804e7493 100644
--- a/lib/chef/file_content_management/deploy/cp.rb
+++ b/lib/chef/file_content_management/deploy/cp.rb
@@ -34,12 +34,12 @@ class Chef
#
class Cp
def create(file)
- Chef::Log.debug("Touching #{file} to create it")
+ Chef::Log.trace("Touching #{file} to create it")
FileUtils.touch(file)
end
def deploy(src, dst)
- Chef::Log.debug("Copying temporary file #{src} into place at #{dst}")
+ Chef::Log.trace("Copying temporary file #{src} into place at #{dst}")
FileUtils.cp(src, dst)
end
end
diff --git a/lib/chef/file_content_management/deploy/mv_unix.rb b/lib/chef/file_content_management/deploy/mv_unix.rb
index 3805b3bef3..cbc9b903a8 100644
--- a/lib/chef/file_content_management/deploy/mv_unix.rb
+++ b/lib/chef/file_content_management/deploy/mv_unix.rb
@@ -30,19 +30,19 @@ class Chef
def create(file)
# this is very simple, but it ensures that ownership and file modes take
# good defaults, in particular mode needs to obey umask on create
- Chef::Log.debug("Touching #{file} to create it")
+ Chef::Log.trace("Touching #{file} to create it")
FileUtils.touch(file)
end
def deploy(src, dst)
# we are only responsible for content so restore the dst files perms
- Chef::Log.debug("Reading modes from #{dst} file")
+ Chef::Log.trace("Reading modes from #{dst} file")
stat = ::File.stat(dst)
mode = stat.mode & 07777
uid = stat.uid
gid = stat.gid
- Chef::Log.debug("Applying mode = #{mode.to_s(8)}, uid = #{uid}, gid = #{gid} to #{src}")
+ Chef::Log.trace("Applying mode = #{mode.to_s(8)}, uid = #{uid}, gid = #{gid} to #{src}")
# i own the inode, so should be able to at least chmod it
::File.chmod(mode, src)
@@ -67,7 +67,7 @@ class Chef
Chef::Log.warn("Could not set gid = #{gid} on #{src}, file modes not preserved")
end
- Chef::Log.debug("Moving temporary file #{src} into place at #{dst}")
+ Chef::Log.trace("Moving temporary file #{src} into place at #{dst}")
FileUtils.mv(src, dst)
end
end
diff --git a/lib/chef/file_content_management/deploy/mv_windows.rb b/lib/chef/file_content_management/deploy/mv_windows.rb
index 0e6e6cd76f..210400bc08 100644
--- a/lib/chef/file_content_management/deploy/mv_windows.rb
+++ b/lib/chef/file_content_management/deploy/mv_windows.rb
@@ -35,7 +35,7 @@ class Chef
ACL = Security::ACL
def create(file)
- Chef::Log.debug("Touching #{file} to create it")
+ Chef::Log.trace("Touching #{file} to create it")
FileUtils.touch(file)
end
diff --git a/lib/chef/file_content_management/tempfile.rb b/lib/chef/file_content_management/tempfile.rb
index bba4b34d82..c3bf07d92b 100644
--- a/lib/chef/file_content_management/tempfile.rb
+++ b/lib/chef/file_content_management/tempfile.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright 2013-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -40,11 +40,12 @@ class Chef
tempfile_dirnames.each do |tempfile_dirname|
begin
- tf = ::Tempfile.open(tempfile_basename, tempfile_dirname)
+ # preserving the file extension of the target filename should be considered a public API
+ tf = ::Tempfile.open([tempfile_basename, tempfile_extension], tempfile_dirname)
break
rescue SystemCallError => e
message = "Creating temp file under '#{tempfile_dirname}' failed with: '#{e.message}'"
- Chef::Log.debug(message)
+ Chef::Log.trace(message)
errors << message
end
end
@@ -63,10 +64,19 @@ class Chef
# as the arguments to Tempfile.new() consistently.
#
def tempfile_basename
- basename = ::File.basename(@new_resource.name)
+ basename = ::File.basename(@new_resource.path, tempfile_extension)
+ # the leading "[.]chef-" here should be considered a public API and should not be changed
basename.insert 0, "chef-"
basename.insert 0, "." unless Chef::Platform.windows? # dotfile if we're not on windows
- basename
+ basename.scrub
+ end
+
+ # this is similar to File.extname() but greedy about the extension (from the first dot, not the last dot)
+ def tempfile_extension
+ # complexity here is due to supporting mangling non-UTF8 strings (e.g. latin-1 filenames with characters that are illegal in UTF-8)
+ b = File.basename(@new_resource.path)
+ i = b.index(".")
+ i.nil? ? "" : b[i..-1].scrub
end
# Returns the possible directories for the tempfile to be created in.
diff --git a/lib/chef/formatters/base.rb b/lib/chef/formatters/base.rb
index 3641e619e9..0b27b55048 100644
--- a/lib/chef/formatters/base.rb
+++ b/lib/chef/formatters/base.rb
@@ -137,7 +137,7 @@ class Chef
def resource_failed(resource, action, exception)
description = ErrorMapper.resource_failed(resource, action, exception)
- display_error(description)
+ display_error(description) unless resource.ignore_failure && resource.ignore_failure.to_s == "quiet"
end
# Generic callback for any attribute/library/lwrp/recipe file in a
@@ -203,17 +203,27 @@ class Chef
end
# Delegates to #file_loaded
- def recipe_file_loaded(path)
+ def recipe_file_loaded(path, recipe)
file_loaded(path)
end
# Delegates to #file_load_failed
- def recipe_file_load_failed(path, exception)
+ def recipe_file_load_failed(path, exception, recipe)
file_load_failed(path, exception)
end
def deprecation(message, location = caller(2..2)[0])
- Chef::Log.deprecation("#{message} at #{location}")
+ out = if is_structured_deprecation?(message)
+ message.inspect
+ else
+ "#{message} at #{location}"
+ end
+
+ Chef::Log.deprecation(out)
+ end
+
+ def is_structured_deprecation?(deprecation)
+ deprecation.kind_of?(Chef::Deprecated::Base)
end
def is_formatter?
diff --git a/lib/chef/formatters/doc.rb b/lib/chef/formatters/doc.rb
index d43f1993b2..0c51cc2cfb 100644
--- a/lib/chef/formatters/doc.rb
+++ b/lib/chef/formatters/doc.rb
@@ -32,9 +32,9 @@ class Chef
def pretty_elapsed_time
time = elapsed_time
- if time < 60 then
+ if time < 60
message = Time.at(time).utc.strftime("%S seconds")
- elsif time < 3600 then
+ elsif time < 3600
message = Time.at(time).utc.strftime("%M minutes %S seconds")
else
message = Time.at(time).utc.strftime("%H hours %M minutes %S seconds")
@@ -61,9 +61,10 @@ class Chef
if !deprecations.empty?
puts_line ""
puts_line "Deprecated features used!"
- deprecations.each do |message, locations|
+ deprecations.each do |message, details|
+ locations = details[:locations]
if locations.size == 1
- puts_line " #{message} at #{locations.size} location:"
+ puts_line " #{message} at 1 location:"
else
puts_line " #{message} at #{locations.size} locations:"
end
@@ -74,6 +75,9 @@ class Chef
prefix = " "
end
end
+ unless details[:url].nil?
+ puts_line " See #{details[:url]} for further details."
+ end
end
puts_line ""
end
@@ -416,8 +420,13 @@ class Chef
end
# Save deprecations to the screen until the end
- deprecations[message] ||= Set.new
- deprecations[message] << location
+ if is_structured_deprecation?(message)
+ url = message.url
+ message = message.message
+ end
+
+ deprecations[message] ||= { url: url, locations: Set.new }
+ deprecations[message][:locations] << location
end
def indent
diff --git a/lib/chef/formatters/error_description.rb b/lib/chef/formatters/error_description.rb
index 8d7f940181..ceb2f68539 100644
--- a/lib/chef/formatters/error_description.rb
+++ b/lib/chef/formatters/error_description.rb
@@ -17,6 +17,8 @@
# limitations under the License.
#
+require "chef/version"
+
class Chef
module Formatters
# == Formatters::ErrorDescription
@@ -45,10 +47,10 @@ class Chef
display_section(heading, text, out)
end
end
- display_section("Platform:", RUBY_PLATFORM, out)
+ display_section("System Info:", error_context_info, out)
end
- def for_json()
+ def for_json
{
"title" => @title,
"sections" => @sections,
@@ -64,6 +66,21 @@ class Chef
out.puts "\n"
end
+ def error_context_info
+ context_info = { chef_version: Chef::VERSION }
+ if Chef.node
+ context_info[:platform] = Chef.node["platform"]
+ context_info[:platform_version] = Chef.node["platform_version"]
+ end
+ # A string like "ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]"
+ context_info[:ruby] = RUBY_DESCRIPTION
+ # The argv[0] value.
+ context_info[:program_name] = $PROGRAM_NAME
+ # This is kind of wonky but it's the only way to get the entry path script.
+ context_info[:executable] = File.realpath(caller.last[/^(.*):\d+:in /, 1])
+ context_info.map { |k, v| "#{k}=#{v}" }.join("\n")
+ end
+
end
end
end
diff --git a/lib/chef/formatters/error_inspectors/api_error_formatting.rb b/lib/chef/formatters/error_inspectors/api_error_formatting.rb
index 2415d0f4bb..53549ee77c 100644
--- a/lib/chef/formatters/error_inspectors/api_error_formatting.rb
+++ b/lib/chef/formatters/error_inspectors/api_error_formatting.rb
@@ -172,11 +172,10 @@ E
# .../lib/ruby/1.9.1/net/http.rb:2709:in `read_body'
# .../lib/ruby/1.9.1/net/http.rb:2736:in `body'
# .../lib/chef/formatters/error_inspectors/api_error_formatting.rb:91:in `rescue in format_rest_error'
- begin
- exception.response.body
- rescue Exception
- "Cannot fetch the contents of the response."
- end
+
+ exception.response.body
+ rescue Exception
+ "Cannot fetch the contents of the response."
end
end
diff --git a/lib/chef/formatters/error_inspectors/compile_error_inspector.rb b/lib/chef/formatters/error_inspectors/compile_error_inspector.rb
index d5ed69a83d..ea423a2f97 100644
--- a/lib/chef/formatters/error_inspectors/compile_error_inspector.rb
+++ b/lib/chef/formatters/error_inspectors/compile_error_inspector.rb
@@ -108,7 +108,7 @@ class Chef
def culprit_backtrace_entry
@culprit_backtrace_entry ||= begin
bt_entry = filtered_bt.first
- Chef::Log.debug("Backtrace entry for compile error: '#{bt_entry}'")
+ Chef::Log.trace("Backtrace entry for compile error: '#{bt_entry}'")
bt_entry
end
end
@@ -116,7 +116,7 @@ class Chef
def culprit_line
@culprit_line ||= begin
line_number = culprit_backtrace_entry[/^(?:.\:)?[^:]+:([\d]+)/, 1].to_i
- Chef::Log.debug("Line number of compile error: '#{line_number}'")
+ Chef::Log.trace("Line number of compile error: '#{line_number}'")
line_number
end
end
@@ -138,7 +138,7 @@ class Chef
begin
filters = Array(Chef::Config.cookbook_path).map { |p| /^#{Regexp.escape(p)}/i }
r = exception.backtrace.select { |line| filters.any? { |filter| line =~ filter } }
- Chef::Log.debug("Filtered backtrace of compile error: #{r.join(",")}")
+ Chef::Log.trace("Filtered backtrace of compile error: #{r.join(",")}")
r
end
end
diff --git a/lib/chef/formatters/error_mapper.rb b/lib/chef/formatters/error_mapper.rb
index 4786a20465..eb89662308 100644
--- a/lib/chef/formatters/error_mapper.rb
+++ b/lib/chef/formatters/error_mapper.rb
@@ -30,7 +30,7 @@ class Chef
headline = "Chef encountered an error attempting to create the client \"#{node_name}\""
description = ErrorDescription.new(headline)
error_inspector.add_explanation(description)
- return description
+ description
end
def self.node_load_failed(node_name, exception, config)
@@ -38,7 +38,7 @@ class Chef
headline = "Chef encountered an error attempting to load the node data for \"#{node_name}\""
description = ErrorDescription.new(headline)
error_inspector.add_explanation(description)
- return description
+ description
end
def self.run_list_expand_failed(node, exception)
@@ -46,7 +46,7 @@ class Chef
headline = "Error expanding the run_list:"
description = ErrorDescription.new(headline)
error_inspector.add_explanation(description)
- return description
+ description
end
def self.cookbook_resolution_failed(expanded_run_list, exception)
@@ -54,7 +54,7 @@ class Chef
headline = "Error Resolving Cookbooks for Run List:"
description = ErrorDescription.new(headline)
error_inspector.add_explanation(description)
- return description
+ description
end
def self.cookbook_sync_failed(cookbooks, exception)
@@ -62,7 +62,7 @@ class Chef
headline = "Error Syncing Cookbooks:"
description = ErrorDescription.new(headline)
error_inspector.add_explanation(description)
- return description
+ description
end
def self.resource_failed(resource, action, exception)
@@ -70,7 +70,7 @@ class Chef
headline = "Error executing action `#{action}` on resource '#{resource}'"
description = ErrorDescription.new(headline)
error_inspector.add_explanation(description)
- return description
+ description
end
def self.file_load_failed(path, exception)
diff --git a/lib/chef/guard_interpreter/default_guard_interpreter.rb b/lib/chef/guard_interpreter/default_guard_interpreter.rb
index 449ca9a316..52dc803858 100644
--- a/lib/chef/guard_interpreter/default_guard_interpreter.rb
+++ b/lib/chef/guard_interpreter/default_guard_interpreter.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,7 +33,11 @@ class Chef
public
def evaluate
- shell_out(@command, @command_opts).status.success?
+ result = shell_out_with_systems_locale(@command, @command_opts)
+ Chef::Log.debug "Command failed: #{result.stderr}" unless result.status.success?
+ result.status.success?
+ # Timeout fails command rather than chef-client run, see:
+ # https://tickets.opscode.com/browse/CHEF-2690
rescue Chef::Exceptions::CommandTimeout
Chef::Log.warn "Command '#{@command}' timed out"
false
diff --git a/lib/chef/guard_interpreter/resource_guard_interpreter.rb b/lib/chef/guard_interpreter/resource_guard_interpreter.rb
index 6df60aec89..e245eab2c7 100644
--- a/lib/chef/guard_interpreter/resource_guard_interpreter.rb
+++ b/lib/chef/guard_interpreter/resource_guard_interpreter.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright 2014-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -108,7 +108,7 @@ class Chef
def block_from_attributes(attributes)
Proc.new do
- attributes.keys.each do |attribute_name|
+ attributes.each_key do |attribute_name|
send(attribute_name, attributes[attribute_name]) if respond_to?(attribute_name)
end
end
diff --git a/lib/chef/handler.rb b/lib/chef/handler.rb
index b720a11a45..34db223d43 100644
--- a/lib/chef/handler.rb
+++ b/lib/chef/handler.rb
@@ -19,14 +19,12 @@ require "chef/client"
require "forwardable"
class Chef
- # == Chef::Handler
# The base class for an Exception or Notification Handler. Create your own
# handler by subclassing Chef::Handler. When a Chef run fails with an
# uncaught Exception, Chef will set the +run_status+ on your handler and call
# +report+
#
- # ===Example:
- #
+ # @example
# require 'net/smtp'
#
# module MyOrg
@@ -57,16 +55,47 @@ class Chef
#
class Handler
+ def self.handler_for(*args)
+ if args.include?(:start)
+ Chef::Config[:start_handlers] ||= []
+ Chef::Config[:start_handlers] |= [self]
+ end
+ if args.include?(:report)
+ Chef::Config[:report_handlers] ||= []
+ Chef::Config[:report_handlers] |= [self]
+ end
+ if args.include?(:exception)
+ Chef::Config[:exception_handlers] ||= []
+ Chef::Config[:exception_handlers] |= [self]
+ end
+ end
+
# The list of currently configured start handlers
def self.start_handlers
Array(Chef::Config[:start_handlers])
end
+ def self.resolve_handler_instance(handler)
+ if handler.is_a?(Class)
+ if handler.respond_to?(:instance)
+ # support retrieving a Singleton reporting object
+ handler.instance
+ else
+ # just a class with no way to insert data
+ handler.new
+ end
+ else
+ # the Chef::Config array contains an instance, not a class
+ handler
+ end
+ end
+
# Run the start handlers. This will usually be called by a notification
# from Chef::Client
def self.run_start_handlers(run_status)
Chef::Log.info("Running start handlers")
start_handlers.each do |handler|
+ handler = resolve_handler_instance(handler)
handler.run_report_safely(run_status)
end
Chef::Log.info("Start handlers complete.")
@@ -90,6 +119,7 @@ class Chef
events.handlers_start(report_handlers.size)
Chef::Log.info("Running report handlers")
report_handlers.each do |handler|
+ handler = resolve_handler_instance(handler)
handler.run_report_safely(run_status)
events.handler_executed(handler)
end
@@ -115,6 +145,7 @@ class Chef
events.handlers_start(exception_handlers.size)
Chef::Log.error("Running exception handlers")
exception_handlers.each do |handler|
+ handler = resolve_handler_instance(handler)
handler.run_report_safely(run_status)
events.handler_executed(handler)
end
diff --git a/lib/chef/handler/json_file.rb b/lib/chef/handler/json_file.rb
index 9ba3d70383..7f29b91449 100644
--- a/lib/chef/handler/json_file.rb
+++ b/lib/chef/handler/json_file.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright 2010-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,7 +28,6 @@ class Chef
def initialize(config = {})
@config = config
@config[:path] ||= "/var/chef/reports"
- @config
end
def report
diff --git a/lib/chef/http.rb b/lib/chef/http.rb
index c6afa97d23..016e81d12c 100644
--- a/lib/chef/http.rb
+++ b/lib/chef/http.rb
@@ -5,7 +5,7 @@
# Author:: Christopher Brown (<cb@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2009-2016, 2013-2015 Chef Software, Inc.
+# Copyright:: Copyright 2009-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -54,7 +54,7 @@ class Chef
# stream handlers handle responses so must be applied in reverse order
# (same as #apply_stream_complete_middleware or #apply_response_midddleware)
@stream_handlers.reverse.inject(next_chunk) do |chunk, handler|
- Chef::Log.debug("Chef::HTTP::StreamHandler calling #{handler.class}#handle_chunk")
+ Chef::Log.trace("Chef::HTTP::StreamHandler calling #{handler.class}#handle_chunk")
handler.handle_chunk(chunk)
end
end
@@ -77,6 +77,9 @@ class Chef
attr_reader :middlewares
+ # [Boolean] if we're doing keepalives or not
+ attr_reader :keepalives
+
# Create a HTTP client object. The supplied +url+ is used as the base for
# all subsequent requests. For example, when initialized with a base url
# http://localhost:4000, a call to +get+ with 'nodes' will make an
@@ -87,6 +90,7 @@ class Chef
@sign_on_redirect = true
@redirects_followed = 0
@redirect_limit = 10
+ @keepalives = options[:keepalives] || false
@options = options
@middlewares = []
@@ -138,13 +142,25 @@ class Chef
# Makes an HTTP request to +path+ with the given +method+, +headers+, and
# +data+ (if applicable).
def request(method, path, headers = {}, data = false)
+ http_attempts ||= 0
url = create_url(path)
- method, url, headers, data = apply_request_middleware(method, url, headers, data)
+ processed_method, url, processed_headers, processed_data = apply_request_middleware(method, url, headers, data)
- response, rest_request, return_value = send_http_request(method, url, headers, data)
+ response, rest_request, return_value = send_http_request(processed_method, url, processed_headers, processed_data)
response, rest_request, return_value = apply_response_middleware(response, rest_request, return_value)
+
response.error! unless success_response?(response)
return_value
+
+ rescue Net::HTTPServerException => e
+ http_attempts += 1
+ response = e.response
+ if response.kind_of?(Net::HTTPNotAcceptable) && version_retries - http_attempts > 0
+ Chef::Log.trace("Negotiating protocol version with #{url}, retry #{http_attempts}/#{version_retries}")
+ retry
+ else
+ raise
+ end
rescue Exception => exception
log_failed_request(response, return_value) unless response.nil?
@@ -154,17 +170,18 @@ class Chef
raise
end
- def streaming_request_with_progress(path, headers = {}, &progress_block)
+ def streaming_request_with_progress(path, headers = {}, tempfile = nil, &progress_block)
+ http_attempts ||= 0
url = create_url(path)
response, rest_request, return_value = nil, nil, nil
- tempfile = nil
+ data = nil
method = :GET
- method, url, headers, data = apply_request_middleware(method, url, headers, data)
+ method, url, processed_headers, data = apply_request_middleware(method, url, headers, data)
- response, rest_request, return_value = send_http_request(method, url, headers, data) do |http_response|
+ response, rest_request, return_value = send_http_request(method, url, processed_headers, data) do |http_response|
if http_response.kind_of?(Net::HTTPSuccess)
- tempfile = stream_to_tempfile(url, http_response, &progress_block)
+ tempfile = stream_to_tempfile(url, http_response, tempfile, &progress_block)
end
apply_stream_complete_middleware(http_response, rest_request, return_value)
end
@@ -173,6 +190,15 @@ class Chef
response.error!
end
tempfile
+ rescue Net::HTTPServerException => e
+ http_attempts += 1
+ response = e.response
+ if response.kind_of?(Net::HTTPNotAcceptable) && version_retries - http_attempts > 0
+ Chef::Log.trace("Negotiating protocol version with #{url}, retry #{http_attempts}/#{version_retries}")
+ retry
+ else
+ raise
+ end
rescue Exception => e
log_failed_request(response, return_value) unless response.nil?
if e.respond_to?(:chef_rest_request=)
@@ -190,17 +216,18 @@ class Chef
#
# @yield [tempfile] block to process the tempfile
# @yieldparams [tempfile<Tempfile>] tempfile
- def streaming_request(path, headers = {})
+ def streaming_request(path, headers = {}, tempfile = nil)
+ http_attempts ||= 0
url = create_url(path)
response, rest_request, return_value = nil, nil, nil
- tempfile = nil
+ data = nil
method = :GET
- method, url, headers, data = apply_request_middleware(method, url, headers, data)
+ method, url, processed_headers, data = apply_request_middleware(method, url, headers, data)
- response, rest_request, return_value = send_http_request(method, url, headers, data) do |http_response|
+ response, rest_request, return_value = send_http_request(method, url, processed_headers, data) do |http_response|
if http_response.kind_of?(Net::HTTPSuccess)
- tempfile = stream_to_tempfile(url, http_response)
+ tempfile = stream_to_tempfile(url, http_response, tempfile)
end
apply_stream_complete_middleware(http_response, rest_request, return_value)
end
@@ -218,6 +245,15 @@ class Chef
end
end
tempfile
+ rescue Net::HTTPServerException => e
+ http_attempts += 1
+ response = e.response
+ if response.kind_of?(Net::HTTPNotAcceptable) && version_retries - http_attempts > 0
+ Chef::Log.trace("Negotiating protocol version with #{url}, retry #{http_attempts}/#{version_retries}")
+ retry
+ else
+ raise
+ end
rescue Exception => e
log_failed_request(response, return_value) unless response.nil?
if e.respond_to?(:chef_rest_request=)
@@ -228,23 +264,49 @@ class Chef
def http_client(base_url = nil)
base_url ||= url
+ if keepalives && !base_url.nil?
+ # only reuse the http_client if we want keepalives and have a base_url
+ @http_client ||= {}
+ # the per-host per-port cache here gets peristent connections correct when
+ # redirecting to different servers
+ if base_url.is_a?(String) # sigh, this kind of abuse can't happen with strongly typed languages
+ @http_client[base_url] ||= build_http_client(base_url)
+ else
+ @http_client[base_url.host] ||= {}
+ @http_client[base_url.host][base_url.port] ||= build_http_client(base_url)
+ end
+ else
+ build_http_client(base_url)
+ end
+ end
+
+ # DEPRECATED: This is only kept around to provide access to cache control data in
+ # lib/chef/provider/remote_file/http.rb
+ # FIXME: Find a better API.
+ def last_response
+ @last_response
+ end
+
+ private
+
+ # @api private
+ def build_http_client(base_url)
if chef_zero_uri?(base_url)
# PERFORMANCE CRITICAL: *MUST* lazy require here otherwise we load up webrick
# via chef-zero and that hits DNS (at *require* time) which may timeout,
# when for most knife/chef-client work we never need/want this loaded.
- Thread.exclusive {
- unless defined?(SocketlessChefZeroClient)
- require "chef/http/socketless_chef_zero_client"
- end
- }
+
+ unless defined?(SocketlessChefZeroClient)
+ require "chef/http/socketless_chef_zero_client"
+ end
+
SocketlessChefZeroClient.new(base_url)
else
- BasicClient.new(base_url, :ssl_policy => Chef::HTTP::APISSLPolicy)
+ BasicClient.new(base_url, ssl_policy: Chef::HTTP::APISSLPolicy, keepalives: keepalives)
end
end
- protected
-
+ # @api private
def create_url(path)
return path if path.is_a?(URI)
if path =~ /^(http|https|chefzero):\/\//i
@@ -259,27 +321,31 @@ class Chef
end
end
+ # @api private
def apply_request_middleware(method, url, headers, data)
middlewares.inject([method, url, headers, data]) do |req_data, middleware|
- Chef::Log.debug("Chef::HTTP calling #{middleware.class}#handle_request")
+ Chef::Log.trace("Chef::HTTP calling #{middleware.class}#handle_request")
middleware.handle_request(*req_data)
end
end
+ # @api private
def apply_response_middleware(response, rest_request, return_value)
middlewares.reverse.inject([response, rest_request, return_value]) do |res_data, middleware|
- Chef::Log.debug("Chef::HTTP calling #{middleware.class}#handle_response")
+ Chef::Log.trace("Chef::HTTP calling #{middleware.class}#handle_response")
middleware.handle_response(*res_data)
end
end
+ # @api private
def apply_stream_complete_middleware(response, rest_request, return_value)
middlewares.reverse.inject([response, rest_request, return_value]) do |res_data, middleware|
- Chef::Log.debug("Chef::HTTP calling #{middleware.class}#handle_stream_complete")
+ Chef::Log.trace("Chef::HTTP calling #{middleware.class}#handle_stream_complete")
middleware.handle_stream_complete(*res_data)
end
end
+ # @api private
def log_failed_request(response, return_value)
return_value ||= {}
error_message = "HTTP Request Returned #{response.code} #{response.message}: "
@@ -287,16 +353,17 @@ class Chef
Chef::Log.info(error_message)
end
+ # @api private
def success_response?(response)
response.kind_of?(Net::HTTPSuccess) || response.kind_of?(Net::HTTPRedirection)
end
# Runs a synchronous HTTP request, with no middleware applied (use #request
# to have the middleware applied). The entire response will be loaded into memory.
- def send_http_request(method, url, headers, body, &response_handler)
- headers = build_headers(method, url, headers, body)
-
+ # @api private
+ def send_http_request(method, url, base_headers, body, &response_handler)
retrying_http_errors(url) do
+ headers = build_headers(method, url, base_headers, body)
client = http_client(url)
return_value = nil
if block_given?
@@ -314,7 +381,14 @@ class Chef
elsif redirect_location = redirected_to(response)
if [:GET, :HEAD].include?(method)
follow_redirect do
- send_http_request(method, url + redirect_location, headers, body, &response_handler)
+ redirected_url = url + redirect_location
+ if http_disable_auth_on_redirect
+ new_headers = build_headers(method, redirected_url, headers, body)
+ new_headers.delete("Authorization") if url.host != redirected_url.host
+ send_http_request(method, redirected_url, new_headers, body, &response_handler)
+ else
+ send_http_request(method, redirected_url, headers, body, &response_handler)
+ end
end
else
raise Exceptions::InvalidRedirect, "#{method} request was redirected from #{url} to #{redirect_location}. Only GET and HEAD support redirects."
@@ -328,6 +402,7 @@ class Chef
# Wraps an HTTP request with retry logic.
# === Arguments
# url:: URL of the request, used for error messages
+ # @api private
def retrying_http_errors(url)
http_attempts = 0
begin
@@ -377,35 +452,48 @@ class Chef
end
end
+ def version_retries
+ @version_retries ||= options[:version_class].possible_requests
+ end
+
+ # @api private
def http_retry_delay
config[:http_retry_delay]
end
+ # @api private
def http_retry_count
config[:http_retry_count]
end
+ # @api private
+ def http_disable_auth_on_redirect
+ config[:http_disable_auth_on_redirect]
+ end
+
+ # @api private
def config
Chef::Config
end
+ # @api private
def follow_redirect
raise Chef::Exceptions::RedirectLimitExceeded if @redirects_followed >= redirect_limit
@redirects_followed += 1
- Chef::Log.debug("Following redirect #{@redirects_followed}/#{redirect_limit}")
+ Chef::Log.trace("Following redirect #{@redirects_followed}/#{redirect_limit}")
yield
ensure
@redirects_followed = 0
end
- private
-
+ # @api private
def chef_zero_uri?(uri)
uri = URI.parse(uri) unless uri.respond_to?(:scheme)
uri.scheme == "chefzero"
end
+ # @api private
def redirected_to(response)
return nil unless response.kind_of?(Net::HTTPRedirection)
# Net::HTTPNotModified is undesired subclass of Net::HTTPRedirection so test for this
@@ -413,6 +501,7 @@ class Chef
response["location"]
end
+ # @api private
def build_headers(method, url, headers = {}, json_body = false)
headers = @default_headers.merge(headers)
headers["Content-Length"] = json_body.bytesize.to_s if json_body
@@ -420,13 +509,16 @@ class Chef
headers
end
- def stream_to_tempfile(url, response, &progress_block)
+ # @api private
+ def stream_to_tempfile(url, response, tf = nil, &progress_block)
content_length = response["Content-Length"]
- tf = Tempfile.open("chef-rest")
- if Chef::Platform.windows?
- tf.binmode # required for binary files on Windows platforms
+ if tf.nil?
+ tf = Tempfile.open("chef-rest")
+ if Chef::Platform.windows?
+ tf.binmode # required for binary files on Windows platforms
+ end
end
- Chef::Log.debug("Streaming download from #{url} to tempfile #{tf.path}")
+ Chef::Log.trace("Streaming download from #{url} to tempfile #{tf.path}")
# Stolen from http://www.ruby-forum.com/topic/166423
# Kudos to _why!
@@ -443,18 +535,5 @@ class Chef
raise
end
- public
-
- ############################################################################
- # DEPRECATED
- ############################################################################
-
- # This is only kept around to provide access to cache control data in
- # lib/chef/provider/remote_file/http.rb
- # Find a better API.
- def last_response
- @last_response
- end
-
end
end
diff --git a/lib/chef/http/api_versions.rb b/lib/chef/http/api_versions.rb
new file mode 100644
index 0000000000..6c5ede40aa
--- /dev/null
+++ b/lib/chef/http/api_versions.rb
@@ -0,0 +1,56 @@
+#--
+# Copyright:: Copyright 2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/server_api_versions"
+require "chef/json_compat"
+
+class Chef
+ class HTTP
+ # An HTTP middleware to retrieve and store the Chef Server's minimum
+ # and maximum supported API versions.
+ class APIVersions
+
+ def initialize(options = {})
+ end
+
+ def handle_request(method, url, headers = {}, data = false)
+ [method, url, headers, data]
+ end
+
+ def handle_response(http_response, rest_request, return_value)
+ if http_response.code == "406"
+ ServerAPIVersions.instance.reset!
+ end
+ if http_response.key?("x-ops-server-api-version")
+ ServerAPIVersions.instance.set_versions(JSONCompat.parse(http_response["x-ops-server-api-version"]))
+ else
+ ServerAPIVersions.instance.unversioned!
+ end
+ [http_response, rest_request, return_value]
+ end
+
+ def stream_response_handler(response)
+ nil
+ end
+
+ def handle_stream_complete(http_response, rest_request, return_value)
+ [http_response, rest_request, return_value]
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/http/auth_credentials.rb b/lib/chef/http/auth_credentials.rb
index d5dbff3758..e2494c9405 100644
--- a/lib/chef/http/auth_credentials.rb
+++ b/lib/chef/http/auth_credentials.rb
@@ -38,7 +38,7 @@ class Chef
def signature_headers(request_params = {})
raise ArgumentError, "Cannot sign the request without a client name, check that :node_name is assigned" if client_name.nil?
- Chef::Log.debug("Signing the request as #{client_name}")
+ Chef::Log.trace("Signing the request as #{client_name}")
# params_in = {:http_method => :GET, :path => "/clients", :body => "", :host => "localhost"}
request_params = request_params.dup
@@ -49,7 +49,7 @@ class Chef
sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object(request_params)
signed = sign_obj.sign(key).merge({ :host => host })
- signed.inject({}) { |memo, kv| memo["#{kv[0].to_s.upcase}"] = kv[1];memo }
+ signed.inject({}) { |memo, kv| memo["#{kv[0].to_s.upcase}"] = kv[1]; memo }
end
end
diff --git a/lib/chef/http/authenticator.rb b/lib/chef/http/authenticator.rb
index 84065bf816..65367af107 100644
--- a/lib/chef/http/authenticator.rb
+++ b/lib/chef/http/authenticator.rb
@@ -30,6 +30,8 @@ class Chef
attr_reader :raw_key
attr_reader :attr_names
attr_reader :auth_credentials
+ attr_reader :version_class
+ attr_reader :api_version
attr_accessor :sign_request
@@ -39,15 +41,12 @@ class Chef
@signing_key_filename = opts[:signing_key_filename]
@key = load_signing_key(opts[:signing_key_filename], opts[:raw_key])
@auth_credentials = AuthCredentials.new(opts[:client_name], @key)
- if opts[:api_version]
- @api_version = opts[:api_version]
- else
- @api_version = DEFAULT_SERVER_API_VERSION
- end
+ @version_class = opts[:version_class]
+ @api_version = opts[:api_version]
end
def handle_request(method, url, headers = {}, data = false)
- headers["X-Ops-Server-API-Version"] = @api_version
+ headers["X-Ops-Server-API-Version"] = request_version
headers.merge!(authentication_headers(method, url, data, headers)) if sign_requests?
[method, url, headers, data]
end
@@ -64,6 +63,16 @@ class Chef
[http_response, rest_request, return_value]
end
+ def request_version
+ if version_class
+ version_class.best_request_version
+ elsif api_version
+ api_version
+ else
+ DEFAULT_SERVER_API_VERSION
+ end
+ end
+
def sign_requests?
auth_credentials.sign_requests? && @sign_request
end
diff --git a/lib/chef/http/basic_client.rb b/lib/chef/http/basic_client.rb
index 3a87fe85e4..54da722b3d 100644
--- a/lib/chef/http/basic_client.rb
+++ b/lib/chef/http/basic_client.rb
@@ -5,7 +5,7 @@
# Author:: Christopher Brown (<cb@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,8 +32,8 @@ class Chef
HTTPS = "https".freeze
attr_reader :url
- attr_reader :http_client
attr_reader :ssl_policy
+ attr_reader :keepalives
# Instantiate a BasicClient.
# === Arguments:
@@ -43,7 +43,11 @@ class Chef
def initialize(url, opts = {})
@url = url
@ssl_policy = opts[:ssl_policy] || DefaultSSLPolicy
- @http_client = build_http_client
+ @keepalives = opts[:keepalives] || false
+ end
+
+ def http_client
+ @http_client ||= build_http_client
end
def host
@@ -56,32 +60,32 @@ class Chef
def request(method, url, req_body, base_headers = {})
http_request = HTTPRequest.new(method, url, req_body, base_headers).http_request
- Chef::Log.debug("Initiating #{method} to #{url}")
- Chef::Log.debug("---- HTTP Request Header Data: ----")
+ Chef::Log.trace("Initiating #{method} to #{url}")
+ Chef::Log.trace("---- HTTP Request Header Data: ----")
base_headers.each do |name, value|
- Chef::Log.debug("#{name}: #{value}")
+ Chef::Log.trace("#{name}: #{value}")
end
- Chef::Log.debug("---- End HTTP Request Header Data ----")
+ Chef::Log.trace("---- End HTTP Request Header Data ----")
http_client.request(http_request) do |response|
- Chef::Log.debug("---- HTTP Status and Header Data: ----")
- Chef::Log.debug("HTTP #{response.http_version} #{response.code} #{response.msg}")
+ Chef::Log.trace("---- HTTP Status and Header Data: ----")
+ Chef::Log.trace("HTTP #{response.http_version} #{response.code} #{response.msg}")
response.each do |header, value|
- Chef::Log.debug("#{header}: #{value}")
+ Chef::Log.trace("#{header}: #{value}")
end
- Chef::Log.debug("---- End HTTP Status/Header Data ----")
+ Chef::Log.trace("---- End HTTP Status/Header Data ----")
# For non-400's, log the request and response bodies
if !response.code || !response.code.start_with?("2")
if response.body
- Chef::Log.debug("---- HTTP Response Body ----")
- Chef::Log.debug(response.body)
- Chef::Log.debug("---- End HTTP Response Body -----")
+ Chef::Log.trace("---- HTTP Response Body ----")
+ Chef::Log.trace(response.body)
+ Chef::Log.trace("---- End HTTP Response Body -----")
end
if req_body
- Chef::Log.debug("---- HTTP Request Body ----")
- Chef::Log.debug(req_body)
- Chef::Log.debug("---- End HTTP Request Body ----")
+ Chef::Log.trace("---- HTTP Request Body ----")
+ Chef::Log.trace(req_body)
+ Chef::Log.trace("---- End HTTP Request Body ----")
end
end
@@ -106,7 +110,7 @@ class Chef
# match no_proxy with a fuzzy matcher, rather than letting Net::HTTP
# do it.
http_client = http_client_builder.new(host, port, nil)
- http_client.proxy_port = nil if http_client.proxy_address == nil
+ http_client.proxy_port = nil if http_client.proxy_address.nil?
if url.scheme == HTTPS
configure_ssl(http_client)
@@ -114,7 +118,11 @@ class Chef
http_client.read_timeout = config[:rest_timeout]
http_client.open_timeout = config[:rest_timeout]
- http_client
+ if keepalives
+ http_client.start
+ else
+ http_client
+ end
end
def config
@@ -125,7 +133,7 @@ class Chef
if proxy_uri.nil?
Net::HTTP
else
- Chef::Log.debug("Using #{proxy_uri.host}:#{proxy_uri.port} for proxy")
+ Chef::Log.trace("Using #{proxy_uri.host}:#{proxy_uri.port} for proxy")
Net::HTTP.Proxy(proxy_uri.host, proxy_uri.port, http_proxy_user(proxy_uri),
http_proxy_pass(proxy_uri))
end
diff --git a/lib/chef/http/decompressor.rb b/lib/chef/http/decompressor.rb
index 1bba4cc492..cf4a4bc593 100644
--- a/lib/chef/http/decompressor.rb
+++ b/lib/chef/http/decompressor.rb
@@ -79,10 +79,10 @@ class Chef
else
case response[CONTENT_ENCODING]
when GZIP
- Chef::Log.debug "Decompressing gzip response"
+ Chef::Log.trace "Decompressing gzip response"
Zlib::Inflate.new(Zlib::MAX_WBITS + 16).inflate(response.body)
when DEFLATE
- Chef::Log.debug "Decompressing deflate response"
+ Chef::Log.trace "Decompressing deflate response"
Zlib::Inflate.inflate(response.body)
else
response.body
@@ -94,20 +94,20 @@ class Chef
# object you can use to unzip/inflate a streaming response.
def stream_response_handler(response)
if gzip_disabled?
- Chef::Log.debug "disable_gzip is set. \
+ Chef::Log.trace "disable_gzip is set. \
Not using #{response[CONTENT_ENCODING]} \
and initializing noop stream deflator."
NoopInflater.new
else
case response[CONTENT_ENCODING]
when GZIP
- Chef::Log.debug "Initializing gzip stream deflator"
+ Chef::Log.trace "Initializing gzip stream deflator"
GzipInflater.new
when DEFLATE
- Chef::Log.debug "Initializing deflate stream deflator"
+ Chef::Log.trace "Initializing deflate stream deflator"
DeflateInflater.new
else
- Chef::Log.debug "content_encoding = '#{response[CONTENT_ENCODING]}' \
+ Chef::Log.trace "content_encoding = '#{response[CONTENT_ENCODING]}' \
initializing noop stream deflator."
NoopInflater.new
end
diff --git a/lib/chef/http/http_request.rb b/lib/chef/http/http_request.rb
index 20c46aaa8d..de589e429e 100644
--- a/lib/chef/http/http_request.rb
+++ b/lib/chef/http/http_request.rb
@@ -5,7 +5,7 @@
# Author:: Christopher Brown (<cb@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2009-2016, 2010-2016 Chef Software, Inc.
+# Copyright:: Copyright 2009-2016, 2010-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -49,6 +49,7 @@ class Chef
ENCODING_GZIP_DEFLATE = "gzip;q=1.0,deflate;q=0.6,identity;q=0.3".freeze
GET = "get".freeze
+ PATCH = "patch".freeze
PUT = "put".freeze
POST = "post".freeze
DELETE = "delete".freeze
@@ -70,7 +71,7 @@ class Chef
@user_agent ||= DEFAULT_UA
end
- attr_reader :method, :url, :headers, :http_client, :http_request
+ attr_reader :method, :url, :headers, :http_request
def initialize(method, url, req_body, base_headers = {})
@method, @url = method, url
@@ -126,9 +127,9 @@ class Chef
# http://redmine.ruby-lang.org/issues/show/2708
# http://redmine.ruby-lang.org/issues/show/2758
if e.to_s =~ /#{Regexp.escape(%q{undefined method `closed?' for nil:NilClass})}/
- Chef::Log.debug("Rescued error in http connect, re-raising as Errno::ECONNREFUSED to hide bug in net/http")
- Chef::Log.debug("#{e.class.name}: #{e}")
- Chef::Log.debug(e.backtrace.join("\n"))
+ Chef::Log.trace("Rescued error in http connect, re-raising as Errno::ECONNREFUSED to hide bug in net/http")
+ Chef::Log.trace("#{e.class.name}: #{e}")
+ Chef::Log.trace(e.backtrace.join("\n"))
raise Errno::ECONNREFUSED, "Connection refused attempting to contact #{url.scheme}://#{host}:#{port}"
else
raise
@@ -161,6 +162,8 @@ class Chef
Net::HTTP::Post.new(req_path, headers)
when PUT
Net::HTTP::Put.new(req_path, headers)
+ when PATCH
+ Net::HTTP::Patch.new(req_path, headers)
when DELETE
Net::HTTP::Delete.new(req_path, headers)
when HEAD
diff --git a/lib/chef/http/json_input.rb b/lib/chef/http/json_input.rb
index 4cc1aa2e10..7cf2e4012f 100644
--- a/lib/chef/http/json_input.rb
+++ b/lib/chef/http/json_input.rb
@@ -33,7 +33,7 @@ class Chef
def handle_request(method, url, headers = {}, data = false)
if data && should_encode_as_json?(headers)
- headers.delete_if { |key, _value| key.casecmp("content-type").zero? }
+ headers.delete_if { |key, _value| key.casecmp("content-type") == 0 }
headers["Content-Type"] = "application/json"
json_opts = {}
json_opts[:validate_utf8] = opts[:validate_utf8] if opts.has_key?(:validate_utf8)
@@ -64,7 +64,7 @@ class Chef
# ruby/Net::HTTP don't enforce capitalized headers (it normalizes them
# for you before sending the request), so we have to account for all
# the variations we might find
- requested_content_type = headers.find { |k, v| k.casecmp("content-type").zero? }
+ requested_content_type = headers.find { |k, v| k.casecmp("content-type") == 0 }
requested_content_type.nil? || requested_content_type.last.include?("json")
end
diff --git a/lib/chef/http/json_output.rb b/lib/chef/http/json_output.rb
index 6053c38a56..25f1380ceb 100644
--- a/lib/chef/http/json_output.rb
+++ b/lib/chef/http/json_output.rb
@@ -1,7 +1,7 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright 2013-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -60,7 +60,10 @@ class Chef
end
[http_response, rest_request, return_value]
else
- Chef::Log.debug("Expected JSON response, but got content-type '#{http_response['content-type']}'")
+ Chef::Log.trace("Expected JSON response, but got content-type '#{http_response['content-type']}'")
+ if http_response.body
+ Chef::Log.trace("Response body contains:\n#{http_response.body.length < 256 ? http_response.body : http_response.body[0..256] + " [...truncated...]"}")
+ end
return [http_response, rest_request, http_response.body.to_s]
end
end
diff --git a/lib/chef/http/socketless_chef_zero_client.rb b/lib/chef/http/socketless_chef_zero_client.rb
index 1acac5e758..34a5dbb083 100644
--- a/lib/chef/http/socketless_chef_zero_client.rb
+++ b/lib/chef/http/socketless_chef_zero_client.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2015-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -133,7 +133,7 @@ class Chef
511 => "Network Authentication Required",
}
- STATUS_MESSAGE.values.each { |v| v.freeze }
+ STATUS_MESSAGE.each_value { |v| v.freeze }
STATUS_MESSAGE.freeze
def initialize(base_url)
@@ -170,6 +170,7 @@ class Chef
"QUERY_STRING" => url.query,
"SERVER_PORT" => url.port,
"HTTP_HOST" => "localhost:#{url.port}",
+ "HTTP_X_OPS_SERVER_API_VERSION" => headers["X-Ops-Server-API-Version"],
"rack.url_scheme" => "chefzero",
"rack.input" => StringIO.new(body_str),
}
@@ -197,9 +198,9 @@ class Chef
private
def headers_extracted_from_options
- options.reject { |name, _| KNOWN_OPTIONS.include?(name) }.map { |name, value|
+ options.reject { |name, _| KNOWN_OPTIONS.include?(name) }.map do |name, value|
[name.to_s.split("_").map { |segment| segment.capitalize }.join("-"), value]
- }
+ end
end
end
diff --git a/lib/chef/http/validate_content_length.rb b/lib/chef/http/validate_content_length.rb
index c1073867d3..c8e8ac53ad 100644
--- a/lib/chef/http/validate_content_length.rb
+++ b/lib/chef/http/validate_content_length.rb
@@ -50,12 +50,12 @@ class Chef
def handle_response(http_response, rest_request, return_value)
validate(http_response, http_response.body.bytesize) if http_response && http_response.body
- return [http_response, rest_request, return_value]
+ [http_response, rest_request, return_value]
end
def handle_stream_complete(http_response, rest_request, return_value)
if @content_length_counter.nil?
- Chef::Log.debug("No content-length information collected for the streamed download, cannot identify streamed download.")
+ Chef::Log.trace("No content-length information collected for the streamed download, cannot identify streamed download.")
else
validate(http_response, @content_length_counter.content_length)
end
@@ -63,7 +63,7 @@ class Chef
# Make sure the counter is reset since this object might get used
# again. See CHEF-5100
@content_length_counter = nil
- return [http_response, rest_request, return_value]
+ [http_response, rest_request, return_value]
end
def stream_response_handler(response)
@@ -86,19 +86,19 @@ class Chef
transfer_encoding = http_response["transfer-encoding"]
if content_length.nil?
- Chef::Log.debug "HTTP server did not include a Content-Length header in response, cannot identify truncated downloads."
+ Chef::Log.trace "HTTP server did not include a Content-Length header in response, cannot identify truncated downloads."
return true
end
if content_length < 0
- Chef::Log.debug "HTTP server responded with a negative Content-Length header (#{content_length}), cannot identify truncated downloads."
+ Chef::Log.trace "HTTP server responded with a negative Content-Length header (#{content_length}), cannot identify truncated downloads."
return true
end
# if Transfer-Encoding is set the RFC states that we must ignore the Content-Length field
# CHEF-5041: some proxies uncompress gzip content, leave the incorrect content-length, but set the transfer-encoding field
unless transfer_encoding.nil?
- Chef::Log.debug "Transfer-Encoding header is set, skipping Content-Length check."
+ Chef::Log.trace "Transfer-Encoding header is set, skipping Content-Length check."
return true
end
@@ -106,7 +106,7 @@ class Chef
raise Chef::Exceptions::ContentLengthMismatch.new(response_length, content_length)
end
- Chef::Log.debug "Content-Length validated correctly."
+ Chef::Log.trace "Content-Length validated correctly."
true
end
end
diff --git a/lib/chef/json_compat.rb b/lib/chef/json_compat.rb
index f8f05a0074..b819d5e57d 100644
--- a/lib/chef/json_compat.rb
+++ b/lib/chef/json_compat.rb
@@ -26,32 +26,13 @@ class Chef
class JSONCompat
JSON_MAX_NESTING = 1000
- JSON_CLASS = "json_class".freeze
-
- CHEF_APICLIENT = "Chef::ApiClient".freeze
- CHEF_CHECKSUM = "Chef::Checksum".freeze
- CHEF_COOKBOOKVERSION = "Chef::CookbookVersion".freeze
- CHEF_DATABAG = "Chef::DataBag".freeze
- CHEF_DATABAGITEM = "Chef::DataBagItem".freeze
- CHEF_ENVIRONMENT = "Chef::Environment".freeze
- CHEF_NODE = "Chef::Node".freeze
- CHEF_ROLE = "Chef::Role".freeze
- CHEF_SANDBOX = "Chef::Sandbox".freeze
- CHEF_RESOURCE = "Chef::Resource".freeze
- CHEF_RESOURCECOLLECTION = "Chef::ResourceCollection".freeze
- CHEF_RESOURCESET = "Chef::ResourceCollection::ResourceSet".freeze
- CHEF_RESOURCELIST = "Chef::ResourceCollection::ResourceList".freeze
- CHEF_RUNLISTEXPANSION = "Chef::RunListExpansion".freeze
-
class <<self
# API to use to avoid create_addtions
def parse(source, opts = {})
- begin
- FFI_Yajl::Parser.parse(source, opts)
- rescue FFI_Yajl::ParseError => e
- raise Chef::Exceptions::JSON::ParseError, e.message
- end
+ FFI_Yajl::Parser.parse(source, opts)
+ rescue FFI_Yajl::ParseError => e
+ raise Chef::Exceptions::JSON::ParseError, e.message
end
# Just call the JSON gem's parse method with a modified :max_nesting field
@@ -65,48 +46,13 @@ class Chef
raise Chef::Exceptions::JSON::ParseError, "Top level JSON object must be a Hash or Array. (actual: #{obj.class})"
end
- # The old default in the json gem (which we are mimicing because we
- # sadly rely on this misfeature) is to "create additions" i.e., convert
- # JSON objects into ruby objects. Explicit :create_additions => false
- # is required to turn it off.
- if opts[:create_additions].nil? || opts[:create_additions]
- map_to_rb_obj(obj)
- else
- obj
- end
- end
-
- # Look at an object that's a basic type (from json parse) and convert it
- # to an instance of Chef classes if desired.
- def map_to_rb_obj(json_obj)
- case json_obj
- when Hash
- mapped_hash = map_hash_to_rb_obj(json_obj)
- if json_obj.has_key?(JSON_CLASS) && (class_to_inflate = class_for_json_class(json_obj[JSON_CLASS]))
- class_to_inflate.json_create(mapped_hash)
- else
- mapped_hash
- end
- when Array
- json_obj.map { |e| map_to_rb_obj(e) }
- else
- json_obj
- end
- end
-
- def map_hash_to_rb_obj(json_hash)
- json_hash.each do |key, value|
- json_hash[key] = map_to_rb_obj(value)
- end
- json_hash
+ obj
end
def to_json(obj, opts = nil)
- begin
- FFI_Yajl::Encoder.encode(obj, opts)
- rescue FFI_Yajl::EncodeError => e
- raise Chef::Exceptions::JSON::EncodeError, e.message
- end
+ FFI_Yajl::Encoder.encode(obj, opts)
+ rescue FFI_Yajl::EncodeError => e
+ raise Chef::Exceptions::JSON::EncodeError, e.message
end
def to_json_pretty(obj, opts = nil)
@@ -117,48 +63,6 @@ class Chef
to_json(obj, options_map).chomp
end
- # Map +json_class+ to a Class object. We use a +case+ instead of a Hash
- # assigned to a constant because otherwise this file could not be loaded
- # until all the constants were defined, which means you'd have to load
- # the world to get json, which would make knife very slow.
- def class_for_json_class(json_class)
- case json_class
- when CHEF_APICLIENT
- Chef::ApiClient
- when CHEF_CHECKSUM
- Chef::Checksum
- when CHEF_COOKBOOKVERSION
- Chef::CookbookVersion
- when CHEF_DATABAG
- Chef::DataBag
- when CHEF_DATABAGITEM
- Chef::DataBagItem
- when CHEF_ENVIRONMENT
- Chef::Environment
- when CHEF_NODE
- Chef::Node
- when CHEF_ROLE
- Chef::Role
- when CHEF_SANDBOX
- # a falsey return here will disable object inflation/"create
- # additions" in the caller. In Chef 11 this is correct, we just have
- # a dummy Chef::Sandbox class for compat with Chef 10 servers.
- false
- when CHEF_RESOURCE
- Chef::Resource
- when CHEF_RESOURCECOLLECTION
- Chef::ResourceCollection
- when CHEF_RESOURCESET
- Chef::ResourceCollection::ResourceSet
- when CHEF_RESOURCELIST
- Chef::ResourceCollection::ResourceList
- when /^Chef::Resource/
- Chef::Resource.find_descendants_by_name(json_class)
- else
- raise Chef::Exceptions::JSON::ParseError, "Unsupported `json_class` type '#{json_class}'"
- end
- end
-
end
end
end
diff --git a/lib/chef/key.rb b/lib/chef/key.rb
index 1c25c5daad..bf8e533d62 100644
--- a/lib/chef/key.rb
+++ b/lib/chef/key.rb
@@ -155,7 +155,7 @@ class Chef
result = chef_rest.post("#{api_base}/#{@actor}/keys", payload)
# append the private key to the current key if the server returned one,
# since the POST endpoint just returns uri and private_key if needed.
- new_key = self.to_hash
+ new_key = to_hash
new_key["private_key"] = result["private_key"] if result["private_key"]
Chef::Key.from_hash(new_key)
end
@@ -178,9 +178,9 @@ class Chef
new_key = chef_rest.put("#{api_base}/#{@actor}/keys/#{put_name}", to_hash)
# if the server returned a public_key, remove the create_key field, as we now have a key
if new_key["public_key"]
- self.delete_create_key
+ delete_create_key
end
- Chef::Key.from_hash(self.to_hash.merge(new_key))
+ Chef::Key.from_hash(to_hash.merge(new_key))
end
def save
@@ -201,72 +201,66 @@ class Chef
chef_rest.delete("#{api_base}/#{@actor}/keys/#{@name}")
end
- # Class methods
- def self.from_hash(key_hash)
- if key_hash.has_key?("user")
- key = Chef::Key.new(key_hash["user"], "user")
- elsif key_hash.has_key?("client")
- key = Chef::Key.new(key_hash["client"], "client")
- else
- raise Chef::Exceptions::MissingKeyAttribute, "The hash passed to from_hash does not contain the key 'user' or 'client'. Please pass a hash that defines one of those keys."
+ class << self
+ def from_hash(key_hash)
+ if key_hash.has_key?("user")
+ key = Chef::Key.new(key_hash["user"], "user")
+ elsif key_hash.has_key?("client")
+ key = Chef::Key.new(key_hash["client"], "client")
+ else
+ raise Chef::Exceptions::MissingKeyAttribute, "The hash passed to from_hash does not contain the key 'user' or 'client'. Please pass a hash that defines one of those keys."
+ end
+ key.name key_hash["name"] if key_hash.key?("name")
+ key.public_key key_hash["public_key"] if key_hash.key?("public_key")
+ key.private_key key_hash["private_key"] if key_hash.key?("private_key")
+ key.create_key key_hash["create_key"] if key_hash.key?("create_key")
+ key.expiration_date key_hash["expiration_date"] if key_hash.key?("expiration_date")
+ key
end
- key.name key_hash["name"] if key_hash.key?("name")
- key.public_key key_hash["public_key"] if key_hash.key?("public_key")
- key.private_key key_hash["private_key"] if key_hash.key?("private_key")
- key.create_key key_hash["create_key"] if key_hash.key?("create_key")
- key.expiration_date key_hash["expiration_date"] if key_hash.key?("expiration_date")
- key
- end
-
- def self.from_json(json)
- Chef::Key.from_hash(Chef::JSONCompat.from_json(json))
- end
- def self.json_create(json)
- Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please use Chef::Key#from_json or one of the load_by methods.")
- Chef::Key.from_json(json)
- end
-
- def self.list_by_user(actor, inflate = false)
- keys = Chef::ServerAPI.new(Chef::Config[:chef_server_root]).get("users/#{actor}/keys")
- self.list(keys, actor, :load_by_user, inflate)
- end
+ def from_json(json)
+ Chef::Key.from_hash(Chef::JSONCompat.from_json(json))
+ end
- def self.list_by_client(actor, inflate = false)
- keys = Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("clients/#{actor}/keys")
- self.list(keys, actor, :load_by_client, inflate)
- end
+ def list_by_user(actor, inflate = false)
+ keys = Chef::ServerAPI.new(Chef::Config[:chef_server_root]).get("users/#{actor}/keys")
+ list(keys, actor, :load_by_user, inflate)
+ end
- def self.load_by_user(actor, key_name)
- response = Chef::ServerAPI.new(Chef::Config[:chef_server_root]).get("users/#{actor}/keys/#{key_name}")
- Chef::Key.from_hash(response.merge({ "user" => actor }))
- end
+ def list_by_client(actor, inflate = false)
+ keys = Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("clients/#{actor}/keys")
+ list(keys, actor, :load_by_client, inflate)
+ end
- def self.load_by_client(actor, key_name)
- response = Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("clients/#{actor}/keys/#{key_name}")
- Chef::Key.from_hash(response.merge({ "client" => actor }))
- end
+ def load_by_user(actor, key_name)
+ response = Chef::ServerAPI.new(Chef::Config[:chef_server_root]).get("users/#{actor}/keys/#{key_name}")
+ Chef::Key.from_hash(response.merge({ "user" => actor }))
+ end
- def self.generate_fingerprint(public_key)
- openssl_key_object = OpenSSL::PKey::RSA.new(public_key)
- data_string = OpenSSL::ASN1::Sequence([
- OpenSSL::ASN1::Integer.new(openssl_key_object.public_key.n),
- OpenSSL::ASN1::Integer.new(openssl_key_object.public_key.e),
- ])
- OpenSSL::Digest::SHA1.hexdigest(data_string.to_der).scan(/../).join(":")
- end
+ def load_by_client(actor, key_name)
+ response = Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("clients/#{actor}/keys/#{key_name}")
+ Chef::Key.from_hash(response.merge({ "client" => actor }))
+ end
- private
+ def generate_fingerprint(public_key)
+ openssl_key_object = OpenSSL::PKey::RSA.new(public_key)
+ data_string = OpenSSL::ASN1::Sequence([
+ OpenSSL::ASN1::Integer.new(openssl_key_object.public_key.n),
+ OpenSSL::ASN1::Integer.new(openssl_key_object.public_key.e),
+ ])
+ OpenSSL::Digest::SHA1.hexdigest(data_string.to_der).scan(/../).join(":")
+ end
- def self.list(keys, actor, load_method_symbol, inflate)
- if inflate
- keys.inject({}) do |key_map, result|
- name = result["name"]
- key_map[name] = Chef::Key.send(load_method_symbol, actor, name)
- key_map
+ def list(keys, actor, load_method_symbol, inflate)
+ if inflate
+ keys.inject({}) do |key_map, result|
+ name = result["name"]
+ key_map[name] = Chef::Key.send(load_method_symbol, actor, name)
+ key_map
+ end
+ else
+ keys
end
- else
- keys
end
end
end
diff --git a/lib/chef/knife.rb b/lib/chef/knife.rb
index 356bf47fd4..1e436f0fe6 100644
--- a/lib/chef/knife.rb
+++ b/lib/chef/knife.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Christopher Brown (<cb@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -42,9 +42,9 @@ class Chef
extend Chef::Mixin::ConvertToClassName
extend Forwardable
- # Backwards Compat:
- # Ideally, we should not vomit all of these methods into this base class;
- # instead, they should be accessed by hitting the ui object directly.
+ # @note Backwards Compat:
+ # Ideally, we should not vomit all of these methods into this base class;
+ # instead, they should be accessed by hitting the ui object directly.
def_delegator :@ui, :stdout
def_delegator :@ui, :stderr
def_delegator :@ui, :stdin
@@ -105,12 +105,11 @@ class Chef
# Explicitly set the category for the current command to +new_category+
# The category is normally determined from the first word of the command
# name, but some commands make more sense using two or more words
- # ===Arguments
- # new_category::: A String to set the category to (see examples)
- # ===Examples:
- # Data bag commands would be in the 'data' category by default. To put them
- # in the 'data bag' category:
- # category('data bag')
+ # @param new_category [String] value to set the category to (see examples)
+ #
+ # @example Data bag commands would be in the 'data' category by default. To
+ # put them in the 'data bag' category:
+ # category('data bag')
def self.category(new_category)
@category = new_category
end
@@ -145,7 +144,7 @@ class Chef
end
def self.subcommand_class_from(args)
- if args.size == 1 && args[0].strip.casecmp("rehash").zero?
+ if args.size == 1 && args[0].strip.casecmp("rehash") == 0
# To prevent issues with the rehash file not pointing to the correct plugins,
# we always use the glob loader when regenerating the rehash file
@subcommand_loader = Chef::Knife::SubcommandLoader.gem_glob_loader(chef_config_dir)
@@ -178,11 +177,12 @@ class Chef
@config_loader ||= WorkstationConfigLoader.new(nil, Chef::Log)
end
- def self.load_config(explicit_config_file)
+ def self.load_config(explicit_config_file, profile)
config_loader.explicit_config_file = explicit_config_file
+ config_loader.profile = profile
config_loader.load
- ui.warn("No knife configuration file found") if config_loader.no_config_found?
+ ui.warn("No knife configuration file found. See https://docs.chef.io/config_rb_knife.html for details.") if config_loader.no_config_found?
config_loader
rescue Exceptions::ConfigurationError => e
@@ -196,10 +196,11 @@ class Chef
# Run knife for the given +args+ (ARGV), adding +options+ to the list of
# CLI options that the subcommand knows how to handle.
- # ===Arguments
- # args::: usually ARGV
- # options::: A Mixlib::CLI option parser hash. These +options+ are how
- # subcommands know about global knife CLI options
+ #
+ # @param args [Array] The arguments. Usually ARGV
+ # @param options [Mixlib::CLI option parser hash] These +options+ are how
+ # subcommands know about global knife CLI options
+ #
def self.run(args, options = {})
# Fallback debug logging. Normally the logger isn't configured until we
# read the config, but this means any logging that happens before the
@@ -233,61 +234,64 @@ class Chef
end
end
- private
-
OFFICIAL_PLUGINS = %w{ec2 rackspace windows openstack terremark bluebox}
- def self.path_from_caller(caller_line)
- caller_line.split(/:\d+/).first
- end
+ class << self
+ def list_commands(preferred_category = nil)
+ category_desc = preferred_category ? preferred_category + " " : ""
+ msg "Available #{category_desc}subcommands: (for details, knife SUB-COMMAND --help)\n\n"
+ subcommand_loader.list_commands(preferred_category).sort.each do |category, commands|
+ next if category =~ /deprecated/i
+ msg "** #{category.upcase} COMMANDS **"
+ commands.sort.each do |command|
+ subcommand_loader.load_command(command)
+ msg subcommands[command].banner if subcommands[command]
+ end
+ msg
+ end
+ end
- # :nodoc:
- # Error out and print usage. probably because the arguments given by the
- # user could not be resolved to a subcommand.
- def self.subcommand_not_found!(args)
- ui.fatal("Cannot find subcommand for: '#{args.join(' ')}'")
+ private
- # Mention rehash when the subcommands cache(plugin_manifest.json) is used
- if subcommand_loader.is_a?(Chef::Knife::SubcommandLoader::HashedCommandLoader) ||
- subcommand_loader.is_a?(Chef::Knife::SubcommandLoader::CustomManifestLoader)
- ui.info("If this is a recently installed plugin, please run 'knife rehash' to update the subcommands cache.")
+ # @api private
+ def path_from_caller(caller_line)
+ caller_line.split(/:\d+/).first
end
- if category_commands = guess_category(args)
- list_commands(category_commands)
- elsif missing_plugin = ( OFFICIAL_PLUGINS.find { |plugin| plugin == args[0] } )
- ui.info("The #{missing_plugin} commands were moved to plugins in Chef 0.10")
- ui.info("You can install the plugin with `(sudo) gem install knife-#{missing_plugin}`")
- ui.info("Use `chef gem install knife-#{missing_plugin}` instead if using ChefDK")
- else
- list_commands
- end
+ # :nodoc:
+ # Error out and print usage. probably because the arguments given by the
+ # user could not be resolved to a subcommand.
+ # @api private
+ def subcommand_not_found!(args)
+ ui.fatal("Cannot find subcommand for: '#{args.join(' ')}'")
- exit 10
- end
+ # Mention rehash when the subcommands cache(plugin_manifest.json) is used
+ if subcommand_loader.is_a?(Chef::Knife::SubcommandLoader::HashedCommandLoader)
+ ui.info("If this is a recently installed plugin, please run 'knife rehash' to update the subcommands cache.")
+ end
- def self.list_commands(preferred_category = nil)
- category_desc = preferred_category ? preferred_category + " " : ""
- msg "Available #{category_desc}subcommands: (for details, knife SUB-COMMAND --help)\n\n"
- subcommand_loader.list_commands(preferred_category).sort.each do |category, commands|
- next if category =~ /deprecated/i
- msg "** #{category.upcase} COMMANDS **"
- commands.sort.each do |command|
- subcommand_loader.load_command(command)
- msg subcommands[command].banner if subcommands[command]
+ if category_commands = guess_category(args)
+ list_commands(category_commands)
+ elsif missing_plugin = ( OFFICIAL_PLUGINS.find { |plugin| plugin == args[0] } )
+ ui.info("The #{missing_plugin} commands were moved to plugins in Chef 0.10")
+ ui.info("You can install the plugin with `(sudo) gem install knife-#{missing_plugin}`")
+ ui.info("Use `chef gem install knife-#{missing_plugin}` instead if using ChefDK")
+ else
+ list_commands
end
- msg
+
+ exit 10
+ end
+
+ # @api private
+ def reset_config_path!
+ @@chef_config_dir = nil
end
- end
- def self.reset_config_path!
- @@chef_config_dir = nil
end
reset_config_path!
- public
-
# Create a new instance of the current class configured for the given
# arguments and options
def initialize(argv = [])
@@ -324,31 +328,35 @@ class Chef
exit(1)
end
- # Returns a subset of the Chef::Config[:knife] Hash that is relevant to the
- # currently executing knife command. This is used by #configure_chef to
- # apply settings from knife.rb to the +config+ hash.
+ # keys from mixlib-cli options
+ def cli_keys
+ self.class.options.keys
+ end
+
+ # extracts the settings from the Chef::Config[:knife] sub-hash that correspond
+ # to knife cli options -- in preparation for merging config values with cli values
+ #
+ # NOTE: due to weirdness in mixlib-config #has_key? is only true if the value has
+ # been set by the user -- the Chef::Config defaults return #has_key?() of false and
+ # this code DEPENDS on that functionality since applying the default values in
+ # Chef::Config[:knife] would break the defaults in the cli that we would otherwise
+ # overwrite.
def config_file_settings
- config_file_settings = {}
- self.class.options.keys.each do |key|
- config_file_settings[key] = Chef::Config[:knife][key] if Chef::Config[:knife].has_key?(key)
+ cli_keys.each_with_object({}) do |key, memo|
+ memo[key] = Chef::Config[:knife][key] if Chef::Config[:knife].has_key?(key)
end
- config_file_settings
end
- # Apply Config in this order:
- # defaults from mixlib-cli
- # settings from config file, via Chef::Config[:knife]
- # config from command line
+ # config is merged in this order (inverse of precedence)
+ # default_config - mixlib-cli defaults (accessor from the mixin)
+ # config_file_settings - Chef::Config[:knife] sub-hash
+ # config - mixlib-cli settings (accessor from the mixin)
def merge_configs
- # Apply config file settings on top of mixlib-cli defaults
- combined_config = default_config.merge(config_file_settings)
- # Apply user-supplied options on top of the above combination
- combined_config = combined_config.merge(config)
- # replace the config hash from mixlib-cli with our own.
- # Need to use the mutate-in-place #replace method instead of assigning to
- # the instance variable because other code may have a reference to the
- # original config hash object.
- config.replace(combined_config)
+ # other code may have a handle to the config object, so use Hash#replace to deliberately
+ # update-in-place.
+ config.replace(
+ default_config.merge(config_file_settings).merge(config)
+ )
end
# Catch-all method that does any massaging needed for various config
@@ -362,11 +370,13 @@ class Chef
Chef::Config[:log_level] = :warn
when 1
Chef::Config[:log_level] = :info
- else
+ when 2
Chef::Config[:log_level] = :debug
+ else
+ Chef::Config[:log_level] = :trace
end
- Chef::Config[:log_level] = :debug if ENV["KNIFE_DEBUG"]
+ Chef::Config[:log_level] = :trace if ENV["KNIFE_DEBUG"]
Chef::Config[:node_name] = config[:node_name] if config[:node_name]
Chef::Config[:client_key] = config[:client_key] if config[:client_key]
@@ -397,22 +407,36 @@ class Chef
def configure_chef
# knife needs to send logger output to STDERR by default
Chef::Config[:log_location] = STDERR
- config_loader = self.class.load_config(config[:config_file])
+ config_loader = self.class.load_config(config[:config_file], config[:profile])
config[:config_file] = config_loader.config_location
+ # For CLI options like `--config-option key=value`. These have to get
+ # parsed and applied separately.
+ extra_config_options = config.delete(:config_option)
+
merge_configs
apply_computed_config
- Chef::Config.export_proxies
+
# This has to be after apply_computed_config so that Mixlib::Log is configured
Chef::Log.info("Using configuration from #{config[:config_file]}") if config[:config_file]
+
+ begin
+ Chef::Config.apply_extra_config_options(extra_config_options)
+ rescue ChefConfig::UnparsableConfigOption => e
+ ui.error e.message
+ show_usage
+ exit(1)
+ end
+
+ Chef::Config.export_proxies
end
def show_usage
- stdout.puts("USAGE: " + self.opt_parser.to_s)
+ stdout.puts("USAGE: " + opt_parser.to_s)
end
def run_with_pretty_exceptions(raise_exception = false)
- unless self.respond_to?(:run)
+ unless respond_to?(:run)
ui.error "You need to add a #run method to your knife command before you can use it"
end
enforce_path_sanity
@@ -445,7 +469,7 @@ class Chef
when NameError, NoMethodError
ui.error "knife encountered an unexpected error"
ui.info "This may be a bug in the '#{self.class.common_name}' knife command or plugin"
- ui.info "Please collect the output of this command with the `-VV` option before filing a bug report."
+ ui.info "Please collect the output of this command with the `-VVV` option before filing a bug report."
ui.info "Exception: #{e.class.name}: #{e.message}"
when Chef::Exceptions::PrivateKeyMissing
ui.error "Your private key could not be loaded from #{api_key}"
@@ -465,7 +489,11 @@ class Chef
ui.error "Failed to authenticate to #{server_url} as #{username} with key #{api_key}"
ui.info "Response: #{format_rest_error(response)}"
when Net::HTTPForbidden
- ui.error "You authenticated successfully to #{server_url} as #{username} but you are not authorized for this action"
+ ui.error "You authenticated successfully to #{server_url} as #{username} but you are not authorized for this action."
+ proxy_env_vars = ENV.to_hash.keys.map(&:downcase) & %w{http_proxy https_proxy ftp_proxy socks_proxy no_proxy}
+ unless proxy_env_vars.empty?
+ ui.error "There are proxy servers configured, your Chef server may need to be added to NO_PROXY."
+ end
ui.info "Response: #{format_rest_error(response)}"
when Net::HTTPBadRequest
ui.error "The data in your request was invalid"
@@ -517,7 +545,11 @@ class Chef
# FIXME: yard with @yield
def create_object(object, pretty_name = nil, object_class: nil)
- output = edit_data(object, object_class: object_class)
+ output = if object_class
+ edit_data(object, object_class: object_class)
+ else
+ edit_hash(object)
+ end
if Kernel.block_given?
output = yield(output)
@@ -527,7 +559,7 @@ class Chef
pretty_name ||= output
- self.msg("Created #{pretty_name}")
+ msg("Created #{pretty_name}")
output(output) if config[:print_after]
end
@@ -546,7 +578,7 @@ class Chef
output(format_for_display(object)) if config[:print_after]
obj_name = delete_name ? "#{delete_name}[#{name}]" : object
- self.msg("Deleted #{obj_name}")
+ msg("Deleted #{obj_name}")
end
# helper method for testing if a field exists
diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb
index f5dc29371f..15d0f1be18 100644
--- a/lib/chef/knife/bootstrap.rb
+++ b/lib/chef/knife/bootstrap.rb
@@ -67,6 +67,11 @@ class Chef
:description => "The ssh gateway",
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key }
+ option :ssh_gateway_identity,
+ :long => "--ssh-gateway-identity SSH_GATEWAY_IDENTITY",
+ :description => "The SSH identity file used for gateway authentication",
+ :proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway_identity] = key }
+
option :forward_agent,
:short => "-A",
:long => "--forward-agent",
@@ -101,21 +106,19 @@ class Chef
:description => "The proxy server for the node being bootstrapped",
:proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
+ option :bootstrap_proxy_user,
+ :long => "--bootstrap-proxy-user PROXY_USER",
+ :description => "The proxy authentication username for the node being bootstrapped"
+
+ option :bootstrap_proxy_pass,
+ :long => "--bootstrap-proxy-pass PROXY_PASS",
+ :description => "The proxy authentication password for the node being bootstrapped"
+
option :bootstrap_no_proxy,
:long => "--bootstrap-no-proxy [NO_PROXY_URL|NO_PROXY_IP]",
:description => "Do not proxy locations for the node being bootstrapped; this option is used internally by Opscode",
:proc => Proc.new { |np| Chef::Config[:knife][:bootstrap_no_proxy] = np }
- # DEPR: Remove this option in Chef 13
- option :distro,
- :short => "-d DISTRO",
- :long => "--distro DISTRO",
- :description => "Bootstrap a distro using a template. [DEPRECATED] Use -t / --bootstrap-template option instead.",
- :proc => Proc.new { |v|
- Chef::Log.warn("[DEPRECATED] -d / --distro option is deprecated. Use -t / --bootstrap-template option instead.")
- v
- }
-
option :bootstrap_template,
:short => "-t TEMPLATE",
:long => "--bootstrap-template TEMPLATE",
@@ -136,15 +139,6 @@ class Chef
:description => "Execute the bootstrap via sudo with password",
:boolean => false
- # DEPR: Remove this option in Chef 13
- option :template_file,
- :long => "--template-file TEMPLATE",
- :description => "Full path to location of template to use. [DEPRECATED] Use -t / --bootstrap-template option instead.",
- :proc => Proc.new { |v|
- Chef::Log.warn("[DEPRECATED] --template-file option is deprecated. Use -t / --bootstrap-template option instead.")
- v
- }
-
option :run_list,
:short => "-r RUN_LIST",
:long => "--run-list RUN_LIST",
@@ -206,6 +200,11 @@ class Chef
:description => "Custom command to install chef-client",
:proc => Proc.new { |ic| Chef::Config[:knife][:bootstrap_install_command] = ic }
+ option :bootstrap_preinstall_command,
+ :long => "--bootstrap-preinstall-command COMMANDS",
+ :description => "Custom commands to run before installing chef-client",
+ :proc => Proc.new { |preic| Chef::Config[:knife][:bootstrap_preinstall_command] = preic }
+
option :bootstrap_wget_options,
:long => "--bootstrap-wget-options OPTIONS",
:description => "Add options to wget when installing chef-client",
@@ -224,6 +223,7 @@ class Chef
unless valid_values.include?(v)
raise "Invalid value '#{v}' for --node-ssl-verify-mode. Valid values are: #{valid_values.join(", ")}"
end
+ v
}
option :node_verify_api_cert,
@@ -293,10 +293,9 @@ class Chef
end
def bootstrap_template
- # The order here is important. We want to check if we have the new Chef 12 option is set first.
- # Knife cloud plugins unfortunately all set a default option for the :distro so it should be at
- # the end.
- config[:bootstrap_template] || config[:template_file] || config[:distro] || default_bootstrap_template
+ # Allow passing a bootstrap template or use the default
+ # @return [String] The CLI specific bootstrap template or the default
+ config[:bootstrap_template] || default_bootstrap_template
end
def find_template
@@ -304,7 +303,7 @@ class Chef
# Use the template directly if it's a path to an actual file
if File.exists?(template)
- Chef::Log.debug("Using the specified bootstrap template: #{File.dirname(template)}")
+ Chef::Log.trace("Using the specified bootstrap template: #{File.dirname(template)}")
return template
end
@@ -317,7 +316,7 @@ class Chef
bootstrap_files.flatten!
template_file = Array(bootstrap_files).find do |bootstrap_template|
- Chef::Log.debug("Looking for bootstrap template in #{File.dirname(bootstrap_template)}")
+ Chef::Log.trace("Looking for bootstrap template in #{File.dirname(bootstrap_template)}")
File.exists?(bootstrap_template)
end
@@ -326,7 +325,7 @@ class Chef
raise Errno::ENOENT
end
- Chef::Log.debug("Found bootstrap template in #{File.dirname(template_file)}")
+ Chef::Log.trace("Found bootstrap template in #{File.dirname(template_file)}")
template_file
end
@@ -429,11 +428,12 @@ class Chef
ssh.config[:ssh_password] = config[:ssh_password]
ssh.config[:ssh_port] = config[:ssh_port]
ssh.config[:ssh_gateway] = config[:ssh_gateway]
+ ssh.config[:ssh_gateway_identity] = config[:ssh_gateway_identity]
ssh.config[:forward_agent] = config[:forward_agent]
ssh.config[:ssh_identity_file] = config[:ssh_identity_file] || config[:identity_file]
ssh.config[:manual] = true
ssh.config[:host_key_verify] = config[:host_key_verify]
- ssh.config[:on_error] = :raise
+ ssh.config[:on_error] = true
ssh
end
diff --git a/lib/chef/knife/bootstrap/templates/chef-full.erb b/lib/chef/knife/bootstrap/templates/chef-full.erb
index 6007ff9859..c80a3aa4ff 100644
--- a/lib/chef/knife/bootstrap/templates/chef-full.erb
+++ b/lib/chef/knife/bootstrap/templates/chef-full.erb
@@ -162,6 +162,12 @@ do_download() {
return 16
}
+<%# Run any custom commands before installing chef-client -%>
+<%# Ex. wait for cloud-init to complete -%>
+<% if knife_config[:bootstrap_preinstall_command] %>
+ <%= knife_config[:bootstrap_preinstall_command] %>
+<% end %>
+
<% if knife_config[:bootstrap_install_command] %>
<%= knife_config[:bootstrap_install_command] %>
<% else %>
@@ -182,21 +188,21 @@ fi
mkdir -p /etc/chef
<% if client_pem -%>
-cat > /etc/chef/client.pem <<EOP
+cat > /etc/chef/client.pem <<'EOP'
<%= ::File.read(::File.expand_path(client_pem)) %>
EOP
chmod 0600 /etc/chef/client.pem
<% end -%>
<% if validation_key -%>
-cat > /etc/chef/validation.pem <<EOP
+cat > /etc/chef/validation.pem <<'EOP'
<%= validation_key %>
EOP
chmod 0600 /etc/chef/validation.pem
<% end -%>
<% if encrypted_data_bag_secret -%>
-cat > /etc/chef/encrypted_data_bag_secret <<EOP
+cat > /etc/chef/encrypted_data_bag_secret <<'EOP'
<%= encrypted_data_bag_secret %>
EOP
chmod 0600 /etc/chef/encrypted_data_bag_secret
@@ -212,17 +218,17 @@ mkdir -p /etc/chef/trusted_certs
mkdir -p /etc/chef/ohai/hints
<% @chef_config[:knife][:hints].each do |name, hash| -%>
-cat > /etc/chef/ohai/hints/<%= name %>.json <<EOP
+cat > /etc/chef/ohai/hints/<%= name %>.json <<'EOP'
<%= Chef::JSONCompat.to_json(hash) %>
EOP
<% end -%>
<% end -%>
-cat > /etc/chef/client.rb <<EOP
+cat > /etc/chef/client.rb <<'EOP'
<%= config_content %>
EOP
-cat > /etc/chef/first-boot.json <<EOP
+cat > /etc/chef/first-boot.json <<'EOP'
<%= Chef::JSONCompat.to_json(first_boot) %>
EOP
diff --git a/lib/chef/knife/client_delete.rb b/lib/chef/knife/client_delete.rb
index e1b2365361..82b521c7d1 100644
--- a/lib/chef/knife/client_delete.rb
+++ b/lib/chef/knife/client_delete.rb
@@ -32,29 +32,32 @@ class Chef
:long => "--delete-validators",
:description => "Force deletion of client if it's a validator"
- banner "knife client delete CLIENT (options)"
+ banner "knife client delete [CLIENT [CLIENT]] (options)"
def run
- @client_name = @name_args[0]
-
- if @client_name.nil?
+ if @name_args.length == 0
show_usage
- ui.fatal("You must specify a client name")
+ ui.fatal("You must specify at least one client name")
exit 1
end
- delete_object(Chef::ApiClientV1, @client_name, "client") {
- object = Chef::ApiClientV1.load(@client_name)
+ @name_args.each do |client_name|
+ delete_client(client_name)
+ end
+ end
+
+ def delete_client(client_name)
+ delete_object(Chef::ApiClientV1, client_name, "client") do
+ object = Chef::ApiClientV1.load(client_name)
if object.validator
unless config[:delete_validators]
- ui.fatal("You must specify --delete-validators to delete the validator client #{@client_name}")
+ ui.fatal("You must specify --delete-validators to delete the validator client #{client_name}")
exit 2
end
end
object.destroy
- }
+ end
end
-
end
end
end
diff --git a/lib/chef/knife/client_key_create.rb b/lib/chef/knife/client_key_create.rb
index 68ad4d16d2..1f209ec879 100644
--- a/lib/chef/knife/client_key_create.rb
+++ b/lib/chef/knife/client_key_create.rb
@@ -17,6 +17,7 @@
#
require "chef/knife"
+require "chef/knife/key_create"
require "chef/knife/key_create_base"
class Chef
@@ -30,6 +31,8 @@ class Chef
class ClientKeyCreate < Knife
include Chef::Knife::KeyCreateBase
+ banner "knife client key create CLIENT (options)"
+
attr_reader :actor
def initialize(argv = [])
diff --git a/lib/chef/knife/client_key_delete.rb b/lib/chef/knife/client_key_delete.rb
index 64eae2e27c..3463389a05 100644
--- a/lib/chef/knife/client_key_delete.rb
+++ b/lib/chef/knife/client_key_delete.rb
@@ -17,6 +17,7 @@
#
require "chef/knife"
+require "chef/knife/key_delete"
class Chef
class Knife
diff --git a/lib/chef/knife/client_key_edit.rb b/lib/chef/knife/client_key_edit.rb
index 1dbd3c487b..1a685b7a56 100644
--- a/lib/chef/knife/client_key_edit.rb
+++ b/lib/chef/knife/client_key_edit.rb
@@ -17,6 +17,7 @@
#
require "chef/knife"
+require "chef/knife/key_edit"
require "chef/knife/key_edit_base"
class Chef
diff --git a/lib/chef/knife/client_key_list.rb b/lib/chef/knife/client_key_list.rb
index 194ad42931..aa63c1196b 100644
--- a/lib/chef/knife/client_key_list.rb
+++ b/lib/chef/knife/client_key_list.rb
@@ -17,6 +17,7 @@
#
require "chef/knife"
+require "chef/knife/key_list"
require "chef/knife/key_list_base"
class Chef
diff --git a/lib/chef/knife/client_key_show.rb b/lib/chef/knife/client_key_show.rb
index 77f9e96c5a..49bc9d3596 100644
--- a/lib/chef/knife/client_key_show.rb
+++ b/lib/chef/knife/client_key_show.rb
@@ -17,6 +17,7 @@
#
require "chef/knife"
+require "chef/knife/key_show"
class Chef
class Knife
diff --git a/lib/chef/knife/client_reregister.rb b/lib/chef/knife/client_reregister.rb
index 5d9b2c0962..cc2b218e87 100644
--- a/lib/chef/knife/client_reregister.rb
+++ b/lib/chef/knife/client_reregister.rb
@@ -44,7 +44,7 @@ class Chef
end
client = Chef::ApiClientV1.reregister(@client_name)
- Chef::Log.debug("Updated client data: #{client.inspect}")
+ Chef::Log.trace("Updated client data: #{client.inspect}")
key = client.private_key
if config[:file]
File.open(config[:file], "w") do |f|
diff --git a/lib/chef/knife/configure.rb b/lib/chef/knife/configure.rb
index e726e32684..abde923788 100644
--- a/lib/chef/knife/configure.rb
+++ b/lib/chef/knife/configure.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,6 +17,7 @@
#
require "chef/knife"
+require "chef/util/path_helper"
class Chef
class Knife
@@ -67,26 +68,22 @@ class Chef
end
def run
- ask_user_for_config_path
-
FileUtils.mkdir_p(chef_config_path)
+ config_file = File.join(chef_config_path, "credentials")
ask_user_for_config
- ::File.open(config[:config_file], "w") do |f|
+ config_file = File.expand_path(config_file)
+ if File.exist?(config_file)
+ confirm("Overwrite #{config_file}")
+ end
+ ::File.open(config_file, "w") do |f|
f.puts <<-EOH
-log_level :info
-log_location STDOUT
-node_name '#{new_client_name}'
-client_key '#{new_client_key}'
-validation_client_name '#{validation_client_name}'
-validation_key '#{validation_key}'
-chef_server_url '#{chef_server}'
-syntax_check_cache_path '#{File.join(chef_config_path, "syntax_check_cache")}'
+[default]
+client_name = '#{new_client_name}'
+client_key = '#{new_client_key}'
+chef_server_url = '#{chef_server}'
EOH
- unless chef_repo.empty?
- f.puts "cookbook_path [ '#{chef_repo}/cookbooks' ]"
- end
end
if config[:initial]
@@ -111,29 +108,14 @@ EOH
ui.msg("Before running commands with Knife")
ui.msg("")
ui.msg("*****")
- ui.msg("")
- ui.msg("You must place your validation key in:")
- ui.msg(" #{validation_key}")
- ui.msg("Before generating instance data with Knife")
- ui.msg("")
- ui.msg("*****")
end
ui.msg("Configuration file written to #{config[:config_file]}")
end
- def ask_user_for_config_path
- config[:config_file] ||= ask_question("Where should I put the config file? ", :default => "#{Chef::Config[:user_home]}/.chef/knife.rb")
- # have to use expand path to expand the tilde character to the user's home
- config[:config_file] = File.expand_path(config[:config_file])
- if File.exists?(config[:config_file])
- confirm("Overwrite #{config[:config_file]}")
- end
- end
-
def ask_user_for_config
server_name = guess_servername
- @chef_server = config[:chef_server_url] || ask_question("Please enter the chef server URL: ", :default => "https://#{server_name}:443")
+ @chef_server = config[:chef_server_url] || ask_question("Please enter the chef server URL: ", :default => "https://#{server_name}/organizations/myorg")
if config[:initial]
@new_client_name = config[:node_name] || ask_question("Please enter a name for the new user: ", :default => Etc.getlogin)
@admin_client_name = config[:admin_client_name] || ask_question("Please enter the existing admin name: ", :default => "admin")
@@ -142,10 +124,6 @@ EOH
else
@new_client_name = config[:node_name] || ask_question("Please enter an existing username or clientname for the API: ", :default => Etc.getlogin)
end
- @validation_client_name = config[:validation_client_name] || ask_question("Please enter the validation clientname: ", :default => "chef-validator")
- @validation_key = config[:validation_key] || ask_question("Please enter the location of the validation key: ", :default => "/etc/chef-server/chef-validator.pem")
- @validation_key = File.expand_path(@validation_key)
- @chef_repo = config[:repository] || ask_question("Please enter the path to a chef repository (or leave blank): ")
@new_client_key = config[:client_key] || File.join(chef_config_path, "#{@new_client_name}.pem")
@new_client_key = File.expand_path(@new_client_key)
@@ -153,18 +131,12 @@ EOH
def guess_servername
o = Ohai::System.new
- o.load_plugins
- o.require_plugin "os"
- o.require_plugin "hostname"
+ o.all_plugins(%w{ os hostname fqdn })
o[:fqdn] || o[:machinename] || o[:hostname] || "localhost"
end
- def config_file
- config[:config_file]
- end
-
def chef_config_path
- File.dirname(config_file)
+ Chef::Util::PathHelper.home(".chef")
end
end
end
diff --git a/lib/chef/knife/configure_client.rb b/lib/chef/knife/configure_client.rb
index 7d0b3d260d..c015687ac7 100644
--- a/lib/chef/knife/configure_client.rb
+++ b/lib/chef/knife/configure_client.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,8 +34,6 @@ class Chef
FileUtils.mkdir_p(@config_dir)
ui.info("Writing client.rb")
File.open(File.join(@config_dir, "client.rb"), "w") do |file|
- file.puts("log_level :info")
- file.puts("log_location STDOUT")
file.puts("chef_server_url '#{Chef::Config[:chef_server_url]}'")
file.puts("validation_client_name '#{Chef::Config[:validation_client_name]}'")
end
diff --git a/lib/chef/knife/cookbook_bulk_delete.rb b/lib/chef/knife/cookbook_bulk_delete.rb
index bd1c8a22cc..cdd1584e36 100644
--- a/lib/chef/knife/cookbook_bulk_delete.rb
+++ b/lib/chef/knife/cookbook_bulk_delete.rb
@@ -42,7 +42,7 @@ class Chef
all_cookbooks = Chef::CookbookVersion.list
cookbooks_names = all_cookbooks.keys.grep(regex)
- cookbooks_to_delete = cookbooks_names.inject({}) { |hash, name| hash[name] = all_cookbooks[name];hash }
+ cookbooks_to_delete = cookbooks_names.inject({}) { |hash, name| hash[name] = all_cookbooks[name]; hash }
ui.msg "All versions of the following cookbooks will be deleted:"
ui.msg ""
ui.msg ui.list(cookbooks_to_delete.keys.sort, :columns_down)
diff --git a/lib/chef/knife/cookbook_create.rb b/lib/chef/knife/cookbook_create.rb
index 950de380f8..6122fd52d3 100644
--- a/lib/chef/knife/cookbook_create.rb
+++ b/lib/chef/knife/cookbook_create.rb
@@ -21,438 +21,9 @@ require "chef/knife"
class Chef
class Knife
class CookbookCreate < Knife
-
- deps do
- require "chef/json_compat"
- require "uri"
- require "fileutils"
- end
-
- banner "knife cookbook create COOKBOOK (options)"
-
- option :cookbook_path,
- :short => "-o PATH",
- :long => "--cookbook-path PATH",
- :description => "The directory where the cookbook will be created"
-
- option :readme_format,
- :short => "-r FORMAT",
- :long => "--readme-format FORMAT",
- :description => "Format of the README file, supported formats are 'md' (markdown) and 'rdoc' (rdoc)"
-
- option :cookbook_license,
- :short => "-I LICENSE",
- :long => "--license LICENSE",
- :description => "License for cookbook, apachev2, gplv2, gplv3, mit or none"
-
- option :cookbook_copyright,
- :short => "-C COPYRIGHT",
- :long => "--copyright COPYRIGHT",
- :description => "Name of copyright holder"
-
- option :cookbook_email,
- :short => "-m EMAIL",
- :long => "--email EMAIL",
- :description => "Email address of cookbook maintainer"
-
def run
- self.config = Chef::Config.merge!(config)
- if @name_args.length < 1
- show_usage
- ui.fatal("You must specify a cookbook name")
- exit 1
- end
-
- if default_cookbook_path_empty? && parameter_empty?(config[:cookbook_path])
- raise ArgumentError, "Default cookbook_path is not specified in the knife.rb config file, and a value to -o is not provided. Nowhere to write the new cookbook to."
- end
-
- cookbook_path = File.expand_path(Array(config[:cookbook_path]).first)
- cookbook_name = @name_args.first
- copyright = config[:cookbook_copyright] || "YOUR_COMPANY_NAME"
- email = config[:cookbook_email] || "YOUR_EMAIL"
- license = ((config[:cookbook_license] != "false") && config[:cookbook_license]) || "none"
- readme_format = ((config[:readme_format] != "false") && config[:readme_format]) || "md"
- create_cookbook(cookbook_path, cookbook_name, copyright, license)
- create_readme(cookbook_path, cookbook_name, readme_format)
- create_changelog(cookbook_path, cookbook_name)
- create_metadata(cookbook_path, cookbook_name, copyright, email, license, readme_format)
- end
-
- def create_cookbook(dir, cookbook_name, copyright, license)
- msg("** Creating cookbook #{cookbook_name} in #{dir}")
- FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "attributes")}"
- FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "recipes")}"
- FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "definitions")}"
- FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "libraries")}"
- FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "resources")}"
- FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "providers")}"
- FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "files", "default")}"
- FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "templates", "default")}"
- unless File.exists?(File.join(dir, cookbook_name, "recipes", "default.rb"))
- open(File.join(dir, cookbook_name, "recipes", "default.rb"), "w") do |file|
- file.puts <<-EOH
-#
-# Cookbook Name:: #{cookbook_name}
-# Recipe:: default
-#
-# Copyright #{Time.now.year}, #{copyright}
-#
-EOH
- case license
- when "apachev2"
- file.puts <<-EOH
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-EOH
- when "gplv2"
- file.puts <<-EOH
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-EOH
- when "gplv3"
- file.puts <<-EOH
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-EOH
- when "mit"
- file.puts <<-EOH
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-EOH
- when "none"
- file.puts <<-EOH
-# All rights reserved - Do Not Redistribute
-#
-EOH
- end
- end
- end
- end
-
- def create_changelog(dir, cookbook_name)
- msg("** Creating CHANGELOG for cookbook: #{cookbook_name}")
- unless File.exists?(File.join(dir, cookbook_name, "CHANGELOG.md"))
- open(File.join(dir, cookbook_name, "CHANGELOG.md"), "w") do |file|
- file.puts <<-EOH
-# #{cookbook_name} CHANGELOG
-
-This file is used to list changes made in each version of the #{cookbook_name} cookbook.
-
-## 0.1.0
-- [your_name] - Initial release of #{cookbook_name}
-
-- - -
-Check the [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax) for help with Markdown.
-
-The [Github Flavored Markdown page](http://github.github.com/github-flavored-markdown/) describes the differences between markdown on github and standard markdown.
-EOH
- end
- end
+ Chef::Log.fatal("knife cookbook create has been removed. Please use `chef generate cookbook` from the ChefDK")
end
-
- def create_readme(dir, cookbook_name, readme_format)
- msg("** Creating README for cookbook: #{cookbook_name}")
- unless File.exist?(File.join(dir, cookbook_name, "README.#{readme_format}"))
- open(File.join(dir, cookbook_name, "README.#{readme_format}"), "w") do |file|
- case readme_format
- when "rdoc"
- file.puts <<-EOH
-= #{cookbook_name} Cookbook
-TODO: Enter the cookbook description here.
-
-e.g.
-This cookbook makes your favorite breakfast sandwich.
-
-== Requirements
-TODO: List your cookbook requirements. Be sure to include any requirements this cookbook has on platforms, libraries, other cookbooks, packages, operating systems, etc.
-
-e.g.
-==== packages
-- +toaster+ - #{cookbook_name} needs toaster to brown your bagel.
-
-== Attributes
-TODO: List your cookbook attributes here.
-
-e.g.
-==== #{cookbook_name}::default
-<table>
- <tr>
- <th>Key</th>
- <th>Type</th>
- <th>Description</th>
- <th>Default</th>
- </tr>
- <tr>
- <td><tt>['#{cookbook_name}']['bacon']</tt></td>
- <td>Boolean</td>
- <td>whether to include bacon</td>
- <td><tt>true</tt></td>
- </tr>
-</table>
-
-== Usage
-==== #{cookbook_name}::default
-TODO: Write usage instructions for each cookbook.
-
-e.g.
-Just include +#{cookbook_name}+ in your node's +run_list+:
-
- {
- "name":"my_node",
- "run_list": [
- "recipe[#{cookbook_name}]"
- ]
- }
-
-== Contributing
-TODO: (optional) If this is a public cookbook, detail the process for contributing. If this is a private cookbook, remove this section.
-
-e.g.
-1. Fork the repository on Github
-2. Create a named feature branch (like `add_component_x`)
-3. Write your change
-4. Write tests for your change (if applicable)
-5. Run the tests, ensuring they all pass
-6. Submit a Pull Request using Github
-
-== License and Authors
-Authors: TODO: List authors
-EOH
- when "md", "mkd", "txt"
- file.puts <<-EOH
-# #{cookbook_name} Cookbook
-
-TODO: Enter the cookbook description here.
-
-e.g.
-This cookbook makes your favorite breakfast sandwich.
-
-## Requirements
-
-TODO: List your cookbook requirements. Be sure to include any requirements this cookbook has on platforms, libraries, other cookbooks, packages, operating systems, etc.
-
-e.g.
-### Platforms
-
-- SandwichOS
-
-### Chef
-
-- Chef 12.0 or later
-
-### Cookbooks
-
-- `toaster` - #{cookbook_name} needs toaster to brown your bagel.
-
-## Attributes
-
-TODO: List your cookbook attributes here.
-
-e.g.
-### #{cookbook_name}::default
-
-<table>
- <tr>
- <th>Key</th>
- <th>Type</th>
- <th>Description</th>
- <th>Default</th>
- </tr>
- <tr>
- <td><tt>['#{cookbook_name}']['bacon']</tt></td>
- <td>Boolean</td>
- <td>whether to include bacon</td>
- <td><tt>true</tt></td>
- </tr>
-</table>
-
-## Usage
-
-### #{cookbook_name}::default
-
-TODO: Write usage instructions for each cookbook.
-
-e.g.
-Just include `#{cookbook_name}` in your node's `run_list`:
-
-```json
-{
- "name":"my_node",
- "run_list": [
- "recipe[#{cookbook_name}]"
- ]
-}
-```
-
-## Contributing
-
-TODO: (optional) If this is a public cookbook, detail the process for contributing. If this is a private cookbook, remove this section.
-
-e.g.
-1. Fork the repository on Github
-2. Create a named feature branch (like `add_component_x`)
-3. Write your change
-4. Write tests for your change (if applicable)
-5. Run the tests, ensuring they all pass
-6. Submit a Pull Request using Github
-
-## License and Authors
-
-Authors: TODO: List authors
-
-EOH
- else
- file.puts <<-EOH
-#{cookbook_name} Cookbook
-#{'=' * "#{cookbook_name} Cookbook".length}
- TODO: Enter the cookbook description here.
-
- e.g.
- This cookbook makes your favorite breakfast sandwich.
-
-Requirements
- TODO: List your cookbook requirements. Be sure to include any requirements this cookbook has on platforms, libraries, other cookbooks, packages, operating systems, etc.
-
- e.g.
- toaster #{cookbook_name} needs toaster to brown your bagel.
-
-Attributes
- TODO: List your cookbook attributes here.
-
- #{cookbook_name}
- Key Type Description Default
- ['#{cookbook_name}']['bacon'] Boolean whether to include bacon true
-
-Usage
- #{cookbook_name}
- TODO: Write usage instructions for each cookbook.
-
- e.g.
- Just include `#{cookbook_name}` in your node's `run_list`:
-
- [code]
- {
- "name":"my_node",
- "run_list": [
- "recipe[#{cookbook_name}]"
- ]
- }
- [/code]
-
-Contributing
- TODO: (optional) If this is a public cookbook, detail the process for contributing. If this is a private cookbook, remove this section.
-
- e.g.
- 1. Fork the repository on Github
- 2. Create a named feature branch (like `add_component_x`)
- 3. Write your change
- 4. Write tests for your change (if applicable)
- 5. Run the tests, ensuring they all pass
- 6. Submit a Pull Request using Github
-
-License and Authors
- Authors: TODO: List authors
-EOH
- end
- end
- end
- end
-
- def create_metadata(dir, cookbook_name, copyright, email, license, readme_format)
- msg("** Creating metadata for cookbook: #{cookbook_name}")
-
- license_name = case license
- when "apachev2"
- "Apache 2.0"
- when "gplv2"
- "GNU Public License 2.0"
- when "gplv3"
- "GNU Public License 3.0"
- when "mit"
- "MIT"
- when "none"
- "All rights reserved"
- end
-
- unless File.exist?(File.join(dir, cookbook_name, "metadata.rb"))
- open(File.join(dir, cookbook_name, "metadata.rb"), "w") do |file|
- if File.exist?(File.join(dir, cookbook_name, "README.#{readme_format}"))
- long_description = "long_description IO.read(File.join(File.dirname(__FILE__), 'README.#{readme_format}'))"
- end
- file.puts <<-EOH
-name '#{cookbook_name}'
-maintainer '#{copyright}'
-maintainer_email '#{email}'
-license '#{license_name}'
-description 'Installs/Configures #{cookbook_name}'
-#{long_description}
-version '0.1.0'
-EOH
- end
- end
- end
-
- private
-
- def default_cookbook_path_empty?
- Chef::Config[:cookbook_path].nil? || Chef::Config[:cookbook_path].empty?
- end
-
- def parameter_empty?(parameter)
- parameter.nil? || parameter.empty?
- end
-
end
end
end
diff --git a/lib/chef/knife/cookbook_download.rb b/lib/chef/knife/cookbook_download.rb
index 741f444093..77e7aa0d09 100644
--- a/lib/chef/knife/cookbook_download.rb
+++ b/lib/chef/knife/cookbook_download.rb
@@ -70,12 +70,12 @@ class Chef
ui.info("Downloading #{@cookbook_name} cookbook version #{@version}")
cookbook = Chef::CookbookVersion.load(@cookbook_name, @version)
- manifest = cookbook.manifest
+ manifest = cookbook.cookbook_manifest
basedir = File.join(config[:download_directory], "#{@cookbook_name}-#{cookbook.version}")
if File.exists?(basedir)
if config[:force]
- Chef::Log.debug("Deleting #{basedir}")
+ Chef::Log.trace("Deleting #{basedir}")
FileUtils.rm_rf(basedir)
else
ui.fatal("Directory #{basedir} exists, use --force to overwrite")
@@ -83,12 +83,11 @@ class Chef
end
end
- Chef::CookbookVersion::COOKBOOK_SEGMENTS.each do |segment|
- next unless manifest.has_key?(segment)
+ manifest.by_parent_directory.each do |segment, files|
ui.info("Downloading #{segment}")
- manifest[segment].each do |segment_file|
+ files.each do |segment_file|
dest = File.join(basedir, segment_file["path"].gsub("/", File::SEPARATOR))
- Chef::Log.debug("Downloading #{segment_file['path']} to #{dest}")
+ Chef::Log.trace("Downloading #{segment_file['path']} to #{dest}")
FileUtils.mkdir_p(File.dirname(dest))
tempfile = rest.streaming_request(segment_file["url"])
FileUtils.mv(tempfile.path, dest)
diff --git a/lib/chef/knife/cookbook_metadata.rb b/lib/chef/knife/cookbook_metadata.rb
index 29eba6a36a..6f8f6db996 100644
--- a/lib/chef/knife/cookbook_metadata.rb
+++ b/lib/chef/knife/cookbook_metadata.rb
@@ -1,7 +1,7 @@
#
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -47,7 +47,7 @@ class Chef
if config[:all]
cl = Chef::CookbookLoader.new(config[:cookbook_path])
cl.load_cookbooks
- cl.each do |cname, cookbook|
+ cl.each_key do |cname|
generate_metadata(cname.to_s)
end
else
@@ -80,7 +80,7 @@ class Chef
File.open(json_file, "w") do |f|
f.write(Chef::JSONCompat.to_json_pretty(md))
end
- Chef::Log.debug("Generated #{json_file}")
+ Chef::Log.trace("Generated #{json_file}")
rescue Exceptions::ObsoleteDependencySyntax, Exceptions::InvalidVersionConstraint => e
ui.stderr.puts "ERROR: The cookbook '#{cookbook}' contains invalid or obsolete metadata syntax."
ui.stderr.puts "in #{file}:"
diff --git a/lib/chef/knife/cookbook_show.rb b/lib/chef/knife/cookbook_show.rb
index 5fab7c303f..1d9983632d 100644
--- a/lib/chef/knife/cookbook_show.rb
+++ b/lib/chef/knife/cookbook_show.rb
@@ -51,6 +51,10 @@ class Chef
:description => "Show corresponding URIs"
def run
+ cookbook_name, cookbook_version, segment, filename = @name_args
+
+ cookbook = Chef::CookbookVersion.load(cookbook_name, cookbook_version) unless cookbook_version.nil?
+
case @name_args.length
when 4 # We are showing a specific file
node = Hash.new
@@ -59,15 +63,11 @@ class Chef
node[:platform_version] = config[:platform_version] if config.has_key?(:platform_version)
class << node
- def attribute?(name)
+ def attribute?(name) # rubocop:disable Lint/NestedMethodDefinition
has_key?(name)
end
end
- cookbook_name, segment, filename = @name_args[0], @name_args[2], @name_args[3]
- cookbook_version = @name_args[1] == "latest" ? "_latest" : @name_args[1]
-
- cookbook = rest.get("cookbooks/#{cookbook_name}/#{cookbook_version}")
manifest_entry = cookbook.preferred_manifest_record(node, segment, filename)
temp_file = rest.streaming_request(manifest_entry[:url])
@@ -76,14 +76,14 @@ class Chef
pretty_print(temp_file.read)
when 3 # We are showing a specific part of the cookbook
- cookbook_version = @name_args[1] == "latest" ? "_latest" : @name_args[1]
- result = rest.get("cookbooks/#{@name_args[0]}/#{cookbook_version}")
- output(result.manifest[@name_args[2]])
- when 2 # We are showing the whole cookbook data
- cookbook_version = @name_args[1] == "latest" ? "_latest" : @name_args[1]
- output(rest.get("cookbooks/#{@name_args[0]}/#{cookbook_version}"))
+ if segment == "metadata"
+ output(cookbook.metadata)
+ else
+ output(cookbook.files_for(segment))
+ end
+ when 2 # We are showing the whole cookbook
+ output(cookbook.display)
when 1 # We are showing the cookbook versions (all of them)
- cookbook_name = @name_args[0]
env = config[:environment]
api_endpoint = env ? "environments/#{env}/cookbooks/#{cookbook_name}" : "cookbooks/#{cookbook_name}"
output(format_cookbook_list_for_display(rest.get(api_endpoint)))
diff --git a/lib/chef/knife/cookbook_site_download.rb b/lib/chef/knife/cookbook_site_download.rb
index 7d0e21791d..07e0037bd6 100644
--- a/lib/chef/knife/cookbook_site_download.rb
+++ b/lib/chef/knife/cookbook_site_download.rb
@@ -37,10 +37,22 @@ class Chef
:long => "--force",
:description => "Force download deprecated version"
+ option :supermarket_site,
+ :short => "-m SUPERMARKET_SITE",
+ :long => "--supermarket-site SUPERMARKET_SITE",
+ :description => "Supermarket Site",
+ :default => "https://supermarket.chef.io",
+ :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
def run
if current_cookbook_deprecated?
message = "DEPRECATION: This cookbook has been deprecated. "
- message << "It has been replaced by #{replacement_cookbook}."
+ replacement = replacement_cookbook
+ if !replacement.to_s.strip.empty?
+ message << "It has been replaced by #{replacement}."
+ else
+ message << "No replacement has been defined."
+ end
ui.warn message
unless config[:force]
@@ -59,7 +71,7 @@ class Chef
private
def cookbooks_api_url
- "https://supermarket.chef.io/api/v1/cookbooks"
+ "#{config[:supermarket_site]}/api/v1/cookbooks"
end
def current_cookbook_data
@@ -98,7 +110,7 @@ class Chef
end
def replacement_cookbook
- File.basename(current_cookbook_data["replacement"])
+ File.basename(current_cookbook_data["replacement"] || "")
end
def specific_cookbook_version_url
diff --git a/lib/chef/knife/cookbook_site_install.rb b/lib/chef/knife/cookbook_site_install.rb
index 802fdd792b..f4d692e13c 100644
--- a/lib/chef/knife/cookbook_site_install.rb
+++ b/lib/chef/knife/cookbook_site_install.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright 2010-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,6 +19,7 @@
require "chef/knife"
require "chef/exceptions"
require "shellwords"
+require "mixlib/archive"
class Chef
class Knife
@@ -59,6 +60,13 @@ class Chef
:boolean => true,
:default => false
+ option :supermarket_site,
+ :short => "-m SUPERMARKET_SITE",
+ :long => "--supermarket-site SUPERMARKET_SITE",
+ :description => "Supermarket Site",
+ :default => "https://supermarket.chef.io",
+ :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
attr_reader :cookbook_name
attr_reader :vendor_path
@@ -75,7 +83,7 @@ class Chef
# Check to ensure we have a valid source of cookbooks before continuing
#
@install_path = File.expand_path(Array(config[:cookbook_path]).first)
- ui.info "Installing #@cookbook_name to #{@install_path}"
+ ui.info "Installing #{@cookbook_name} to #{@install_path}"
@repo = CookbookSCMRepo.new(@install_path, ui, config)
#cookbook_path = File.join(vendor_path, name_args[0])
@@ -108,7 +116,7 @@ class Chef
end
unless config[:no_deps]
- preferred_metadata.dependencies.each do |cookbook, version_list|
+ preferred_metadata.dependencies.each_key do |cookbook|
# Doesn't do versions.. yet
nv = self.class.new
nv.config = config
@@ -134,6 +142,7 @@ class Chef
def download_cookbook_to(download_path)
downloader = Chef::Knife::CookbookSiteDownload.new
downloader.config[:file] = download_path
+ downloader.config[:supermarket_site] = config[:supermarket_site]
downloader.name_args = name_args
downloader.run
downloader
@@ -141,12 +150,7 @@ class Chef
def extract_cookbook(upstream_file, version)
ui.info("Uncompressing #{@cookbook_name} version #{version}.")
- # FIXME: Detect if we have the bad tar from git on Windows: https://github.com/opscode/chef/issues/1753
- extract_command = "tar zxvf \"#{convert_path upstream_file}\""
- if Chef::Platform.windows?
- extract_command << " --force-local"
- end
- shell_out!(extract_command, :cwd => @install_path)
+ Mixlib::Archive.new(convert_path(upstream_file)).extract(@install_path, perms: false)
end
def clear_existing_files(cookbook_path)
@@ -157,9 +161,9 @@ class Chef
def convert_path(upstream_file)
# converts a Windows path (C:\foo) to a mingw path (/c/foo)
if ENV["MSYSTEM"] == "MINGW32"
- return upstream_file.sub(/^([[:alpha:]]):/, '/\1')
+ upstream_file.sub(/^([[:alpha:]]):/, '/\1')
else
- return Shellwords.escape upstream_file
+ Shellwords.escape upstream_file
end
end
diff --git a/lib/chef/knife/cookbook_site_list.rb b/lib/chef/knife/cookbook_site_list.rb
index abe48bf340..3bdef8abe5 100644
--- a/lib/chef/knife/cookbook_site_list.rb
+++ b/lib/chef/knife/cookbook_site_list.rb
@@ -30,6 +30,13 @@ class Chef
:long => "--with-uri",
:description => "Show corresponding URIs"
+ option :supermarket_site,
+ :short => "-m SUPERMARKET_SITE",
+ :long => "--supermarket-site SUPERMARKET_SITE",
+ :description => "Supermarket Site",
+ :default => "https://supermarket.chef.io",
+ :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
def run
if config[:with_uri]
cookbooks = Hash.new
@@ -41,7 +48,7 @@ class Chef
end
def get_cookbook_list(items = 10, start = 0, cookbook_collection = {})
- cookbooks_url = "https://supermarket.chef.io/api/v1/cookbooks?items=#{items}&start=#{start}"
+ cookbooks_url = "#{config[:supermarket_site]}/api/v1/cookbooks?items=#{items}&start=#{start}"
cr = noauth_rest.get(cookbooks_url)
cr["items"].each do |cookbook|
cookbook_collection[cookbook["cookbook_name"]] = cookbook
diff --git a/lib/chef/knife/cookbook_site_search.rb b/lib/chef/knife/cookbook_site_search.rb
index ba4b873efc..d401844217 100644
--- a/lib/chef/knife/cookbook_site_search.rb
+++ b/lib/chef/knife/cookbook_site_search.rb
@@ -24,12 +24,19 @@ class Chef
banner "knife cookbook site search QUERY (options)"
category "cookbook site"
+ option :supermarket_site,
+ :short => "-m SUPERMARKET_SITE",
+ :long => "--supermarket-site SUPERMARKET_SITE",
+ :description => "Supermarket Site",
+ :default => "https://supermarket.chef.io",
+ :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
def run
output(search_cookbook(name_args[0]))
end
def search_cookbook(query, items = 10, start = 0, cookbook_collection = {})
- cookbooks_url = "https://supermarket.chef.io/api/v1/search?q=#{query}&items=#{items}&start=#{start}"
+ cookbooks_url = "#{config[:supermarket_site]}/api/v1/search?q=#{query}&items=#{items}&start=#{start}"
cr = noauth_rest.get(cookbooks_url)
cr["items"].each do |cookbook|
cookbook_collection[cookbook["cookbook_name"]] = cookbook
diff --git a/lib/chef/knife/cookbook_site_share.rb b/lib/chef/knife/cookbook_site_share.rb
index 6f37568f5f..e4d55276da 100644
--- a/lib/chef/knife/cookbook_site_share.rb
+++ b/lib/chef/knife/cookbook_site_share.rb
@@ -50,6 +50,13 @@ class Chef
:default => false,
:description => "Don't take action, only print what files will be uploaded to Supermarket."
+ option :supermarket_site,
+ :short => "-m SUPERMARKET_SITE",
+ :long => "--supermarket-site SUPERMARKET_SITE",
+ :description => "Supermarket Site",
+ :default => "https://supermarket.chef.io",
+ :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
def run
config[:cookbook_path] ||= Chef::Config[:cookbook_path]
@@ -71,12 +78,12 @@ class Chef
Chef::CookbookUploader.new(cookbook).validate_cookbooks
tmp_cookbook_dir = Chef::CookbookSiteStreamingUploader.create_build_dir(cookbook)
begin
- Chef::Log.debug("Temp cookbook directory is #{tmp_cookbook_dir.inspect}")
+ Chef::Log.trace("Temp cookbook directory is #{tmp_cookbook_dir.inspect}")
ui.info("Making tarball #{cookbook_name}.tgz")
shell_out!("#{tar_cmd} -czf #{cookbook_name}.tgz #{cookbook_name}", :cwd => tmp_cookbook_dir)
rescue => e
ui.error("Error making tarball #{cookbook_name}.tgz: #{e.message}. Increase log verbosity (-VV) for more information.")
- Chef::Log.debug("\n#{e.backtrace.join("\n")}")
+ Chef::Log.trace("\n#{e.backtrace.join("\n")}")
exit(1)
end
@@ -91,11 +98,11 @@ class Chef
begin
do_upload("#{tmp_cookbook_dir}/#{cookbook_name}.tgz", category, Chef::Config[:node_name], Chef::Config[:client_key])
ui.info("Upload complete")
- Chef::Log.debug("Removing local staging directory at #{tmp_cookbook_dir}")
+ Chef::Log.trace("Removing local staging directory at #{tmp_cookbook_dir}")
FileUtils.rm_rf tmp_cookbook_dir
rescue => e
ui.error("Error uploading cookbook #{cookbook_name} to Supermarket: #{e.message}. Increase log verbosity (-VV) for more information.")
- Chef::Log.debug("\n#{e.backtrace.join("\n")}")
+ Chef::Log.trace("\n#{e.backtrace.join("\n")}")
exit(1)
end
@@ -106,23 +113,17 @@ class Chef
end
def get_category(cookbook_name)
- begin
- data = noauth_rest.get("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}")
- if !data["category"] && data["error_code"]
- ui.fatal("Received an error from Supermarket: #{data["error_code"]}. On the first time you upload it, you are required to specify the category you want to share this cookbook to.")
- exit(1)
- else
- data["category"]
- end
- rescue => e
- ui.fatal("Unable to reach Supermarket: #{e.message}. Increase log verbosity (-VV) for more information.")
- Chef::Log.debug("\n#{e.backtrace.join("\n")}")
- exit(1)
- end
+ data = noauth_rest.get("#{config[:supermarket_site]}/api/v1/cookbooks/#{@name_args[0]}")
+ data["category"]
+ rescue => e
+ return "Other" if e.kind_of?(Net::HTTPServerException) && e.response.code == "404"
+ ui.fatal("Unable to reach Supermarket: #{e.message}. Increase log verbosity (-VV) for more information.")
+ Chef::Log.trace("\n#{e.backtrace.join("\n")}")
+ exit(1)
end
def do_upload(cookbook_filename, cookbook_category, user_id, user_secret_filename)
- uri = "https://supermarket.chef.io/api/v1/cookbooks"
+ uri = "#{config[:supermarket_site]}/api/v1/cookbooks"
category_string = Chef::JSONCompat.to_json({ "category" => cookbook_category })
diff --git a/lib/chef/knife/cookbook_site_show.rb b/lib/chef/knife/cookbook_site_show.rb
index c0280cb318..ce153ca5a1 100644
--- a/lib/chef/knife/cookbook_site_show.rb
+++ b/lib/chef/knife/cookbook_site_show.rb
@@ -24,21 +24,32 @@ class Chef
banner "knife cookbook site show COOKBOOK [VERSION] (options)"
category "cookbook site"
+ option :supermarket_site,
+ :short => "-m SUPERMARKET_SITE",
+ :long => "--supermarket-site SUPERMARKET_SITE",
+ :description => "Supermarket Site",
+ :default => "https://supermarket.chef.io",
+ :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
def run
output(format_for_display(get_cookbook_data))
end
+ def supermarket_uri
+ "#{config[:supermarket_site]}/api/v1"
+ end
+
def get_cookbook_data
case @name_args.length
when 1
- noauth_rest.get("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}")
+ noauth_rest.get("#{supermarket_uri}/cookbooks/#{@name_args[0]}")
when 2
- noauth_rest.get("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}/versions/#{name_args[1].tr('.', '_')}")
+ noauth_rest.get("#{supermarket_uri}/cookbooks/#{@name_args[0]}/versions/#{name_args[1].tr('.', '_')}")
end
end
def get_cookbook_list(items = 10, start = 0, cookbook_collection = {})
- cookbooks_url = "https://supermarket.chef.io/api/v1/cookbooks?items=#{items}&start=#{start}"
+ cookbooks_url = "#{supermarket_uri}/cookbooks?items=#{items}&start=#{start}"
cr = noauth_rest.get(cookbooks_url)
cr["items"].each do |cookbook|
cookbook_collection[cookbook["cookbook_name"]] = cookbook
diff --git a/lib/chef/knife/cookbook_site_unshare.rb b/lib/chef/knife/cookbook_site_unshare.rb
index 310f6ac41d..bdabff0b94 100644
--- a/lib/chef/knife/cookbook_site_unshare.rb
+++ b/lib/chef/knife/cookbook_site_unshare.rb
@@ -30,6 +30,13 @@ class Chef
banner "knife cookbook site unshare COOKBOOK"
category "cookbook site"
+ option :supermarket_site,
+ :short => "-m SUPERMARKET_SITE",
+ :long => "--supermarket-site SUPERMARKET_SITE",
+ :description => "Supermarket Site",
+ :default => "https://supermarket.chef.io",
+ :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
def run
@cookbook_name = @name_args[0]
if @cookbook_name.nil?
@@ -41,7 +48,7 @@ class Chef
confirm "Do you really want to unshare all versions of the cookbook #{@cookbook_name}"
begin
- rest.delete "https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}"
+ rest.delete "#{config[:supermarket_site]}/api/v1/cookbooks/#{@name_args[0]}"
rescue Net::HTTPServerException => e
raise e unless e.message =~ /Forbidden/
ui.error "Forbidden: You must be the maintainer of #{@cookbook_name} to unshare it."
diff --git a/lib/chef/knife/cookbook_test.rb b/lib/chef/knife/cookbook_test.rb
index 1a5c9df74e..f3a4981c95 100644
--- a/lib/chef/knife/cookbook_test.rb
+++ b/lib/chef/knife/cookbook_test.rb
@@ -2,7 +2,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Matthew Kent (<mkent@magoazul.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2018, Chef Software Inc.
# Copyright:: Copyright 2010-2016, Matthew Kent
# License:: Apache License, Version 2.0
#
@@ -43,14 +43,14 @@ class Chef
:description => "Test all cookbooks, rather than just a single cookbook"
def run
- ui.warn("DEPRECATED: Please use ChefSpec or Rubocop to syntax-check cookbooks.")
+ ui.warn("DEPRECATED: Please use ChefSpec or Cookstyle to syntax-check cookbooks.")
config[:cookbook_path] ||= Chef::Config[:cookbook_path]
checked_a_cookbook = false
if config[:all]
cl = cookbook_loader
cl.load_cookbooks
- cl.each do |key, cookbook|
+ cl.each_key do |key|
checked_a_cookbook = true
test_cookbook(key)
end
diff --git a/lib/chef/knife/cookbook_upload.rb b/lib/chef/knife/cookbook_upload.rb
index 6938ac280d..f67a26dc9a 100644
--- a/lib/chef/knife/cookbook_upload.rb
+++ b/lib/chef/knife/cookbook_upload.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Nuo Yan (<yan.nuo@gmail.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -146,12 +146,12 @@ class Chef
end
if upload_failures == 0
- ui.info "Uploaded #{upload_ok} cookbook#{upload_ok > 1 ? "s" : ""}."
+ ui.info "Uploaded #{upload_ok} cookbook#{upload_ok == 1 ? "" : "s"}."
elsif upload_failures > 0 && upload_ok > 0
- ui.warn "Uploaded #{upload_ok} cookbook#{upload_ok > 1 ? "s" : ""} ok but #{upload_failures} " +
- "cookbook#{upload_failures > 1 ? "s" : ""} upload failed."
+ ui.warn "Uploaded #{upload_ok} cookbook#{upload_ok == 1 ? "" : "s"} ok but #{upload_failures} " +
+ "cookbook#{upload_failures == 1 ? "" : "s"} upload failed."
elsif upload_failures > 0 && upload_ok == 0
- ui.error "Failed to upload #{upload_failures} cookbook#{upload_failures > 1 ? "s" : ""}."
+ ui.error "Failed to upload #{upload_failures} cookbook#{upload_failures == 1 ? "" : "s"}."
exit 1
end
end
@@ -172,7 +172,7 @@ class Chef
if ! upload_set.has_key?(cookbook_name)
upload_set[cookbook_name] = cookbook_repo[cookbook_name]
if config[:depends]
- upload_set[cookbook_name].metadata.dependencies.each { |dep, ver| @name_args << dep }
+ upload_set[cookbook_name].metadata.dependencies.each_key { |dep| @name_args << dep }
end
end
rescue Exceptions::CookbookNotFoundInRepo => e
diff --git a/lib/chef/knife/core/bootstrap_context.rb b/lib/chef/knife/core/bootstrap_context.rb
index 48d2cb9e77..deb7f8a3b4 100644
--- a/lib/chef/knife/core/bootstrap_context.rb
+++ b/lib/chef/knife/core/bootstrap_context.rb
@@ -54,7 +54,7 @@ class Chef
end
def client_d
- @cliend_d ||= client_d_content
+ @client_d ||= client_d_content
end
def encrypted_data_bag_secret
@@ -67,12 +67,36 @@ class Chef
@trusted_certs ||= trusted_certs_content
end
+ def get_log_location
+ if !(@chef_config[:config_log_location].class == IO ) && (@chef_config[:config_log_location].nil? || @chef_config[:config_log_location].to_s.empty?)
+ "STDOUT"
+ elsif @chef_config[:config_log_location].equal?(:win_evt)
+ raise "The value :win_evt is not supported for config_log_location on Linux Platforms \n"
+ elsif @chef_config[:config_log_location].equal?(:syslog)
+ ":syslog"
+ elsif @chef_config[:config_log_location].equal?(STDOUT)
+ "STDOUT"
+ elsif @chef_config[:config_log_location].equal?(STDERR)
+ "STDERR"
+ elsif @chef_config[:config_log_location]
+ %Q{"#{@chef_config[:config_log_location]}"}
+ else
+ "STDOUT"
+ end
+ end
+
def config_content
client_rb = <<-CONFIG
-log_location STDOUT
chef_server_url "#{@chef_config[:chef_server_url]}"
validation_client_name "#{@chef_config[:validation_client_name]}"
CONFIG
+
+ if !(@chef_config[:config_log_level].nil? || @chef_config[:config_log_level].empty?)
+ client_rb << %Q{log_level :#{@chef_config[:config_log_level]}\n}
+ end
+
+ client_rb << "log_location #{get_log_location}\n"
+
if @config[:chef_node_name]
client_rb << %Q{node_name "#{@config[:chef_node_name]}"\n}
else
@@ -114,6 +138,16 @@ validation_client_name "#{@chef_config[:validation_client_name]}"
client_rb << %Q{https_proxy "#{knife_config[:bootstrap_proxy]}"\n}
end
+ if knife_config[:bootstrap_proxy_user]
+ client_rb << %Q{http_proxy_user "#{knife_config[:bootstrap_proxy_user]}"\n}
+ client_rb << %Q{https_proxy_user "#{knife_config[:bootstrap_proxy_user]}"\n}
+ end
+
+ if knife_config[:bootstrap_proxy_pass]
+ client_rb << %Q{http_proxy_pass "#{knife_config[:bootstrap_proxy_pass]}"\n}
+ client_rb << %Q{https_proxy_pass "#{knife_config[:bootstrap_proxy_pass]}"\n}
+ end
+
if knife_config[:bootstrap_no_proxy]
client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n}
end
@@ -127,13 +161,14 @@ validation_client_name "#{@chef_config[:validation_client_name]}"
end
if Chef::Config[:fips]
- client_rb << <<-CONFIG
-fips true
-chef_version = ::Chef::VERSION.split(".")
-unless chef_version[0].to_i > 12 || (chef_version[0].to_i == 12 && chef_version[1].to_i >= 8)
- raise "FIPS Mode requested but not supported by this client"
-end
-CONFIG
+ client_rb << <<-CONFIG.gsub(/^ {14}/, "")
+ fips true
+ require "chef/version"
+ chef_version = ::Chef::VERSION.split(".")
+ unless chef_version[0].to_i > 12 || (chef_version[0].to_i == 12 && chef_version[1].to_i >= 8)
+ raise "FIPS Mode requested but not supported by this client"
+ end
+ CONFIG
end
client_rb
@@ -143,7 +178,11 @@ CONFIG
# If the user doesn't have a client path configure, let bash use the PATH for what it was designed for
client_path = @chef_config[:chef_client_path] || "chef-client"
s = "#{client_path} -j /etc/chef/first-boot.json"
- s << " -l debug" if @config[:verbosity] && @config[:verbosity] >= 2
+ if @config[:verbosity] && @config[:verbosity] >= 3
+ s << " -l trace"
+ elsif @config[:verbosity] && @config[:verbosity] >= 2
+ s << " -l debug"
+ end
s << " -E #{bootstrap_environment}" unless bootstrap_environment.nil?
s << " --no-color" unless @config[:color]
s
@@ -188,6 +227,7 @@ CONFIG
attributes[:run_list] = @run_list
end
+ attributes.delete(:run_list) if attributes[:policy_name] && !attributes[:policy_name].empty?
attributes.merge!(:tags => @config[:tags]) if @config[:tags] && !@config[:tags].empty?
end
end
@@ -219,7 +259,7 @@ CONFIG
content << "mkdir #{file_on_node}\n"
else
content << "cat > #{file_on_node} <<'EOP'\n" +
- f.read + "\nEOP\n"
+ f.read.gsub("'", "'\\\\''") + "\nEOP\n"
end
end
end
diff --git a/lib/chef/knife/core/cookbook_scm_repo.rb b/lib/chef/knife/core/cookbook_scm_repo.rb
index e909066b02..38f432e5bb 100644
--- a/lib/chef/knife/core/cookbook_scm_repo.rb
+++ b/lib/chef/knife/core/cookbook_scm_repo.rb
@@ -122,7 +122,7 @@ class Chef
git("branch --no-color").stdout.lines.any? { |l| l =~ /\s#{Regexp.escape(branch_name)}(?:\s|$)/ }
end
- def get_current_branch()
+ def get_current_branch
ref = git("symbolic-ref HEAD").stdout
ref.chomp.split("/")[2]
end
@@ -131,9 +131,9 @@ class Chef
def git_repo?(directory)
if File.directory?(File.join(directory, ".git"))
- return true
+ true
elsif File.dirname(directory) == directory
- return false
+ false
else
git_repo?(File.dirname(directory))
end
diff --git a/lib/chef/knife/core/custom_manifest_loader.rb b/lib/chef/knife/core/custom_manifest_loader.rb
deleted file mode 100644
index 9fe51599af..0000000000
--- a/lib/chef/knife/core/custom_manifest_loader.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/version"
-class Chef
- class Knife
- class SubcommandLoader
-
- #
- # Load a subcommand from a user-supplied
- # manifest file
- #
- class CustomManifestLoader < Chef::Knife::SubcommandLoader
- attr_accessor :manifest
- def initialize(chef_config_dir, plugin_manifest)
- super(chef_config_dir)
- @manifest = plugin_manifest
- end
-
- # If the user has created a ~/.chef/plugin_manifest.json file, we'll use
- # that instead of inspecting the on-system gems to find the plugins. The
- # file format is expected to look like:
- #
- # { "plugins": {
- # "knife-ec2": {
- # "paths": [
- # "/home/alice/.rubymanagerthing/gems/knife-ec2-x.y.z/lib/chef/knife/ec2_server_create.rb",
- # "/home/alice/.rubymanagerthing/gems/knife-ec2-x.y.z/lib/chef/knife/ec2_server_delete.rb"
- # ]
- # }
- # }
- # }
- #
- # Extraneous content in this file is ignored. This is intentional so that we
- # can adapt the file format for potential behavior changes to knife in
- # the future.
- def find_subcommands_via_manifest
- # Format of subcommand_files is "relative_path" (something you can
- # Kernel.require()) => full_path. The relative path isn't used
- # currently, so we just map full_path => full_path.
- subcommand_files = {}
- manifest["plugins"].each do |plugin_name, plugin_manifest|
- plugin_manifest["paths"].each do |cmd_path|
- subcommand_files[cmd_path] = cmd_path
- end
- end
- subcommand_files.merge(find_subcommands_via_dirglob)
- end
-
- def subcommand_files
- @subcommand_files ||= (find_subcommands_via_manifest.values + site_subcommands).flatten.uniq
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/core/gem_glob_loader.rb b/lib/chef/knife/core/gem_glob_loader.rb
index f5eff26903..c4523d69ad 100644
--- a/lib/chef/knife/core/gem_glob_loader.rb
+++ b/lib/chef/knife/core/gem_glob_loader.rb
@@ -81,9 +81,9 @@ class Chef
files = []
if check_load_path
- files = $LOAD_PATH.map { |load_path|
+ files = $LOAD_PATH.map do |load_path|
Dir["#{File.expand_path glob, Chef::Util::PathHelper.escape_glob_dir(load_path)}#{Gem.suffix_pattern}"]
- }.flatten.select { |file| File.file? file.untaint }
+ end.flatten.select { |file| File.file? file.untaint }
end
gem_files = latest_gem_specs.map do |spec|
@@ -98,7 +98,7 @@ class Chef
files.concat gem_files
files.uniq! if check_load_path
- return files
+ files
end
def latest_gem_specs
@@ -110,7 +110,7 @@ class Chef
end
def check_spec_for_glob(spec, glob)
- dirs = if spec.require_paths.size > 1 then
+ dirs = if spec.require_paths.size > 1
"{#{spec.require_paths.join(',')}}"
else
spec.require_paths.first
diff --git a/lib/chef/knife/core/generic_presenter.rb b/lib/chef/knife/core/generic_presenter.rb
index f273cb5bca..861bf1510d 100644
--- a/lib/chef/knife/core/generic_presenter.rb
+++ b/lib/chef/knife/core/generic_presenter.rb
@@ -28,12 +28,19 @@ class Chef
# :nodoc:
def self.included(includer)
includer.class_eval do
- @attrs_to_show = []
+ option :field_separator,
+ :short => "-S SEPARATOR",
+ :long => "--field-separator SEPARATOR",
+ :description => "Character separator used to delineate nesting in --attribute filters (default \".\")"
+
option :attribute,
:short => "-a ATTR1 [-a ATTR2]",
:long => "--attribute ATTR1 [--attribute ATTR2] ",
- :proc => lambda { |val| @attrs_to_show << val },
- :description => "Show one or more attributes"
+ :description => "Show one or more attributes",
+ :proc => Proc.new { |a|
+ Chef::Config[:knife][:attribute] ||= []
+ Chef::Config[:knife][:attribute].push(a)
+ }
end
end
end
@@ -173,25 +180,28 @@ class Chef
config[:attribute] || config[:run_list]
end
+ # GenericPresenter is used in contexts where MultiAttributeReturnOption
+ # is not, so we need to set the default value here rather than as part
+ # of the CLI option.
+ def attribute_field_separator
+ config[:field_separator] || "."
+ end
+
def extract_nested_value(data, nested_value_spec)
- nested_value_spec.split(".").each do |attr|
- if data.nil?
- nil # don't get no method error on nil
- # Must check :[] before attr because spec can include
- # `keys` - want the key named `keys`, not a list of
- # available keys.
- elsif data.respond_to?(:[]) && data.has_key?(attr)
- data = data[attr]
- elsif data.respond_to?(attr.to_sym)
- data = data.send(attr.to_sym)
- else
- data = begin
- data.send(attr.to_sym)
- rescue NoMethodError
- nil
- end
- end
+ nested_value_spec.split(attribute_field_separator).each do |attr|
+ data =
+ if data.is_a?(Array)
+ data[attr.to_i]
+ elsif data.respond_to?(:[], false) && data.key?(attr)
+ data[attr]
+ elsif data.respond_to?(attr.to_sym, false)
+ # handles -a chef_environment and other things that hang of the node and aren't really attributes
+ data.public_send(attr.to_sym)
+ else
+ nil
+ end
end
+ # necessary (?) for coercing objects (the run_list object?) to hashes
( !data.kind_of?(Array) && data.respond_to?(:to_hash) ) ? data.to_hash : data
end
diff --git a/lib/chef/knife/core/status_presenter.rb b/lib/chef/knife/core/status_presenter.rb
index 68c1acf4f1..1e2d9b41b6 100644
--- a/lib/chef/knife/core/status_presenter.rb
+++ b/lib/chef/knife/core/status_presenter.rb
@@ -101,29 +101,38 @@ class Chef
fqdn = (node[:ec2] && node[:ec2][:public_hostname]) || node[:fqdn]
name = node["name"] || node.name
- hours, minutes, = time_difference_in_hms(node["ohai_time"])
- hours_text = "#{hours} hour#{hours == 1 ? ' ' : 's'}"
- minutes_text = "#{minutes} minute#{minutes == 1 ? ' ' : 's'}"
run_list = "#{node['run_list']}" if config[:run_list]
- if hours > 24
- color = :red
- text = hours_text
- elsif hours >= 1
- color = :yellow
- text = hours_text
+ line_parts = Array.new
+
+ if node["ohai_time"]
+ hours, minutes, seconds = time_difference_in_hms(node["ohai_time"])
+ hours_text = "#{hours} hour#{hours == 1 ? ' ' : 's'}"
+ minutes_text = "#{minutes} minute#{minutes == 1 ? ' ' : 's'}"
+ seconds_text = "#{seconds} second#{seconds == 1 ? ' ' : 's'}"
+ if hours > 24
+ color = :red
+ text = hours_text
+ elsif hours >= 1
+ color = :yellow
+ text = hours_text
+ elsif minutes >= 1
+ color = :green
+ text = minutes_text
+ else
+ color = :green
+ text = seconds_text
+ end
+ line_parts << @ui.color(text, color) + " ago" << name
else
- color = :green
- text = minutes_text
+ line_parts << "Node #{name} has not yet converged"
end
- line_parts = Array.new
- line_parts << @ui.color(text, color) + " ago" << name
line_parts << fqdn if fqdn
line_parts << ip if ip
line_parts << run_list if run_list
if node["platform"]
- platform = node["platform"]
+ platform = node["platform"].dup
if node["platform_version"]
platform << " #{node['platform_version']}"
end
@@ -148,7 +157,7 @@ class Chef
difference = difference % 3600
minutes = (difference / 60).to_i
seconds = (difference % 60)
- return [hours, minutes, seconds]
+ [hours, minutes, seconds]
end
end
diff --git a/lib/chef/knife/core/subcommand_loader.rb b/lib/chef/knife/core/subcommand_loader.rb
index e617b39ded..026967d6ec 100644
--- a/lib/chef/knife/core/subcommand_loader.rb
+++ b/lib/chef/knife/core/subcommand_loader.rb
@@ -1,6 +1,6 @@
# Author:: Christopher Brown (<cb@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,7 +20,6 @@ require "chef/version"
require "chef/util/path_helper"
require "chef/knife/core/gem_glob_loader"
require "chef/knife/core/hashed_command_loader"
-require "chef/knife/core/custom_manifest_loader"
class Chef
class Knife
@@ -38,7 +37,6 @@ class Chef
#
class SubcommandLoader
attr_reader :chef_config_dir
- attr_reader :env
# A small factory method. Eventually, this is the only place
# where SubcommandLoader should know about its subclasses, but
@@ -48,11 +46,8 @@ class Chef
# or directly instantiate the appropriate subclass
def self.for_config(chef_config_dir)
if autogenerated_manifest?
- Chef::Log.debug("Using autogenerated hashed command manifest #{plugin_manifest_path}")
+ Chef::Log.trace("Using autogenerated hashed command manifest #{plugin_manifest_path}")
Knife::SubcommandLoader::HashedCommandLoader.new(chef_config_dir, plugin_manifest)
- elsif custom_manifest?
- Chef.log_deprecation("Using custom manifest #{plugin_manifest_path} is deprecated. Please use a `knife rehash` autogenerated manifest instead.")
- Knife::SubcommandLoader::CustomManifestLoader.new(chef_config_dir, plugin_manifest)
else
Knife::SubcommandLoader::GemGlobLoader.new(chef_config_dir)
end
@@ -72,10 +67,6 @@ class Chef
plugin_manifest? && plugin_manifest.key?(HashedCommandLoader::KEY)
end
- def self.custom_manifest?
- plugin_manifest? && plugin_manifest.key?("plugins")
- end
-
def self.plugin_manifest
Chef::JSONCompat.from_json(File.read(plugin_manifest_path))
end
@@ -84,14 +75,8 @@ class Chef
Chef::Util::PathHelper.home(".chef", "plugin_manifest.json")
end
- def initialize(chef_config_dir, env = nil)
+ def initialize(chef_config_dir)
@chef_config_dir = chef_config_dir
-
- # Deprecated and un-used instance variable.
- @env = env
- unless env.nil?
- Chef.log_deprecation("The env argument to Chef::Knife::SubcommandLoader is deprecated. If you are using env to inject/mock HOME, consider mocking Chef::Util::PathHelper.home instead.")
- end
end
# Load all the sub-commands
@@ -149,21 +134,6 @@ class Chef
end
#
- # Subclassses should define this themselves. Eventually, this will raise a
- # NotImplemented error, but for now, we mimic the behavior the user was likely
- # to get in the past.
- #
- def subcommand_files
- Chef.log_deprecation "Using Chef::Knife::SubcommandLoader directly is deprecated.
-Please use Chef::Knife::SubcommandLoader.for_config(chef_config_dir, env)"
- @subcommand_files ||= if Chef::Knife::SubcommandLoader.plugin_manifest?
- Chef::Knife::SubcommandLoader::CustomManifestLoader.new(chef_config_dir, env).subcommand_files
- else
- Chef::Knife::SubcommandLoader::GemGlobLoader.new(chef_config_dir, env).subcommand_files
- end
- end
-
- #
# Utility function for finding an element in a hash given an array
# of words and a separator. We find the the longest key in the
# hash composed of the given words joined by the separator.
diff --git a/lib/chef/knife/core/ui.rb b/lib/chef/knife/core/ui.rb
index 67e431f1a7..b2efbd8b8f 100644
--- a/lib/chef/knife/core/ui.rb
+++ b/lib/chef/knife/core/ui.rb
@@ -65,22 +65,18 @@ class Chef
# Prints a message to stdout. Aliased as +info+ for compatibility with
# the logger API.
def msg(message)
- begin
- stdout.puts message
- rescue Errno::EPIPE => e
- raise e if @config[:verbosity] >= 2
- exit 0
- end
+ stdout.puts message
+ rescue Errno::EPIPE => e
+ raise e if @config[:verbosity] >= 2
+ exit 0
end
# Prints a msg to stderr. Used for info, warn, error, and fatal.
def log(message)
- begin
- stderr.puts message
- rescue Errno::EPIPE => e
- raise e if @config[:verbosity] >= 2
- exit 0
- end
+ stderr.puts message
+ rescue Errno::EPIPE => e
+ raise e if @config[:verbosity] >= 2
+ exit 0
end
alias :info :log
@@ -138,7 +134,7 @@ class Chef
end
def ask_question(question, opts = {})
- question = question + "[#{opts[:default]}] " if opts[:default]
+ question += "[#{opts[:default]}] " if opts[:default]
if opts[:default] && config[:defaults]
opts[:default]
@@ -155,12 +151,10 @@ class Chef
end
def pretty_print(data)
- begin
- stdout.puts data
- rescue Errno::EPIPE => e
- raise e if @config[:verbosity] >= 2
- exit 0
- end
+ stdout.puts data
+ rescue Errno::EPIPE => e
+ raise e if @config[:verbosity] >= 2
+ exit 0
end
# Hash -> Hash
@@ -178,7 +172,7 @@ class Chef
tf.sync = true
tf.puts output
tf.close
- raise "Please set EDITOR environment variable" unless system("#{config[:editor]} #{tf.path}")
+ raise "Please set EDITOR environment variable. See https://docs.chef.io/knife_setup.html for details." unless system("#{config[:editor]} #{tf.path}")
output = IO.read(tf.path)
end
@@ -186,8 +180,7 @@ class Chef
if parse_output
if object_class.nil?
- Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please pass in the class to inflate or use #edit_hash")
- Chef::JSONCompat.from_json(output)
+ raise ArgumentError, "Please pass in the object class to hydrate or use #edit_hash"
else
object_class.from_hash(Chef::JSONCompat.parse(output))
end
@@ -215,9 +208,9 @@ class Chef
output_parsed_again = Chef::JSONCompat.parse(Chef::JSONCompat.to_json(output))
if object_parsed_again != output_parsed_again
output.save
- self.msg("Saved #{output}")
+ msg("Saved #{output}")
else
- self.msg("Object unchanged, not saving")
+ msg("Object unchanged, not saving")
end
output(format_for_display(object)) if config[:print_after]
end
@@ -247,19 +240,19 @@ class Chef
when "Y", "y"
true
when "N", "n"
- self.msg("You said no, so I'm done here.")
+ msg("You said no, so I'm done here.")
false
when ""
unless default_choice.nil?
default_choice
else
- self.msg("I have no idea what to do with '#{answer}'")
- self.msg("Just say Y or N, please.")
+ msg("I have no idea what to do with '#{answer}'")
+ msg("Just say Y or N, please.")
confirm_without_exit(question, append_instructions, default_choice)
end
else
- self.msg("I have no idea what to do with '#{answer}'")
- self.msg("Just say Y or N, please.")
+ msg("I have no idea what to do with '#{answer}'")
+ msg("Just say Y or N, please.")
confirm_without_exit(question, append_instructions, default_choice)
end
end
diff --git a/lib/chef/knife/data_bag_create.rb b/lib/chef/knife/data_bag_create.rb
index 196278bb80..563e931dca 100644
--- a/lib/chef/knife/data_bag_create.rb
+++ b/lib/chef/knife/data_bag_create.rb
@@ -49,13 +49,15 @@ class Chef
exit(1)
end
- # create the data bag
+ # Verify if the data bag exists
begin
+ rest.get("data/#{@data_bag_name}")
+ ui.info("Data bag #{@data_bag_name} already exists")
+ rescue Net::HTTPServerException => e
+ raise unless e.to_s =~ /^404/
+ # if it doesn't exists, try to create it
rest.post("data", { "name" => @data_bag_name })
ui.info("Created data_bag[#{@data_bag_name}]")
- rescue Net::HTTPServerException => e
- raise unless e.to_s =~ /^409/
- ui.info("Data bag #{@data_bag_name} already exists")
end
# if an item is specified, create it, as well
diff --git a/lib/chef/knife/data_bag_secret_options.rb b/lib/chef/knife/data_bag_secret_options.rb
index b426cd442c..a612004e15 100644
--- a/lib/chef/knife/data_bag_secret_options.rb
+++ b/lib/chef/knife/data_bag_secret_options.rb
@@ -95,7 +95,7 @@ class Chef
##
# Determine if the user has specified an appropriate secret for encrypting data bag items.
- # @returns boolean
+ # @return boolean
def base_encryption_secret_provided?(need_encrypt_flag = true)
validate_secrets
@@ -114,7 +114,7 @@ class Chef
# Certain situations (show and bootstrap) don't need a --encrypt flag to use the config file secret
return true
end
- return false
+ false
end
def has_cl_secret?
diff --git a/lib/chef/knife/data_bag_show.rb b/lib/chef/knife/data_bag_show.rb
index ea9c390f19..5df0561276 100644
--- a/lib/chef/knife/data_bag_show.rb
+++ b/lib/chef/knife/data_bag_show.rb
@@ -51,7 +51,7 @@ class Chef
ui.warn("Encrypted data bag detected, but no secret provided for decoding. Displaying encrypted data.")
format_for_display(raw_data)
else
- ui.warn("Unencrypted data bag detected, ignoring any provided secret options.")
+ ui.warn("Unencrypted data bag detected, ignoring any provided secret options.") if secret
format_for_display(raw_data)
end
diff --git a/lib/chef/knife/deps.rb b/lib/chef/knife/deps.rb
index e773f65106..eec92cc9a3 100644
--- a/lib/chef/knife/deps.rb
+++ b/lib/chef/knife/deps.rb
@@ -72,49 +72,47 @@ class Chef
end
def get_dependencies(entry)
- begin
- if entry.parent && entry.parent.path == "/cookbooks"
- return entry.chef_object.metadata.dependencies.keys.map { |cookbook| "/cookbooks/#{cookbook}" }
+ if entry.parent && entry.parent.path == "/cookbooks"
+ entry.chef_object.metadata.dependencies.keys.map { |cookbook| "/cookbooks/#{cookbook}" }
- elsif entry.parent && entry.parent.path == "/nodes"
- node = Chef::JSONCompat.parse(entry.read)
- result = []
- if node["chef_environment"] && node["chef_environment"] != "_default"
- result << "/environments/#{node['chef_environment']}.json"
- end
- if node["run_list"]
- result += dependencies_from_runlist(node["run_list"])
- end
- result
+ elsif entry.parent && entry.parent.path == "/nodes"
+ node = Chef::JSONCompat.parse(entry.read)
+ result = []
+ if node["chef_environment"] && node["chef_environment"] != "_default"
+ result << "/environments/#{node['chef_environment']}.json"
+ end
+ if node["run_list"]
+ result += dependencies_from_runlist(node["run_list"])
+ end
+ result
- elsif entry.parent && entry.parent.path == "/roles"
- role = Chef::JSONCompat.parse(entry.read)
- result = []
- if role["run_list"]
- dependencies_from_runlist(role["run_list"]).each do |dependency|
- result << dependency if !result.include?(dependency)
- end
+ elsif entry.parent && entry.parent.path == "/roles"
+ role = Chef::JSONCompat.parse(entry.read)
+ result = []
+ if role["run_list"]
+ dependencies_from_runlist(role["run_list"]).each do |dependency|
+ result << dependency if !result.include?(dependency)
end
- if role["env_run_lists"]
- role["env_run_lists"].each_pair do |env, run_list|
- dependencies_from_runlist(run_list).each do |dependency|
- result << dependency if !result.include?(dependency)
- end
+ end
+ if role["env_run_lists"]
+ role["env_run_lists"].each_pair do |env, run_list|
+ dependencies_from_runlist(run_list).each do |dependency|
+ result << dependency if !result.include?(dependency)
end
end
- result
+ end
+ result
- elsif !entry.exists?
- raise Chef::ChefFS::FileSystem::NotFoundError.new(entry)
+ elsif !entry.exists?
+ raise Chef::ChefFS::FileSystem::NotFoundError.new(entry)
- else
- []
- end
- rescue Chef::ChefFS::FileSystem::NotFoundError => e
- ui.error "#{format_path(e.entry)}: No such file or directory"
- self.exit_code = 2
+ else
[]
end
+ rescue Chef::ChefFS::FileSystem::NotFoundError => e
+ ui.error "#{format_path(e.entry)}: No such file or directory"
+ self.exit_code = 2
+ []
end
def dependencies_from_runlist(run_list)
diff --git a/lib/chef/knife/edit.rb b/lib/chef/knife/edit.rb
index 8489e4e179..4d7338f9f6 100644
--- a/lib/chef/knife/edit.rb
+++ b/lib/chef/knife/edit.rb
@@ -9,7 +9,7 @@ class Chef
deps do
require "chef/chef_fs/file_system"
- require "chef/chef_fs/file_system/not_found_error"
+ require "chef/chef_fs/file_system/exceptions"
end
option :local,
@@ -58,7 +58,7 @@ class Chef
# Let the user edit the temporary file
if !system("#{config[:editor]} #{file.path}")
- raise "Please set EDITOR environment variable. See https://docs.chef.io/knife_using.html for details."
+ raise "Please set EDITOR environment variable. See https://docs.chef.io/knife_setup.html for details."
end
result_text = IO.read(file.path)
diff --git a/lib/chef/knife/environment_compare.rb b/lib/chef/knife/environment_compare.rb
index 8a2ef853d3..9131f06068 100644
--- a/lib/chef/knife/environment_compare.rb
+++ b/lib/chef/knife/environment_compare.rb
@@ -81,7 +81,7 @@ class Chef
def constraint_list(environments)
constraints = {}
- environments.each do |env, url|
+ environments.each do |env, url| # rubocop:disable Performance/HashEachMethods
# Because you cannot modify the default environment I filter it out here.
unless env == "_default"
envdata = Chef::Environment.load(env)
@@ -94,17 +94,17 @@ class Chef
def cookbook_list(constraints)
result = {}
- constraints.each { |env, cb| result.merge!(cb) }
+ constraints.each_value { |cb| result.merge!(cb) }
result
end
def matrix_output(cookbooks, constraints)
rows = [ "" ]
environments = []
- constraints.each { |e, v| environments << e.to_s }
+ constraints.each_key { |e| environments << e.to_s }
columns = environments.count + 1
environments.each { |env| rows << ui.color(env, :bold) }
- cookbooks.each do |c, v|
+ cookbooks.each_key do |c|
total = []
environments.each { |n| total << constraints[n][c] }
if total.uniq.count == 1
diff --git a/lib/chef/knife/exec.rb b/lib/chef/knife/exec.rb
index 0aa8ea2ba8..7b27a51b85 100644
--- a/lib/chef/knife/exec.rb
+++ b/lib/chef/knife/exec.rb
@@ -68,15 +68,15 @@ class Chef::Knife::Exec < Chef::Knife
# Failing that, try searching the script path. If we can't find
# anything, fail gracefully.
- Chef::Log.debug("Searching script_path: #{config[:script_path].inspect}")
+ Chef::Log.trace("Searching script_path: #{config[:script_path].inspect}")
config[:script_path].each do |path|
path = File.expand_path(path)
test = File.join(path, x)
- Chef::Log.debug("Testing: #{test}")
+ Chef::Log.trace("Testing: #{test}")
if File.exists?(test)
script = test
- Chef::Log.debug("Found: #{test}")
+ Chef::Log.trace("Found: #{test}")
return script
end
end
diff --git a/lib/chef/knife/help.rb b/lib/chef/knife/help.rb
deleted file mode 100644
index e45b54eec8..0000000000
--- a/lib/chef/knife/help.rb
+++ /dev/null
@@ -1,101 +0,0 @@
-#
-# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-class Chef
- class Knife
- class Help < Chef::Knife
-
- banner "knife help [list|TOPIC]"
-
- def run
- if name_args.empty?
- ui.info "Usage: knife SUBCOMMAND (options)"
- ui.msg ""
- # This command is atypical, the user is likely not interested in usage of
- # this command, but knife in general. So hack the banner.
- opt_parser.banner = "General Knife Options:"
- ui.msg opt_parser.to_s
- ui.msg ""
- ui.info "For further help:"
- ui.info(<<-MOAR_HELP)
- knife help list list help topics
- knife help knife show general knife help
- knife help TOPIC display the manual for TOPIC
- knife SUBCOMMAND --help show the options for a command
-MOAR_HELP
- exit 1
- else
- @query = name_args.join("-")
- end
-
- case @query
- when "topics", "list"
- print_help_topics
- exit 1
- when "intro", "knife"
- @topic = "knife"
- else
- @topic = find_manpages_for_query(@query)
- end
-
- manpage_path = find_manpage_path(@topic)
- exec "man #{manpage_path}"
- end
-
- def help_topics
- # The list of help topics is generated by a rake task from the available man pages
- # This constant is provided in help_topics.rb which is automatically required/loaded by the knife subcommand loader.
- HELP_TOPICS
- end
-
- def print_help_topics
- ui.info "Available help topics are: "
- help_topics.collect { |t| t.gsub(/knife-/, "") }.sort.each do |topic|
- ui.msg " #{topic}"
- end
- end
-
- def find_manpages_for_query(query)
- possibilities = help_topics.select do |manpage|
- ::File.fnmatch("knife-#{query}*", manpage) || ::File.fnmatch("#{query}*", manpage)
- end
- if possibilities.empty?
- ui.error "No help found for '#{query}'"
- ui.msg ""
- print_help_topics
- exit 1
- elsif possibilities.size == 1
- possibilities.first
- else
- ui.info "Multiple help topics match your query. Pick one:"
- ui.highline.choose(*possibilities)
- end
- end
-
- def find_manpage_path(topic)
- if ::File.exists?(::File.expand_path("../distro/common/man/man1/#{topic}.1", CHEF_ROOT))
- # If we've provided the man page in the gem, give that
- return ::File.expand_path("../distro/common/man/man1/#{topic}.1", CHEF_ROOT)
- else
- # Otherwise, we'll just be using MANPATH
- topic
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/help_topics.rb b/lib/chef/knife/help_topics.rb
deleted file mode 100644
index a2aad65f55..0000000000
--- a/lib/chef/knife/help_topics.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# Do not edit this file by hand
-# This file is autogenerated by the docs:list rake task from the available manpages
-
-HELP_TOPICS = ["chef-shell", "knife-bootstrap", "knife-client", "knife-configure", "knife-cookbook-site", "knife-cookbook", "knife-data-bag", "knife-delete", "knife-deps", "knife-diff", "knife-download", "knife-edit", "knife-environment", "knife-exec", "knife-index-rebuild", "knife-list", "knife-node", "knife-raw", "knife-recipe-list", "knife-role", "knife-search", "knife-show", "knife-ssh", "knife-status", "knife-tag", "knife-upload", "knife-user", "knife-xargs", "knife"]
diff --git a/lib/chef/knife/index_rebuild.rb b/lib/chef/knife/index_rebuild.rb
deleted file mode 100644
index 206b7b0fbf..0000000000
--- a/lib/chef/knife/index_rebuild.rb
+++ /dev/null
@@ -1,133 +0,0 @@
-#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2009-2016, Daniel DeLeo
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/knife"
-
-class Chef
- class Knife
- class IndexRebuild < Knife
-
- banner "knife index rebuild (options)"
- option :yes,
- :short => "-y",
- :long => "--yes",
- :boolean => true,
- :description => "don't bother to ask if I'm sure"
-
- def run
- api_info = grab_api_info
-
- if unsupported_version?(api_info)
- unsupported_server_message(api_info)
- exit 1
- else
- deprecated_server_message
- nag
- output rest.post("/search/reindex", {})
- end
- end
-
- def grab_api_info
- # Since we don't yet have any endpoints that implement an
- # OPTIONS handler, we need to get our version header
- # information in a more roundabout way. We'll try to query
- # for a node we know won't exist; the 404 response that comes
- # back will give us what we want
- dummy_node = "knife_index_rebuild_test_#{rand(1000000)}"
- rest.get("/nodes/#{dummy_node}")
- rescue Net::HTTPServerException => exception
- r = exception.response
- parse_api_info(r)
- end
-
- # Only Chef 11+ servers will have version information in their
- # headers, and only those servers will lack an API endpoint for
- # index rebuilding.
- def unsupported_version?(api_info)
- !!api_info["version"]
- end
-
- def unsupported_server_message(api_info)
- ui.error("Rebuilding the index is not available via knife for #{server_type(api_info)}s version 11.0.0 and above.")
- ui.info("Instead, run the '#{ctl_command(api_info)} reindex' command on the server itself.")
- end
-
- def deprecated_server_message
- ui.warn("'knife index rebuild' has been removed for Chef 11+ servers. It will continue to work for prior versions, however.")
- end
-
- def nag
- ui.info("This operation is destructive. Rebuilding the index may take some time.")
- ui.confirm("Continue")
- end
-
- # Chef 11 (and above) servers return various pieces of
- # information about the server in an +x-ops-api-info+ header.
- # This is a +;+ delimited string of key / value pairs, separated
- # by +=+.
- #
- # Given a Net::HTTPResponse object, this method extracts this
- # information (if present), and returns it as a hash. If no
- # such header is found, an empty hash is returned.
- def parse_api_info(response)
- value = response["x-ops-api-info"]
- if value
- kv = value.split(";")
- kv.inject({}) do |acc, pair|
- k, v = pair.split("=")
- acc[k] = v
- acc
- end
- else
- {}
- end
- end
-
- # Given an API info hash (see +#parse_api_info(response)+),
- # return a string describing the kind of server we're
- # interacting with (based on the +flavor+ field)
- def server_type(api_info)
- case api_info["flavor"]
- when "osc"
- "Open Source Chef Server"
- when "opc"
- "Private Chef Server"
- else
- # Generic fallback
- "Chef Server"
- end
- end
-
- # Given an API info hash (see +#parse_api_info(response)+),
- # return the name of the "server-ctl" command for the kind of
- # server we're interacting with (based on the +flavor+ field)
- def ctl_command(api_info)
- case api_info["flavor"]
- when "osc"
- "chef-server-ctl"
- when "opc"
- "private-chef-ctl"
- else
- # Generic fallback
- "chef-server-ctl"
- end
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/list.rb b/lib/chef/knife/list.rb
index 3d1583b270..e1a50b9360 100644
--- a/lib/chef/knife/list.rb
+++ b/lib/chef/knife/list.rb
@@ -44,7 +44,6 @@ class Chef
patterns = name_args.length == 0 ? [""] : name_args
# Get the top-level matches
- args = pattern_args_from(patterns)
all_results = parallelize(pattern_args_from(patterns)) do |pattern|
pattern_results = Chef::ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern).to_a
@@ -70,7 +69,7 @@ class Chef
# Flatten out directory results if necessary
if config[:flat]
- dir_results.each do |result, children|
+ dir_results.each do |result, children| # rubocop:disable Performance/HashEachMethods
results += children
end
dir_results = []
@@ -95,10 +94,10 @@ class Chef
printed_something = true
end
output "#{format_path(result)}:"
- print_results(children.map { |result| maybe_add_slash(result.name, result.dir?) }.sort, "")
+ print_results(children.map { |result| maybe_add_slash(result.display_name, result.dir?) }.sort, "")
end
- exit self.exit_code if self.exit_code
+ exit exit_code if exit_code
end
def add_dir_result(result)
diff --git a/lib/chef/knife/node_delete.rb b/lib/chef/knife/node_delete.rb
index 4dd7d764a1..52b8d122ca 100644
--- a/lib/chef/knife/node_delete.rb
+++ b/lib/chef/knife/node_delete.rb
@@ -27,18 +27,18 @@ class Chef
require "chef/json_compat"
end
- banner "knife node delete NODE (options)"
+ banner "knife node delete [NODE [NODE]] (options)"
def run
- @node_name = @name_args[0]
-
- if @node_name.nil?
+ if @name_args.length == 0
show_usage
- ui.fatal("You must specify a node name")
+ ui.fatal("You must specify at least one node name")
exit 1
end
- delete_object(Chef::Node, @node_name)
+ @name_args.each do |node_name|
+ delete_object(Chef::Node, node_name)
+ end
end
end
diff --git a/lib/chef/knife/node_policy_set.rb b/lib/chef/knife/node_policy_set.rb
new file mode 100644
index 0000000000..404446fe0e
--- /dev/null
+++ b/lib/chef/knife/node_policy_set.rb
@@ -0,0 +1,79 @@
+#
+# Author:: Piyush Awasthi (<piyush.awasthi@chef.io>)
+# Copyright:: Copyright 2017-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the License);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/knife"
+
+class Chef
+ class Knife
+ class NodePolicySet < Knife
+
+ deps do
+ require "chef/node"
+ require "chef/json_compat"
+ end
+
+ banner "knife node policy set NODE POLICY_GROUP POLICY_NAME (options)"
+
+ def run
+ validate_node!
+ validate_options!
+ node = Chef::Node.load(@name_args[0])
+ set_policy(node)
+ if node.save
+ ui.info "Successfully set the policy on node #{node.name}"
+ else
+ ui.info "Error in updating node #{node.name}"
+ end
+ end
+
+ private
+
+ # Set policy name and group to node
+ def set_policy(node)
+ policy_group, policy_name = @name_args[1..-1]
+ node.policy_name = policy_name
+ node.policy_group = policy_group
+ end
+
+ # Validate policy name and policy group
+ def validate_options!
+ if incomplete_policyfile_options?
+ ui.error("Policy group and name must be specified together")
+ exit 1
+ end
+ true
+ end
+
+ # Validate node pass in CLI
+ def validate_node!
+ if @name_args[0].nil?
+ ui.error("You must specify a node name")
+ show_usage
+ exit 1
+ end
+ end
+
+ # True if one of policy_name or policy_group was given, but not both
+ def incomplete_policyfile_options?
+ policy_group, policy_name = @name_args[1..-1]
+ (policy_group.nil? || policy_name.nil? || @name_args[1..-1].size > 2)
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/knife/node_run_list_add.rb b/lib/chef/knife/node_run_list_add.rb
index f8d40c8321..fb4ce3bc12 100644
--- a/lib/chef/knife/node_run_list_add.rb
+++ b/lib/chef/knife/node_run_list_add.rb
@@ -27,7 +27,7 @@ class Chef
require "chef/json_compat"
end
- banner "knife node run_list add [NODE] [ENTRY[,ENTRY]] (options)"
+ banner "knife node run_list add [NODE] [ENTRY [ENTRY]] (options)"
option :after,
:short => "-a ITEM",
diff --git a/lib/chef/knife/node_run_list_remove.rb b/lib/chef/knife/node_run_list_remove.rb
index 3f9cdabfff..df50818c23 100644
--- a/lib/chef/knife/node_run_list_remove.rb
+++ b/lib/chef/knife/node_run_list_remove.rb
@@ -27,7 +27,7 @@ class Chef
require "chef/json_compat"
end
- banner "knife node run_list remove [NODE] [ENTRY[,ENTRY]] (options)"
+ banner "knife node run_list remove [NODE] [ENTRY [ENTRY]] (options)"
def run
node = Chef::Node.load(@name_args[0])
diff --git a/lib/chef/knife/node_show.rb b/lib/chef/knife/node_show.rb
index c616b8ab72..3092b3fc27 100644
--- a/lib/chef/knife/node_show.rb
+++ b/lib/chef/knife/node_show.rb
@@ -55,11 +55,6 @@ class Chef
node = Chef::Node.load(@node_name)
output(format_for_display(node))
- self.class.attrs_to_show = []
- end
-
- def self.attrs_to_show=(attrs)
- @attrs_to_show = attrs
end
end
end
diff --git a/lib/chef/knife/osc_user_reregister.rb b/lib/chef/knife/osc_user_reregister.rb
index b513f31328..dee1cf41b1 100644
--- a/lib/chef/knife/osc_user_reregister.rb
+++ b/lib/chef/knife/osc_user_reregister.rb
@@ -49,7 +49,7 @@ class Chef
end
user = Chef::User.load(@user_name).reregister
- Chef::Log.debug("Updated user data: #{user.inspect}")
+ Chef::Log.trace("Updated user data: #{user.inspect}")
key = user.private_key
if config[:file]
File.open(config[:file], "w") do |f|
diff --git a/lib/chef/knife/osc_user_show.rb b/lib/chef/knife/osc_user_show.rb
index 22e9bf4dcd..5350837ad3 100644
--- a/lib/chef/knife/osc_user_show.rb
+++ b/lib/chef/knife/osc_user_show.rb
@@ -48,7 +48,6 @@ class Chef
user = Chef::User.load(@user_name)
output(format_for_display(user))
end
-
end
end
end
diff --git a/lib/chef/knife/role_env_run_list_add.rb b/lib/chef/knife/role_env_run_list_add.rb
index 61aec506a9..4a3bb70641 100644
--- a/lib/chef/knife/role_env_run_list_add.rb
+++ b/lib/chef/knife/role_env_run_list_add.rb
@@ -27,7 +27,7 @@ class Chef
require "chef/json_compat"
end
- banner "knife role env_run_list add [ROLE] [ENVIRONMENT] [ENTRY[,ENTRY]] (options)"
+ banner "knife role env_run_list add [ROLE] [ENVIRONMENT] [ENTRY [ENTRY]] (options)"
option :after,
:short => "-a ITEM",
diff --git a/lib/chef/knife/role_run_list_add.rb b/lib/chef/knife/role_run_list_add.rb
index 6aa92d37ba..7eaaa136bb 100644
--- a/lib/chef/knife/role_run_list_add.rb
+++ b/lib/chef/knife/role_run_list_add.rb
@@ -27,7 +27,7 @@ class Chef
require "chef/json_compat"
end
- banner "knife role run_list add [ROLE] [ENTRY[,ENTRY]] (options)"
+ banner "knife role run_list add [ROLE] [ENTRY [ENTRY]] (options)"
option :after,
:short => "-a ITEM",
diff --git a/lib/chef/knife/search.rb b/lib/chef/knife/search.rb
index 38d1ab3f42..94c33aa594 100644
--- a/lib/chef/knife/search.rb
+++ b/lib/chef/knife/search.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +18,7 @@
require "chef/knife"
require "chef/knife/core/node_presenter"
+require "addressable/uri"
class Chef
class Knife
@@ -36,12 +37,6 @@ class Chef
banner "knife search INDEX QUERY (options)"
- option :sort,
- :short => "-o SORT",
- :long => "--sort SORT",
- :description => "The order to sort the results in",
- :default => nil
-
option :start,
:short => "-b ROW",
:long => "--start ROW",
@@ -78,33 +73,34 @@ class Chef
def run
read_cli_args
- fuzzify_query
if @type == "node"
ui.use_presenter Knife::Core::NodePresenter
end
q = Chef::Search::Query.new
- escaped_query = URI.escape(@query,
- Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
result_items = []
result_count = 0
search_args = Hash.new
- search_args[:sort] = config[:sort] if config[:sort]
+ search_args[:fuzz] = true
search_args[:start] = config[:start] if config[:start]
search_args[:rows] = config[:rows] if config[:rows]
if config[:filter_result]
search_args[:filter_result] = create_result_filter(config[:filter_result])
elsif (not ui.config[:attribute].nil?) && (not ui.config[:attribute].empty?)
search_args[:filter_result] = create_result_filter_from_attributes(ui.config[:attribute])
+ elsif config[:id_only]
+ search_args[:filter_result] = create_result_filter_from_attributes([])
end
begin
- q.search(@type, escaped_query, search_args) do |item|
+ q.search(@type, @query, search_args) do |item|
formatted_item = Hash.new
- if item.is_a?(Hash)
+ if config[:id_only]
+ formatted_item = format_for_display({ "id" => item["__display_name"] })
+ elsif item.is_a?(Hash)
# doing a little magic here to set the correct name
formatted_item[item["__display_name"]] = item.reject { |k| k == "__display_name" }
else
@@ -116,7 +112,7 @@ class Chef
rescue Net::HTTPServerException => e
msg = Chef::JSONCompat.from_json(e.response.body)["error"].first
ui.error("knife search failed: #{msg}")
- exit 1
+ exit 99
end
if ui.interchange?
@@ -131,6 +127,9 @@ class Chef
end
end
end
+
+ # return a "failure" code to the shell so that knife search can be used in pipes similar to grep
+ exit 1 if result_count == 0
end
def read_cli_args
@@ -158,12 +157,6 @@ class Chef
end
end
- def fuzzify_query
- if @query !~ /:/
- @query = "tags:*#{@query}* OR roles:*#{@query}* OR fqdn:*#{@query}* OR addresses:*#{@query}* OR policy_name:*#{@query}* OR policy_group:*#{@query}*"
- end
- end
-
# This method turns a set of key value pairs in a string into the appropriate data structure that the
# chef-server search api is expecting.
# expected input is in the form of:
@@ -183,7 +176,7 @@ class Chef
return_id, attr_path = f.split("=")
final_filter[return_id.to_sym] = attr_path.split(".")
end
- return final_filter
+ final_filter
end
def create_result_filter_from_attributes(filter_array)
@@ -193,7 +186,7 @@ class Chef
end
# adding magic filter so we can actually pull the name as before
final_filter["__display_name"] = [ "name" ]
- return final_filter
+ final_filter
end
end
diff --git a/lib/chef/knife/ssh.rb b/lib/chef/knife/ssh.rb
index 2bbcbfc79e..c38dc43e74 100644
--- a/lib/chef/knife/ssh.rb
+++ b/lib/chef/knife/ssh.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,7 +26,6 @@ class Chef
deps do
require "net/ssh"
require "net/ssh/multi"
- require "chef/monkey_patches/net-ssh-multi"
require "readline"
require "chef/exceptions"
require "chef/search/query"
@@ -47,11 +46,10 @@ class Chef
:default => nil,
:proc => lambda { |o| o.to_i }
- option :attribute,
+ option :ssh_attribute,
:short => "-a ATTR",
:long => "--attribute ATTR",
- :description => "The attribute to use for opening the connection - default depends on the context",
- :proc => Proc.new { |key| Chef::Config[:knife][:ssh_attribute] = key.strip }
+ :description => "The attribute to use for opening the connection - default depends on the context"
option :manual,
:short => "-m",
@@ -60,6 +58,10 @@ class Chef
:description => "QUERY is a space separated list of servers",
:default => false
+ option :prefix_attribute,
+ :long => "--prefix-attribute ATTR",
+ :description => "The attribute to use for prefixing the ouput - default depends on the context"
+
option :ssh_user,
:short => "-x USERNAME",
:long => "--ssh-user USERNAME",
@@ -93,16 +95,16 @@ class Chef
:description => "The ssh gateway",
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key.strip }
+ option :ssh_gateway_identity,
+ :long => "--ssh-gateway-identity SSH_GATEWAY_IDENTITY",
+ :description => "The SSH identity file used for gateway authentication"
+
option :forward_agent,
:short => "-A",
:long => "--forward-agent",
:description => "Enable SSH agent forwarding",
:boolean => true
- option :identity_file,
- :long => "--identity-file IDENTITY_FILE",
- :description => "The SSH identity file used for authentication. [DEPRECATED] Use --ssh-identity-file instead."
-
option :ssh_identity_file,
:short => "-i IDENTITY_FILE",
:long => "--ssh-identity-file IDENTITY_FILE",
@@ -119,7 +121,13 @@ class Chef
:long => "--exit-on-error",
:description => "Immediately exit if an error is encountered",
:boolean => true,
- :proc => Proc.new { :raise }
+ :default => false
+
+ option :duplicated_fqdns,
+ :long => "--duplicated-fqdns",
+ :description => "Behavior if FQDNs are duplicated, ignored by default",
+ :proc => Proc.new { |key| Chef::Config[:knife][:duplicated_fqdns] = key.strip.to_sym },
+ :default => :ignore
option :tmux_split,
:long => "--tmux-split",
@@ -128,15 +136,13 @@ class Chef
:default => false
def session
- config[:on_error] ||= :skip
ssh_error_handler = Proc.new do |server|
- case config[:on_error]
- when :skip
- ui.warn "Failed to connect to #{server.host} -- #{$!.class.name}: #{$!.message}"
- $!.backtrace.each { |l| Chef::Log.debug(l) }
- when :raise
+ if config[:on_error]
#Net::SSH::Multi magic to force exception to be re-raised.
throw :go, :raise
+ else
+ ui.warn "Failed to connect to #{server.host} -- #{$!.class.name}: #{$!.message}"
+ $!.backtrace.each { |l| Chef::Log.debug(l) }
end
end
@@ -148,7 +154,7 @@ class Chef
if config[:ssh_gateway]
gw_host, gw_user = config[:ssh_gateway].split("@").reverse
gw_host, gw_port = gw_host.split(":")
- gw_opts = session_options(gw_host, gw_port, gw_user)
+ gw_opts = session_options(gw_host, gw_port, gw_user, gateway: true)
user = gw_opts.delete(:user)
begin
@@ -164,63 +170,92 @@ class Chef
end
def configure_session
- list = config[:manual] ?
- @name_args[0].split(" ") :
- search_nodes
+ list = config[:manual] ? @name_args[0].split(" ") : search_nodes
if list.length == 0
- if @action_nodes.length == 0
+ if @search_count == 0
ui.fatal("No nodes returned from search")
else
- ui.fatal("#{@action_nodes.length} #{@action_nodes.length > 1 ? "nodes" : "node"} found, " +
+ ui.fatal("#{@search_count} #{@search_count > 1 ? "nodes" : "node"} found, " +
"but does not have the required attribute to establish the connection. " +
"Try setting another attribute to open the connection using --attribute.")
end
exit 10
end
+ if %i{warn fatal}.include?(config[:duplicated_fqdns])
+ fqdns = list.map { |v| v[0] }
+ if fqdns.count != fqdns.uniq.count
+ duplicated_fqdns = fqdns.uniq
+ ui.send(config[:duplicated_fqdns],
+ "SSH #{duplicated_fqdns.count > 1 ? 'nodes are' : 'node is'} " +
+ "duplicated: #{duplicated_fqdns.join(',')}")
+ exit 10 if config[:duplicated_fqdns] == :fatal
+ end
+ end
session_from_list(list)
end
- def get_ssh_attribute(node)
+ def get_prefix_attribute(item)
+ # Order of precedence for prefix
+ # 1) config value (cli or knife config)
+ # 2) nil
+ msg = "Using node attribute '%s' as the prefix: %s"
+ if item["prefix"]
+ Chef::Log.debug(sprintf(msg, config[:prefix_attribute], item["prefix"]))
+ item["prefix"]
+ else
+ nil
+ end
+ end
+
+ def get_ssh_attribute(item)
# Order of precedence for ssh target
- # 1) command line attribute
- # 2) configuration file
- # 3) cloud attribute
- # 4) fqdn
- if config[:attribute]
- Chef::Log.debug("Using node attribute '#{config[:attribute]}' as the ssh target")
- attribute = config[:attribute]
- elsif Chef::Config[:knife][:ssh_attribute]
- Chef::Log.debug("Using node attribute #{Chef::Config[:knife][:ssh_attribute]}")
- attribute = Chef::Config[:knife][:ssh_attribute]
- elsif node[:cloud] &&
- node[:cloud][:public_hostname] &&
- !node[:cloud][:public_hostname].empty?
- Chef::Log.debug("Using node attribute 'cloud[:public_hostname]' automatically as the ssh target")
- attribute = "cloud.public_hostname"
+ # 1) config value (cli or knife config)
+ # 2) cloud attribute
+ # 3) fqdn
+ msg = "Using node attribute '%s' as the ssh target: %s"
+ if item["target"]
+ Chef::Log.debug(sprintf(msg, config[:ssh_attribute], item["target"]))
+ item["target"]
+ elsif !item.dig("cloud", "public_hostname").to_s.empty?
+ Chef::Log.debug(sprintf(msg, "cloud.public_hostname", item["cloud"]["public_hostname"]))
+ item["cloud"]["public_hostname"]
else
- # falling back to default of fqdn
- Chef::Log.debug("Using node attribute 'fqdn' as the ssh target")
- attribute = "fqdn"
+ Chef::Log.debug(sprintf(msg, "fqdn", item["fqdn"]))
+ item["fqdn"]
end
- attribute
end
def search_nodes
list = Array.new
query = Chef::Search::Query.new
- @action_nodes = query.search(:node, @name_args[0])[0]
- @action_nodes.each do |item|
+ required_attributes = { fqdn: ["fqdn"], cloud: ["cloud"] }
+
+ separator = ui.presenter.attribute_field_separator
+
+ if config[:prefix_attribute]
+ required_attributes[:prefix] = config[:prefix_attribute].split(separator)
+ end
+
+ if config[:ssh_attribute]
+ required_attributes[:target] = config[:ssh_attribute].split(separator)
+ end
+
+ @search_count = 0
+ query.search(:node, @name_args[0], filter_result: required_attributes, fuzz: true) do |item|
+ @search_count += 1
# we should skip the loop to next iteration if the item
# returned by the search is nil
next if item.nil?
# next if we couldn't find the specified attribute in the
# returned node object
- host = extract_nested_value(item, get_ssh_attribute(item))
+ host = get_ssh_attribute(item)
next if host.nil?
- ssh_port = item[:cloud].nil? ? nil : item[:cloud][:public_ssh_port]
- srv = [host, ssh_port]
+ prefix = get_prefix_attribute(item)
+ ssh_port = item.dig("cloud", "public_ssh_port")
+ srv = [host, ssh_port, prefix]
list.push(srv)
end
+
list
end
@@ -232,15 +267,19 @@ class Chef
# @param host [String] Hostname for this session.
# @param port [String] SSH port for this session.
# @param user [String] Optional username for this session.
+ # @param gateway [Boolean] Flag: host or gateway key
# @return [Hash<Symbol, Object>]
- def session_options(host, port, user = nil)
- ssh_config = Net::SSH.configuration_for(host)
+ def session_options(host, port, user = nil, gateway: false)
+ ssh_config = Net::SSH.configuration_for(host, true)
{}.tap do |opts|
# Chef::Config[:knife][:ssh_user] is parsed in #configure_user and written to config[:ssh_user]
opts[:user] = user || config[:ssh_user] || ssh_config[:user]
- if config[:ssh_identity_file]
+ if !gateway && config[:ssh_identity_file]
opts[:keys] = File.expand_path(config[:ssh_identity_file])
opts[:keys_only] = true
+ elsif gateway && config[:ssh_gateway_identity]
+ opts[:keys] = File.expand_path(config[:ssh_gateway_identity])
+ opts[:keys_only] = true
elsif config[:ssh_password]
opts[:password] = config[:ssh_password]
end
@@ -249,38 +288,45 @@ class Chef
opts[:forward_agent] = forward_agent unless forward_agent.nil?
port ||= ssh_config[:port]
opts[:port] = port unless port.nil?
- opts[:logger] = Chef::Log.logger if Chef::Log.level == :debug
+ opts[:logger] = Chef::Log.with_child(subsystem: "net/ssh") if Chef::Log.level == :trace
if !config[:host_key_verify]
- opts[:paranoid] = false
+ opts[:verify_host_key] = false
opts[:user_known_hosts_file] = "/dev/null"
end
+ if ssh_config[:keepalive]
+ opts[:keepalive] = true
+ opts[:keepalive_interval] = ssh_config[:keepalive_interval]
+ end
end
end
def session_from_list(list)
list.each do |item|
- host, ssh_port = item
+ host, ssh_port, prefix = item
+ prefix = host unless prefix
Chef::Log.debug("Adding #{host}")
- session_opts = session_options(host, ssh_port)
+ session_opts = session_options(host, ssh_port, gateway: false)
# Handle port overrides for the main connection.
session_opts[:port] = Chef::Config[:knife][:ssh_port] if Chef::Config[:knife][:ssh_port]
session_opts[:port] = config[:ssh_port] if config[:ssh_port]
# Handle connection timeout
session_opts[:timeout] = Chef::Config[:knife][:ssh_timeout] if Chef::Config[:knife][:ssh_timeout]
session_opts[:timeout] = config[:ssh_timeout] if config[:ssh_timeout]
+ # Handle session prefix
+ session_opts[:properties] = { prefix: prefix }
# Create the hostspec.
hostspec = session_opts[:user] ? "#{session_opts.delete(:user)}@#{host}" : host
# Connect a new session on the multi.
session.use(hostspec, session_opts)
- @longest = host.length if host.length > @longest
+ @longest = prefix.length if prefix.length > @longest
end
session
end
def fixup_sudo(command)
- command.sub(/^sudo/, 'sudo -p \'knife sudo password: \'')
+ command.sub(/^sudo/, "sudo -p 'knife sudo password: '")
end
def print_data(host, data)
@@ -311,19 +357,23 @@ class Chef
subsession ||= session
command = fixup_sudo(command)
command.force_encoding("binary") if command.respond_to?(:force_encoding)
- subsession.open_channel do |ch|
- ch.request_pty
- ch.exec command do |ch, success|
- raise ArgumentError, "Cannot execute #{command}" unless success
- ch.on_data do |ichannel, data|
- print_data(ichannel[:host], data)
- if data =~ /^knife sudo password: /
- print_data(ichannel[:host], "\n")
- ichannel.send_data("#{get_password}\n")
+ subsession.open_channel do |chan|
+ if config[:on_error] && exit_status != 0
+ chan.close()
+ else
+ chan.request_pty
+ chan.exec command do |ch, success|
+ raise ArgumentError, "Cannot execute #{command}" unless success
+ ch.on_data do |ichannel, data|
+ print_data(ichannel.connection[:prefix], data)
+ if data =~ /^knife sudo password: /
+ print_data(ichannel.connection[:prefix], "\n")
+ ichannel.send_data("#{get_password}\n")
+ end
+ end
+ ch.on_request "exit-status" do |ichannel, data|
+ exit_status = [exit_status, data.read_long].max
end
- end
- ch.on_request "exit-status" do |ichannel, data|
- exit_status = [exit_status, data.read_long].max
end
end
end
@@ -434,7 +484,7 @@ class Chef
end.join(" \\; ")
end
- tmux_name = "'knife ssh #{@name_args[0].tr(':', '=')}'"
+ tmux_name = "'knife ssh #{@name_args[0].tr(':.', '=-')}'"
begin
server = session.servers_for.first
cmd = ["tmux new-session -d -s #{tmux_name}",
@@ -530,12 +580,11 @@ class Chef
end
def configure_ssh_identity_file
- # config[:identity_file] is DEPRECATED in favor of :ssh_identity_file
- config[:ssh_identity_file] = get_stripped_unfrozen_value(config[:ssh_identity_file] || config[:identity_file] || Chef::Config[:knife][:ssh_identity_file])
+ config[:ssh_identity_file] = get_stripped_unfrozen_value(config[:ssh_identity_file] || Chef::Config[:knife][:ssh_identity_file])
end
- def extract_nested_value(data_structure, path_spec)
- ui.presenter.extract_nested_value(data_structure, path_spec)
+ def configure_ssh_gateway_identity
+ config[:ssh_gateway_identity] = get_stripped_unfrozen_value(config[:ssh_gateway_identity] || Chef::Config[:knife][:ssh_gateway_identity])
end
def run
@@ -543,29 +592,32 @@ class Chef
configure_user
configure_password
- configure_ssh_identity_file
+ @password = config[:ssh_password] if config[:ssh_password]
+
+ # If a password was not given, check for SSH identity file.
+ if !@password
+ configure_ssh_identity_file
+ configure_ssh_gateway_identity
+ end
+
configure_gateway
configure_session
exit_status =
- case @name_args[1]
- when "interactive"
- interactive
- when "screen"
- screen
- when "tmux"
- tmux
- when "macterm"
- macterm
- when "cssh"
- cssh
- when "csshx"
- Chef::Log.warn("knife ssh csshx will be deprecated in a future release")
- Chef::Log.warn("please use knife ssh cssh instead")
- cssh
- else
- ssh_command(@name_args[1..-1].join(" "))
- end
+ case @name_args[1]
+ when "interactive"
+ interactive
+ when "screen"
+ screen
+ when "tmux"
+ tmux
+ when "macterm"
+ macterm
+ when "cssh"
+ cssh
+ else
+ ssh_command(@name_args[1..-1].join(" "))
+ end
session.close
if exit_status != 0
diff --git a/lib/chef/knife/ssl_check.rb b/lib/chef/knife/ssl_check.rb
index 0c672f322e..c864ef52ec 100644
--- a/lib/chef/knife/ssl_check.rb
+++ b/lib/chef/knife/ssl_check.rb
@@ -44,7 +44,7 @@ class Chef
def uri
@uri ||= begin
- Chef::Log.debug("Checking SSL cert on #{given_uri}")
+ Chef::Log.trace("Checking SSL cert on #{given_uri}")
URI.parse(given_uri)
end
end
@@ -131,7 +131,7 @@ class Chef
true
rescue OpenSSL::SSL::SSLError => e
ui.error "The SSL certificate of #{host} could not be verified"
- Chef::Log.debug e.message
+ Chef::Log.trace e.message
debug_invalid_cert
false
end
@@ -141,7 +141,7 @@ class Chef
true
rescue OpenSSL::SSL::SSLError => e
ui.error "The SSL cert is signed by a trusted authority but is not valid for the given hostname"
- Chef::Log.debug(e)
+ Chef::Log.trace(e)
debug_invalid_host
false
end
@@ -257,7 +257,8 @@ ADVICE
def trusted_certificates
if configuration.trusted_certs_dir && Dir.exist?(configuration.trusted_certs_dir)
- Dir.glob(File.join(configuration.trusted_certs_dir, "*.{crt,pem}"))
+ glob_dir = ChefConfig::PathHelper.escape_glob_dir(configuration.trusted_certs_dir)
+ Dir.glob(File.join(glob_dir, "*.{crt,pem}"))
else
[]
end
@@ -275,7 +276,7 @@ ADVICE
rescue OpenSSL::X509::StoreError => e
return e.message
end
- return nil
+ nil
end
end
end
diff --git a/lib/chef/knife/ssl_fetch.rb b/lib/chef/knife/ssl_fetch.rb
index 5af1a905d5..98c98d06ae 100644
--- a/lib/chef/knife/ssl_fetch.rb
+++ b/lib/chef/knife/ssl_fetch.rb
@@ -41,7 +41,7 @@ class Chef
def uri
@uri ||= begin
- Chef::Log.debug("Checking SSL cert on #{given_uri}")
+ Chef::Log.trace("Checking SSL cert on #{given_uri}")
URI.parse(given_uri)
end
end
@@ -89,8 +89,11 @@ class Chef
def cn_of(certificate)
subject = certificate.subject
- cn_field_tuple = subject.to_a.find { |field| field[0] == "CN" }
- cn_field_tuple[1]
+ if cn_field_tuple = subject.to_a.find { |field| field[0] == "CN" }
+ cn_field_tuple[1]
+ else
+ nil
+ end
end
# Convert the CN of a certificate into something that will work well as a
@@ -117,9 +120,10 @@ class Chef
def write_cert(cert)
FileUtils.mkdir_p(trusted_certs_dir)
cn = cn_of(cert)
- filename = File.join(trusted_certs_dir, "#{normalize_cn(cn)}.crt")
- ui.msg("Adding certificate for #{cn} in #{filename}")
- File.open(filename, File::CREAT | File::TRUNC | File::RDWR, 0644) do |f|
+ filename = cn.nil? ? "#{host}_#{Time.new.to_i}" : normalize_cn(cn)
+ full_path = File.join(trusted_certs_dir, "#{filename}.crt")
+ ui.msg("Adding certificate for #{filename} in #{full_path}")
+ File.open(full_path, File::CREAT | File::TRUNC | File::RDWR, 0644) do |f|
f.print(cert.to_s)
end
end
diff --git a/lib/chef/knife/status.rb b/lib/chef/knife/status.rb
index 551d5addfc..0e3cd7e7d6 100644
--- a/lib/chef/knife/status.rb
+++ b/lib/chef/knife/status.rb
@@ -96,13 +96,13 @@ class Chef
all_nodes << node
end
- output(all_nodes.sort { |n1, n2|
+ output(all_nodes.sort do |n1, n2|
if config[:sort_reverse] || Chef::Config[:knife][:sort_status_reverse]
(n2["ohai_time"] || 0) <=> (n1["ohai_time"] || 0)
else
(n1["ohai_time"] || 0) <=> (n2["ohai_time"] || 0)
end
- })
+ end)
end
end
diff --git a/lib/chef/knife/supermarket_download.rb b/lib/chef/knife/supermarket_download.rb
new file mode 100644
index 0000000000..5657558591
--- /dev/null
+++ b/lib/chef/knife/supermarket_download.rb
@@ -0,0 +1,33 @@
+#
+# Author:: Christopher Webber (<cwebber@chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/knife"
+require "chef/knife/cookbook_site_download"
+
+class Chef
+ class Knife
+ class SupermarketDownload < Knife::CookbookSiteDownload
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+ options.merge!(superclass.options)
+
+ banner "knife supermarket download COOKBOOK [VERSION] (options)"
+ category "supermarket"
+ end
+ end
+end
diff --git a/lib/chef/knife/supermarket_install.rb b/lib/chef/knife/supermarket_install.rb
new file mode 100644
index 0000000000..7642e68181
--- /dev/null
+++ b/lib/chef/knife/supermarket_install.rb
@@ -0,0 +1,33 @@
+#
+# Author:: Christopher Webber (<cwebber@chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/knife"
+require "chef/knife/cookbook_site_install"
+
+class Chef
+ class Knife
+ class SupermarketInstall < Knife::CookbookSiteInstall
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+ options.merge!(superclass.options)
+
+ banner "knife supermarket install COOKBOOK [VERSION] (options)"
+ category "supermarket"
+ end
+ end
+end
diff --git a/lib/chef/resource/easy_install_package.rb b/lib/chef/knife/supermarket_list.rb
index dc5073a6f7..f2bc98bd0e 100644
--- a/lib/chef/resource/easy_install_package.rb
+++ b/lib/chef/knife/supermarket_list.rb
@@ -1,6 +1,6 @@
#
-# Author:: Joe Williams (<joe@joetify.com>)
-# Copyright:: Copyright 2009-2016, Joe Williams
+# Author:: Christopher Webber (<cwebber@chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,17 +16,18 @@
# limitations under the License.
#
-require "chef/resource/package"
+require "chef/knife"
+require "chef/knife/cookbook_site_list"
class Chef
- class Resource
- class EasyInstallPackage < Chef::Resource::Package
- resource_name :easy_install_package
-
- property :easy_install_binary, String, desired_state: false
- property :python_binary, String, desired_state: false
- property :module_name, String, desired_state: false
+ class Knife
+ class SupermarketList < Knife::CookbookSiteList
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+ options.merge!(superclass.options)
+ banner "knife supermarket list (options)"
+ category "supermarket"
end
end
end
diff --git a/lib/chef/provider/breakpoint.rb b/lib/chef/knife/supermarket_search.rb
index a71c9e317d..3206b0cb80 100644
--- a/lib/chef/provider/breakpoint.rb
+++ b/lib/chef/knife/supermarket_search.rb
@@ -1,6 +1,6 @@
#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Author:: Christopher Webber (<cwebber@chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +16,18 @@
# limitations under the License.
#
-class Chef
- class Provider
- class Breakpoint < Chef::Provider
-
- provides :breakpoint
+require "chef/knife"
+require "chef/knife/cookbook_site_search"
- def load_current_resource
- end
-
- def action_break
- if defined?(Shell) && Shell.running?
- run_context.resource_collection.iterator.pause
- @new_resource.updated_by_last_action(true)
- run_context.resource_collection.iterator
- end
- end
+class Chef
+ class Knife
+ class SupermarketSearch < Knife::CookbookSiteSearch
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+ options.merge!(superclass.options)
+ banner "knife supermarket search QUERY (options)"
+ category "supermarket"
end
end
end
diff --git a/lib/chef/knife/supermarket_share.rb b/lib/chef/knife/supermarket_share.rb
new file mode 100644
index 0000000000..3109b9e794
--- /dev/null
+++ b/lib/chef/knife/supermarket_share.rb
@@ -0,0 +1,33 @@
+#
+# Author:: Christopher Webber (<cwebber@chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/knife"
+require "chef/knife/cookbook_site_share"
+
+class Chef
+ class Knife
+ class SupermarketShare < Knife::CookbookSiteShare
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+ options.merge!(superclass.options)
+
+ banner "knife supermarket share COOKBOOK [CATEGORY] (options)"
+ category "supermarket"
+ end
+ end
+end
diff --git a/lib/chef/knife/supermarket_show.rb b/lib/chef/knife/supermarket_show.rb
new file mode 100644
index 0000000000..2ad122143f
--- /dev/null
+++ b/lib/chef/knife/supermarket_show.rb
@@ -0,0 +1,33 @@
+#
+# Author:: Christopher Webber (<cwebber@chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/knife"
+require "chef/knife/cookbook_site_show"
+
+class Chef
+ class Knife
+ class SupermarketShow < Knife::CookbookSiteShow
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+ options.merge!(superclass.options)
+
+ banner "knife supermarket show COOKBOOK [VERSION] (options)"
+ category "supermarket"
+ end
+ end
+end
diff --git a/lib/chef/knife/supermarket_unshare.rb b/lib/chef/knife/supermarket_unshare.rb
new file mode 100644
index 0000000000..fd48e172ce
--- /dev/null
+++ b/lib/chef/knife/supermarket_unshare.rb
@@ -0,0 +1,33 @@
+#
+# Author:: Christopher Webber (<cwebber@chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/knife"
+require "chef/knife/cookbook_site_unshare"
+
+class Chef
+ class Knife
+ class SupermarketUnshare < Knife::CookbookSiteUnshare
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+ options.merge!(superclass.options)
+
+ banner "knife supermarket unshare COOKBOOK (options)"
+ category "supermarket"
+ end
+ end
+end
diff --git a/lib/chef/knife/user_create.rb b/lib/chef/knife/user_create.rb
index d21afb1059..c4a89f3707 100644
--- a/lib/chef/knife/user_create.rb
+++ b/lib/chef/knife/user_create.rb
@@ -78,6 +78,8 @@ knife user create for Open Source 11 Server is being deprecated.
Open Source 11 Server user commands now live under the knife osc_user namespace.
For backwards compatibility, we will forward this request to knife osc_user create.
If you are using an Open Source 11 Server, please use that command to avoid this warning.
+NOTE: Backwards compatibility for Open Source 11 Server in these commands will be removed
+in Chef 15 which will be released April 2019.
EOF
end
diff --git a/lib/chef/knife/user_delete.rb b/lib/chef/knife/user_delete.rb
index ce4575ceab..abfb45253e 100644
--- a/lib/chef/knife/user_delete.rb
+++ b/lib/chef/knife/user_delete.rb
@@ -37,6 +37,8 @@ knife user delete for Open Source 11 Server is being deprecated.
Open Source 11 Server user commands now live under the knife osc_user namespace.
For backwards compatibility, we will forward this request to knife osc_user delete.
If you are using an Open Source 11 Server, please use that command to avoid this warning.
+NOTE: Backwards compatibility for Open Source 11 Server in these commands will be removed
+in Chef 15 which will be released April 2019.
EOF
end
@@ -60,7 +62,7 @@ EOF
end
output(format_for_display(object)) if config[:print_after]
- self.msg("Deleted #{user_name}")
+ msg("Deleted #{user_name}")
end
def run
diff --git a/lib/chef/knife/user_edit.rb b/lib/chef/knife/user_edit.rb
index bb80ede267..d184b85a6c 100644
--- a/lib/chef/knife/user_edit.rb
+++ b/lib/chef/knife/user_edit.rb
@@ -37,6 +37,8 @@ knife user edit for Open Source 11 Server is being deprecated.
Open Source 11 Server user commands now live under the knife oc_user namespace.
For backwards compatibility, we will forward this request to knife osc_user edit.
If you are using an Open Source 11 Server, please use that command to avoid this warning.
+NOTE: Backwards compatibility for Open Source 11 Server in these commands will be removed
+in Chef 15 which will be released April 2019.
EOF
end
diff --git a/lib/chef/knife/user_key_create.rb b/lib/chef/knife/user_key_create.rb
index 95a98a2f4f..1cceb886c4 100644
--- a/lib/chef/knife/user_key_create.rb
+++ b/lib/chef/knife/user_key_create.rb
@@ -17,6 +17,7 @@
#
require "chef/knife"
+require "chef/knife/key_create"
require "chef/knife/key_create_base"
class Chef
diff --git a/lib/chef/knife/user_key_delete.rb b/lib/chef/knife/user_key_delete.rb
index 1c559f2ef0..2a0c958ed9 100644
--- a/lib/chef/knife/user_key_delete.rb
+++ b/lib/chef/knife/user_key_delete.rb
@@ -17,6 +17,7 @@
#
require "chef/knife"
+require "chef/knife/key_delete"
class Chef
class Knife
diff --git a/lib/chef/knife/user_key_edit.rb b/lib/chef/knife/user_key_edit.rb
index 561af8edd0..ca056e3650 100644
--- a/lib/chef/knife/user_key_edit.rb
+++ b/lib/chef/knife/user_key_edit.rb
@@ -17,6 +17,7 @@
#
require "chef/knife"
+require "chef/knife/key_edit"
require "chef/knife/key_edit_base"
class Chef
diff --git a/lib/chef/knife/user_key_list.rb b/lib/chef/knife/user_key_list.rb
index 799c069182..10583f4a3c 100644
--- a/lib/chef/knife/user_key_list.rb
+++ b/lib/chef/knife/user_key_list.rb
@@ -17,6 +17,7 @@
#
require "chef/knife"
+require "chef/knife/key_list"
require "chef/knife/key_list_base"
class Chef
diff --git a/lib/chef/knife/user_key_show.rb b/lib/chef/knife/user_key_show.rb
index e09d5e04ed..b3a6ee73dc 100644
--- a/lib/chef/knife/user_key_show.rb
+++ b/lib/chef/knife/user_key_show.rb
@@ -17,6 +17,7 @@
#
require "chef/knife"
+require "chef/knife/key_show"
class Chef
class Knife
diff --git a/lib/chef/knife/user_reregister.rb b/lib/chef/knife/user_reregister.rb
index 8d2f2c1e73..fec6792134 100644
--- a/lib/chef/knife/user_reregister.rb
+++ b/lib/chef/knife/user_reregister.rb
@@ -37,6 +37,8 @@ knife user reregister for Open Source 11 Server is being deprecated.
Open Source 11 Server user commands now live under the knife osc_user namespace.
For backwards compatibility, we will forward this request to knife osc_user reregister.
If you are using an Open Source 11 Server, please use that command to avoid this warning.
+NOTE: Backwards compatibility for Open Source 11 Server in these commands will be removed
+in Chef 15 which will be released April 2019.
EOF
end
@@ -73,7 +75,7 @@ EOF
run_osc_11_user_reregister
else # EC / CS 12 case
user.reregister
- Chef::Log.debug("Updated user data: #{user.inspect}")
+ Chef::Log.trace("Updated user data: #{user.inspect}")
key = user.private_key
if config[:file]
File.open(config[:file], "w") do |f|
diff --git a/lib/chef/knife/user_show.rb b/lib/chef/knife/user_show.rb
index 04251c0863..6ba4ab5016 100644
--- a/lib/chef/knife/user_show.rb
+++ b/lib/chef/knife/user_show.rb
@@ -39,6 +39,8 @@ knife user show for Open Source 11 Server is being deprecated.
Open Source 11 Server user commands now live under the knife osc_user namespace.
For backwards compatibility, we will forward this request to knife osc_user show.
If you are using an Open Source 11 Server, please use that command to avoid this warning.
+NOTE: Backwards compatibility for Open Source 11 Server in these commands will be removed
+in Chef 15 which will be released April 2019.
EOF
end
diff --git a/lib/chef/knife/xargs.rb b/lib/chef/knife/xargs.rb
index 6559ca2e74..a316fb8cf7 100644
--- a/lib/chef/knife/xargs.rb
+++ b/lib/chef/knife/xargs.rb
@@ -9,7 +9,7 @@ class Chef
deps do
require "chef/chef_fs/file_system"
- require "chef/chef_fs/file_system/not_found_error"
+ require "chef/chef_fs/file_system/exceptions"
end
# TODO modify to remote-only / local-only pattern (more like delete)
@@ -181,7 +181,7 @@ class Chef
def destroy_tempfiles(tempfiles)
# Unlink the files now that we're done with them
- tempfiles.keys.each { |tempfile| tempfile.close! }
+ tempfiles.each_key { |tempfile| tempfile.close! }
end
def xargs_files(command, tempfiles)
diff --git a/lib/chef/local_mode.rb b/lib/chef/local_mode.rb
index 5ce17e6fb3..5630a54335 100644
--- a/lib/chef/local_mode.rb
+++ b/lib/chef/local_mode.rb
@@ -73,6 +73,7 @@ class Chef
@chef_zero_server = ChefZero::Server.new(server_options)
if Chef::Config[:listen]
+ Chef.deprecated(:local_listen, "Starting local-mode server in deprecated socket mode")
@chef_zero_server.start_background
else
@chef_zero_server.start_socketless
diff --git a/lib/chef/log.rb b/lib/chef/log.rb
index ac2baeb9d1..10c9f0f20d 100644
--- a/lib/chef/log.rb
+++ b/lib/chef/log.rb
@@ -58,7 +58,7 @@ class Chef
end
if Chef::Config[:treat_deprecation_warnings_as_errors]
error(msg, &block)
- raise Chef::Exceptions::DeprecatedFeatureError.new(msg)
+ raise Chef::Exceptions::DeprecatedFeatureError.new(msg.inspect)
else
warn(msg, &block)
end
diff --git a/lib/chef/mash.rb b/lib/chef/mash.rb
index 3858ff09dd..8b9e115dd1 100644
--- a/lib/chef/mash.rb
+++ b/lib/chef/mash.rb
@@ -105,6 +105,12 @@ class Mash < Hash
regular_writer(convert_key(key), convert_value(value))
end
+ # internal API for use by Chef's deep merge cache
+ # @api private
+ def internal_set(key, value)
+ regular_writer(key, convert_value(value))
+ end
+
# @param other_hash<Hash>
# A hash to update values in the mash with. The keys and the values will be
# converted to Mash format.
@@ -149,7 +155,7 @@ class Mash < Hash
#
# @return [Mash] A new mash with the hash values merged in.
def merge(hash)
- self.dup.update(hash)
+ dup.update(hash)
end
# @param key<Object>
diff --git a/lib/chef/mixin/api_version_request_handling.rb b/lib/chef/mixin/api_version_request_handling.rb
index b91a1dfe0a..5566addf1d 100644
--- a/lib/chef/mixin/api_version_request_handling.rb
+++ b/lib/chef/mixin/api_version_request_handling.rb
@@ -19,11 +19,9 @@
class Chef
module Mixin
module ApiVersionRequestHandling
- # Input:
- # exeception:
- # Net::HTTPServerException that may or may not contain the x-ops-server-api-version header
+ # @param exeception [Net::HTTPServerException] may or may not contain the x-ops-server-api-version header
# supported_client_versions:
- # An array of Integers that represent the API versions the client supports.
+ # @param supported_client_versions [Array<Integer>] The API versions the client supports.
#
# Output:
# nil:
diff --git a/lib/chef/mixin/checksum.rb b/lib/chef/mixin/checksum.rb
index f223894c39..2888b205a9 100644
--- a/lib/chef/mixin/checksum.rb
+++ b/lib/chef/mixin/checksum.rb
@@ -27,6 +27,11 @@ class Chef
Chef::Digester.checksum_for_file(file)
end
+ def short_cksum(checksum)
+ return "none" if checksum.nil?
+ checksum.slice(0, 6)
+ end
+
end
end
end
diff --git a/lib/chef/mixin/command.rb b/lib/chef/mixin/command.rb
deleted file mode 100644
index 0cc3143ec7..0000000000
--- a/lib/chef/mixin/command.rb
+++ /dev/null
@@ -1,193 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/log"
-require "chef/exceptions"
-require "tmpdir"
-require "fcntl"
-require "etc"
-
-class Chef
- module Mixin
-
- #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # NOTE:
- # The popen4 method upon which all the code here is based has a race
- # condition where it may fail to read all of the data written to stdout and
- # stderr after the child process exits. The tests for the code here
- # occasionally fail because of this race condition, so they have been
- # tagged "volatile".
- #
- # This code is considered deprecated, so it should not need to be modified
- # frequently, if at all. HOWEVER, if you do modify the code here, you must
- # explicitly enable volatile tests:
- #
- # bundle exec rspec spec/unit/mixin/command_spec.rb -t volatile
- #
- # In addition, you should make a note that tests need to be run with
- # volatile tests enabled on any pull request or bug report you submit with
- # your patch.
- #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- module Command
- extend self
-
- # NOTE: run_command is deprecated in favor of using Chef::Shellout which now comes from the mixlib-shellout gem. NOTE #
-
- if RUBY_PLATFORM =~ /mswin|mingw32|windows/
- require "chef/mixin/command/windows"
- include ::Chef::Mixin::Command::Windows
- extend ::Chef::Mixin::Command::Windows
- else
- require "chef/mixin/command/unix"
- include ::Chef::Mixin::Command::Unix
- extend ::Chef::Mixin::Command::Unix
- end
-
- # === Parameters
- # args<Hash>: A number of required and optional arguments
- # command<String>, <Array>: A complete command with options to execute or a command and options as an Array
- # creates<String>: The absolute path to a file that prevents the command from running if it exists
- # cwd<String>: Working directory to execute command in, defaults to Dir.tmpdir
- # timeout<String>: How many seconds to wait for the command to execute before timing out
- # returns<String>: The single exit value command is expected to return, otherwise causes an exception
- # ignore_failure<Boolean>: Whether to raise an exception on failure, or just return the status
- # output_on_failure<Boolean>: Return output in raised exception regardless of Log.level
- #
- # user<String>: The UID or user name of the user to execute the command as
- # group<String>: The GID or group name of the group to execute the command as
- # environment<Hash>: Pairs of environment variable names and their values to set before execution
- #
- # === Returns
- # Returns the exit status of args[:command]
- def run_command(args = {})
- status, stdout, stderr = run_command_and_return_stdout_stderr(args)
-
- status
- end
-
- # works same as above, except that it returns stdout and stderr
- # requirement => platforms like solaris 9,10 has weird issues where
- # even in command failure the exit code is zero, so we need to lookup stderr.
- def run_command_and_return_stdout_stderr(args = {})
- command_output = ""
-
- args[:ignore_failure] ||= false
- args[:output_on_failure] ||= false
-
- # TODO: This is the wrong place for this responsibility.
- if args.has_key?(:creates)
- if File.exists?(args[:creates])
- Chef::Log.debug("Skipping #{args[:command]} - creates #{args[:creates]} exists.")
- return false
- end
- end
-
- status, stdout, stderr = output_of_command(args[:command], args)
- command_output << "STDOUT: #{stdout}"
- command_output << "STDERR: #{stderr}"
- handle_command_failures(status, command_output, args)
-
- return status, stdout, stderr
- end
-
- def output_of_command(command, args)
- Chef::Log.debug("Executing #{command}")
- stderr_string, stdout_string, status = "", "", nil
-
- exec_processing_block = lambda do |pid, stdin, stdout, stderr|
- stdout_string, stderr_string = stdout.string.chomp, stderr.string.chomp
- end
-
- args[:cwd] ||= Dir.tmpdir
- unless ::File.directory?(args[:cwd])
- raise Chef::Exceptions::Exec, "#{args[:cwd]} does not exist or is not a directory"
- end
-
- Dir.chdir(args[:cwd]) do
- if args[:timeout]
- begin
- Timeout.timeout(args[:timeout]) do
- status = popen4(command, args, &exec_processing_block)
- end
- rescue Timeout::Error => e
- Chef::Log.error("#{command} exceeded timeout #{args[:timeout]}")
- raise(e)
- end
- else
- status = popen4(command, args, &exec_processing_block)
- end
-
- Chef::Log.debug("---- Begin output of #{command} ----")
- Chef::Log.debug("STDOUT: #{stdout_string}")
- Chef::Log.debug("STDERR: #{stderr_string}")
- Chef::Log.debug("---- End output of #{command} ----")
- Chef::Log.debug("Ran #{command} returned #{status.exitstatus}")
- end
-
- return status, stdout_string, stderr_string
- end
-
- def handle_command_failures(status, command_output, opts = {})
- return if opts[:ignore_failure]
- opts[:returns] ||= 0
- return if Array(opts[:returns]).include?(status.exitstatus)
-
- # if the log level is not debug, through output of command when we fail
- output = ""
- if Chef::Log.level == :debug || opts[:output_on_failure]
- output << "\n---- Begin output of #{opts[:command]} ----\n"
- output << command_output.to_s
- output << "\n---- End output of #{opts[:command]} ----\n"
- end
- raise Chef::Exceptions::Exec, "#{opts[:command]} returned #{status.exitstatus}, expected #{opts[:returns]}#{output}"
- end
-
- # Call #run_command but set LC_ALL to the system's current environment so it doesn't get changed to C.
- #
- # === Parameters
- # args<Hash>: A number of required and optional arguments that will be handed out to #run_command
- #
- # === Returns
- # Returns the result of #run_command
- def run_command_with_systems_locale(args = {})
- args[:environment] ||= {}
- args[:environment]["LC_ALL"] = ENV["LC_ALL"]
- run_command args
- end
-
- # def popen4(cmd, args={}, &b)
- # @@os_handler.popen4(cmd, args, &b)
- # end
-
- # module_function :popen4
-
- # FIXME: yard with @yield
- def chdir_or_tmpdir(dir)
- dir ||= Dir.tmpdir
- unless File.directory?(dir)
- raise Chef::Exceptions::Exec, "#{dir} does not exist or is not a directory"
- end
- Dir.chdir(dir) do
- yield
- end
- end
-
- end
- end
-end
diff --git a/lib/chef/mixin/command/unix.rb b/lib/chef/mixin/command/unix.rb
deleted file mode 100644
index bfd507979f..0000000000
--- a/lib/chef/mixin/command/unix.rb
+++ /dev/null
@@ -1,220 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-class Chef
- module Mixin
- module Command
- module Unix
- # This is taken directly from Ara T Howard's Open4 library, and then
- # modified to suit the needs of Chef. Any bugs here are most likely
- # my own, and not Ara's.
- #
- # The original appears in external/open4.rb in its unmodified form.
- #
- # Thanks Ara!
- def popen4(cmd, args = {}, &b)
- # Ruby 1.8 suffers from intermittent segfaults believed to be due to GC while IO.select
- # See CHEF-2916 / CHEF-1305
- GC.disable
-
- # Waitlast - this is magic.
- #
- # Do we wait for the child process to die before we yield
- # to the block, or after? That is the magic of waitlast.
- #
- # By default, we are waiting before we yield the block.
- args[:waitlast] ||= false
-
- args[:user] ||= nil
- unless args[:user].kind_of?(Integer)
- args[:user] = Etc.getpwnam(args[:user]).uid if args[:user]
- end
- args[:group] ||= nil
- unless args[:group].kind_of?(Integer)
- args[:group] = Etc.getgrnam(args[:group]).gid if args[:group]
- end
- args[:environment] ||= {}
-
- # Default on C locale so parsing commands output can be done
- # independently of the node's default locale.
- # "LC_ALL" could be set to nil, in which case we also must ignore it.
- unless args[:environment].has_key?("LC_ALL")
- args[:environment]["LC_ALL"] = "C"
- end
-
- pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
-
- verbose = $VERBOSE
- begin
- $VERBOSE = nil
- ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
-
- cid = fork {
- pw.last.close
- STDIN.reopen pw.first
- pw.first.close
-
- pr.first.close
- STDOUT.reopen pr.last
- pr.last.close
-
- pe.first.close
- STDERR.reopen pe.last
- pe.last.close
-
- STDOUT.sync = STDERR.sync = true
-
- if args[:group]
- Process.egid = args[:group]
- Process.gid = args[:group]
- end
-
- if args[:user]
- Process.euid = args[:user]
- Process.uid = args[:user]
- end
-
- args[:environment].each do |key, value|
- ENV[key] = value
- end
-
- if args[:umask]
- umask = ((args[:umask].respond_to?(:oct) ? args[:umask].oct : args[:umask].to_i) & 007777)
- File.umask(umask)
- end
-
- begin
- if cmd.kind_of?(Array)
- Kernel.exec(*cmd)
- else
- Kernel.exec(cmd)
- end
- raise "forty-two"
- rescue Exception => e
- Marshal.dump(e, ps.last)
- ps.last.flush
- end
- ps.last.close unless ps.last.closed?
- exit!
- }
- ensure
- $VERBOSE = verbose
- end
-
- [pw.first, pr.last, pe.last, ps.last].each { |fd| fd.close }
-
- begin
- e = Marshal.load ps.first
- raise(Exception === e ? e : "unknown failure!")
- rescue EOFError # If we get an EOF error, then the exec was successful
- 42
- ensure
- ps.first.close
- end
-
- pw.last.sync = true
-
- pi = [pw.last, pr.first, pe.first]
-
- if b
- begin
- if args[:waitlast]
- b[cid, *pi]
- # send EOF so that if the child process is reading from STDIN
- # it will actually finish up and exit
- pi[0].close_write
- Process.waitpid2(cid).last
- else
- # This took some doing.
- # The trick here is to close STDIN
- # Then set our end of the childs pipes to be O_NONBLOCK
- # Then wait for the child to die, which means any IO it
- # wants to do must be done - it's dead. If it isn't,
- # it's because something totally skanky is happening,
- # and we don't care.
- o = StringIO.new
- e = StringIO.new
-
- pi[0].close
-
- stdout = pi[1]
- stderr = pi[2]
-
- stdout.sync = true
- stderr.sync = true
-
- stdout.fcntl(Fcntl::F_SETFL, pi[1].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
- stderr.fcntl(Fcntl::F_SETFL, pi[2].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
-
- stdout_finished = false
- stderr_finished = false
-
- results = nil
-
- while !stdout_finished || !stderr_finished
- begin
- channels_to_watch = []
- channels_to_watch << stdout if !stdout_finished
- channels_to_watch << stderr if !stderr_finished
- ready = IO.select(channels_to_watch, nil, nil, 1.0)
- rescue Errno::EAGAIN
- ensure
- results = Process.waitpid2(cid, Process::WNOHANG)
- if results
- stdout_finished = true
- stderr_finished = true
- end
- end
-
- if ready && ready.first.include?(stdout)
- line = results ? stdout.gets(nil) : stdout.gets
- if line
- o.write(line)
- else
- stdout_finished = true
- end
- end
- if ready && ready.first.include?(stderr)
- line = results ? stderr.gets(nil) : stderr.gets
- if line
- e.write(line)
- else
- stderr_finished = true
- end
- end
- end
- results = Process.waitpid2(cid) unless results
- o.rewind
- e.rewind
- b[cid, pi[0], o, e]
- results.last
- end
- ensure
- pi.each { |fd| fd.close unless fd.closed? }
- end
- else
- [cid, pw.last, pr.first, pe.first]
- end
- ensure
- GC.enable
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/mixin/command/windows.rb b/lib/chef/mixin/command/windows.rb
deleted file mode 100644
index fd45ab0467..0000000000
--- a/lib/chef/mixin/command/windows.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# Author:: Doug MacEachern (<dougm@vmware.com>)
-# Copyright:: Copyright 2010-2016, VMware, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "open3"
-
-class Chef
- module Mixin
- module Command
- module Windows
- def popen4(cmd, args = {}, &b)
- # By default, we are waiting before we yield the block.
- args[:waitlast] ||= false
-
- #XXX :user, :group, :environment support?
-
- Open3.popen3(cmd) do |stdin, stdout, stderr, cid|
- if b
- if args[:waitlast]
- b[cid, stdin, stdout, stderr]
- # send EOF so that if the child process is reading from STDIN
- # it will actually finish up and exit
- stdin.close_write
- else
- o = StringIO.new
- e = StringIO.new
-
- stdin.close
-
- stdout.sync = true
- stderr.sync = true
-
- line = stdout.gets(nil)
- if line
- o.write(line)
- end
- line = stderr.gets(nil)
- if line
- e.write(line)
- end
- o.rewind
- e.rewind
- b[cid, stdin, o, e]
- end
- else
- [cid, stdin, stdout, stderr]
- end
- end
- $?
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/mixin/create_path.rb b/lib/chef/mixin/create_path.rb
index 233f7b9521..455110b1a2 100644
--- a/lib/chef/mixin/create_path.rb
+++ b/lib/chef/mixin/create_path.rb
@@ -53,7 +53,6 @@ class Chef
private
def create_dir(path)
- begin
# When doing multithreaded downloads into the file cache, the following
# interleaving raises an error here:
#
@@ -62,10 +61,9 @@ class Chef
# File.directory?(create_path) <- false
# Dir.mkdir(create_path)
# Dir.mkdir(create_path) <- raises Errno::EEXIST
- Chef::Log.debug("Creating directory #{path}")
- Dir.mkdir(path)
- rescue Errno::EEXIST
- end
+ Chef::Log.trace("Creating directory #{path}")
+ Dir.mkdir(path)
+ rescue Errno::EEXIST
end
end
diff --git a/lib/chef/mixin/deep_merge.rb b/lib/chef/mixin/deep_merge.rb
index c0b2d0d0c5..9ec2b397d4 100644
--- a/lib/chef/mixin/deep_merge.rb
+++ b/lib/chef/mixin/deep_merge.rb
@@ -64,7 +64,7 @@ class Chef
when Hash
if dest.kind_of?(Hash)
source.each do |src_key, src_value|
- if dest[src_key]
+ if dest.key?(src_key)
dest[src_key] = deep_merge!(src_value, dest[src_key])
else # dest[src_key] doesn't exist so we take whatever source has
dest[src_key] = src_value
@@ -75,7 +75,7 @@ class Chef
end
when Array
if dest.kind_of?(Array)
- dest = dest | source
+ dest |= source
else
dest = source
end
diff --git a/lib/chef/mixin/deprecation.rb b/lib/chef/mixin/deprecation.rb
index 0f059a215f..6621555585 100644
--- a/lib/chef/mixin/deprecation.rb
+++ b/lib/chef/mixin/deprecation.rb
@@ -23,24 +23,24 @@ class Chef
@deprecated_constants ||= {}
end
- # Add a deprecated constant to the Chef::Mixin namespace.
- # === Arguments
- # * name: the constant name, as a relative symbol.
- # * replacement: the constant to return instead.
- # * message: A message telling the user what to do instead.
- # === Example:
- # deprecate_constant(:RecipeDefinitionDSLCore, Chef::DSL::Recipe, <<-EOM)
- # Chef::Mixin::RecipeDefinitionDSLCore is deprecated, use Chef::DSL::Recipe instead.
- # EOM
+ # Add a deprecated constant to the Chef::Mixin namespace.
+ #
+ # @param name [Symbol] the constant name, as a relative symbol.
+ # @param replacement [Object] the constant to return instead.
+ # @param message [String] A message telling the user what to do instead.
+ # @example
+ # deprecate_constant(:RecipeDefinitionDSLCore, Chef::DSL::Recipe, <<-EOM)
+ # Chef::Mixin::RecipeDefinitionDSLCore is deprecated, use Chef::DSL::Recipe instead.
+ # EOM
def self.deprecate_constant(name, replacement, message)
deprecated_constants[name] = { :replacement => replacement, :message => message }
end
- # Const missing hook to look up deprecated constants defined with
- # deprecate_constant. Emits a warning to the logger and returns the
- # replacement constant. Will call super, most likely causing an exception
- # for the missing constant, if +name+ is not found in the
- # deprecated_constants collection.
+ # Const missing hook to look up deprecated constants defined with
+ # deprecate_constant. Emits a warning to the logger and returns the
+ # replacement constant. Will call super, most likely causing an exception
+ # for the missing constant, if +name+ is not found in the
+ # deprecated_constants collection.
def self.const_missing(name)
if new_const = deprecated_constants[name]
Chef::Log.warn(new_const[:message])
@@ -65,7 +65,7 @@ class Chef
end
def method_missing(method_name, *args, &block)
- log_deprecation_msg(caller[0..3])
+ deprecated_msg(caller[0..3])
@target.send(method_name, *args, &block)
end
@@ -75,7 +75,7 @@ class Chef
private
- def log_deprecation_msg(*called_from)
+ def deprecated_msg(*called_from)
called_from = called_from.flatten
log("Accessing #{@ivar_name} by the variable @#{@ivar_name} is deprecated. Support will be removed in a future release.")
log("Please update your cookbooks to use #{@ivar_name} in place of @#{@ivar_name}. Accessed from:")
@@ -101,20 +101,14 @@ class Chef
def deprecated_attr_reader(name, alternative, level = :warn)
define_method(name) do
- Chef.log_deprecation("#{self.class}.#{name} is deprecated. Support will be removed in a future release.")
- Chef.log_deprecation(alternative)
- Chef.log_deprecation("Called from:")
- caller[0..3].each { |c| Chef.log_deprecation(c) }
+ Chef.deprecated(:internal_api, "#{self.class}.#{name} is deprecated. Support will be removed in a future release. #{alternative}")
instance_variable_get("@#{name}")
end
end
def deprecated_attr_writer(name, alternative, level = :warn)
define_method("#{name}=") do |value|
- Chef.log_deprecation("Writing to #{self.class}.#{name} with #{name}= is deprecated. Support will be removed in a future release.")
- Chef.log_deprecation(alternative)
- Chef.log_deprecation("Called from:")
- caller[0..3].each { |c| Chef.log_deprecation(c) }
+ Chef.deprecated(:internal_api, "Writing to #{self.class}.#{name} with #{name}= is deprecated. Support will be removed in a future release. #{alternative}")
instance_variable_set("@#{name}", value)
end
end
diff --git a/lib/chef/mixin/from_file.rb b/lib/chef/mixin/from_file.rb
index a6692d798d..e19597dde8 100644
--- a/lib/chef/mixin/from_file.rb
+++ b/lib/chef/mixin/from_file.rb
@@ -21,13 +21,17 @@ class Chef
module Mixin
module FromFile
+ # Source path from which the object was loaded
+ attr_accessor :source_file
+
# Loads a given ruby file, and runs instance_eval against it in the context of the current
# object.
#
# Raises an IOError if the file cannot be found, or is not readable.
def from_file(filename)
+ self.source_file = filename
if File.exists?(filename) && File.readable?(filename)
- self.instance_eval(IO.read(filename), filename, 1)
+ instance_eval(IO.read(filename), filename, 1)
else
raise IOError, "Cannot open or read #{filename}!"
end
@@ -38,8 +42,9 @@ class Chef
#
# Raises an IOError if the file cannot be found, or is not readable.
def class_from_file(filename)
+ self.source_file = filename
if File.exists?(filename) && File.readable?(filename)
- self.class_eval(IO.read(filename), filename, 1)
+ class_eval(IO.read(filename), filename, 1)
else
raise IOError, "Cannot open or read #{filename}!"
end
diff --git a/lib/chef/mixin/get_source_from_package.rb b/lib/chef/mixin/get_source_from_package.rb
index 555dd634f8..832f96064d 100644
--- a/lib/chef/mixin/get_source_from_package.rb
+++ b/lib/chef/mixin/get_source_from_package.rb
@@ -18,7 +18,7 @@
#
# mixin to make this syntax work without specifying a source:
#
-# gem_pacakge "/tmp/foo-x.y.z.gem"
+# gem_package "/tmp/foo-x.y.z.gem"
# rpm_package "/tmp/foo-x.y-z.rpm"
# dpkg_package "/tmp/foo-x.y.z.deb"
#
@@ -37,9 +37,9 @@ class Chef
return if new_resource.package_name.is_a?(Array)
# if we're passed something that looks like a filesystem path, with no source, use it
# - require at least one '/' in the path to avoid gem_package "foo" breaking if a file named 'foo' exists in the cwd
- if new_resource.source.nil? && new_resource.package_name.match(/#{::File::SEPARATOR}/) && ::File.exists?(new_resource.package_name)
- Chef::Log.debug("No package source specified, but #{new_resource.package_name} exists on the filesystem, copying to package source")
- new_resource.source(@new_resource.package_name)
+ if new_resource.source.nil? && new_resource.package_name.match(/#{::File::SEPARATOR}/) && ::File.exist?(new_resource.package_name)
+ Chef::Log.trace("No package source specified, but #{new_resource.package_name} exists on the filesystem, copying to package source")
+ new_resource.source(new_resource.package_name)
end
end
end
diff --git a/lib/chef/mixin/homebrew_user.rb b/lib/chef/mixin/homebrew_user.rb
index 888c1bcbfd..b038dfd3b7 100644
--- a/lib/chef/mixin/homebrew_user.rb
+++ b/lib/chef/mixin/homebrew_user.rb
@@ -34,6 +34,8 @@ class Chef
# This tries to find the user to execute brew as. If a user is provided, that overrides the brew
# executable user. It is an error condition if the brew executable owner is root or we cannot find
# the brew executable.
+ # @param [String, Integer] provided_user
+ # @return [Integer] UID of the user
def find_homebrew_uid(provided_user = nil)
# They could provide us a user name or a UID
if provided_user
@@ -41,8 +43,17 @@ class Chef
return Etc.getpwnam(provided_user).uid
end
- @homebrew_owner ||= calculate_owner
- @homebrew_owner
+ @homebrew_owner_uid ||= calculate_owner
+ @homebrew_owner_uid
+ end
+
+ # Use find_homebrew_uid to return the UID and then lookup the
+ # name from that UID because sometimes you want the name not the UID
+ # @param [String, Integer] provided_user
+ # @return [String] username
+ def find_homebrew_username(provided_user = nil)
+ @homebrew_owner_username ||= Etc.getpwuid(find_homebrew_uid(provided_user)).name
+ @homebrew_owner_username
end
private
diff --git a/lib/chef/mixin/language.rb b/lib/chef/mixin/language.rb
deleted file mode 100644
index 3f53645a55..0000000000
--- a/lib/chef/mixin/language.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/dsl/platform_introspection"
-require "chef/dsl/data_query"
-require "chef/mixin/deprecation"
-
-class Chef
- module Mixin
-
- # == [DEPRECATED] Chef::Mixin::DeprecatedLanguageModule
- # This module is a temporary replacement for the previous
- # Chef::Mixin::Language. That module's functionality was split into two
- # modules, Chef::DSL::PlatformIntrospection, and Chef::DSL::DataQuery.
- #
- # This module includes both PlatformIntrospection and DataQuery to provide
- # the same interfaces and behavior as the prior Mixin::Language.
- #
- # This module is loaded via const_missing hook when Chef::Mixin::Language
- # is accessed. See chef/mixin/deprecation for details.
- module DeprecatedLanguageModule
-
- include Chef::DSL::PlatformIntrospection
- include Chef::DSL::DataQuery
-
- end
-
- deprecate_constant(:Language, DeprecatedLanguageModule, <<-EOM)
-Chef::Mixin::Language is deprecated. Use either (or both)
-Chef::DSL::PlatformIntrospection or Chef::DSL::DataQuery instead.
-EOM
- end
-end
diff --git a/lib/chef/mixin/notifying_block.rb b/lib/chef/mixin/notifying_block.rb
index 2d6a82f493..d3f235f968 100644
--- a/lib/chef/mixin/notifying_block.rb
+++ b/lib/chef/mixin/notifying_block.rb
@@ -20,15 +20,13 @@ class Chef
module NotifyingBlock
def notifying_block(&block)
- begin
- subcontext = subcontext_block(&block)
- Chef::Runner.new(subcontext).converge
- ensure
- # recipes don't have a new_resource
- if respond_to?(:new_resource)
- if subcontext && subcontext.resource_collection.any?(&:updated?)
- new_resource.updated_by_last_action(true)
- end
+ subcontext = subcontext_block(&block)
+ Chef::Runner.new(subcontext).converge
+ ensure
+ # recipes don't have a new_resource
+ if respond_to?(:new_resource)
+ if subcontext && subcontext.resource_collection.any?(&:updated?)
+ new_resource.updated_by_last_action(true)
end
end
end
diff --git a/lib/chef/mixin/openssl_helper.rb b/lib/chef/mixin/openssl_helper.rb
new file mode 100644
index 0000000000..f12a559097
--- /dev/null
+++ b/lib/chef/mixin/openssl_helper.rb
@@ -0,0 +1,119 @@
+#
+# Copyright:: Copyright 2013-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ module Mixin
+ # various helpers for use with openssl. Currently used by the openssl_* resources
+ module OpenSSLHelper
+ def self.included(_base)
+ require "openssl" unless defined?(::OpenSSL)
+ end
+
+ # determine the key filename from the cert filename
+ # @param [String] cert_filename the path to the certfile
+ # @return [String] the path to the keyfile
+ def get_key_filename(cert_filename)
+ cert_file_path, cert_filename = ::File.split(cert_filename)
+ cert_filename = ::File.basename(cert_filename, ::File.extname(cert_filename))
+ cert_file_path + ::File::SEPARATOR + cert_filename + ".key"
+ end
+
+ # is the key length a valid key length
+ # @param [Integer] number
+ # @return [Boolean] is length valid
+ def key_length_valid?(number)
+ number >= 1024 && ( number & (number - 1) == 0 )
+ end
+
+ # validate a dhparam file from path
+ # @param [String] dhparam_pem_path the path to the pem file
+ # @return [Boolean] is the key valid
+ def dhparam_pem_valid?(dhparam_pem_path)
+ # Check if the dhparam.pem file exists
+ # Verify the dhparam.pem file contains a key
+ return false unless ::File.exist?(dhparam_pem_path)
+ dhparam = ::OpenSSL::PKey::DH.new File.read(dhparam_pem_path)
+ dhparam.params_ok?
+ end
+
+ # given either a key file path or key file content see if it's actually
+ # a private key
+ # @param [String] key_file the path to the keyfile or the key contents
+ # @param [String] key_password optional password to the keyfile
+ # @return [Boolean] is the key valid?
+ def priv_key_file_valid?(key_file, key_password = nil)
+ # if the file exists try to read the content
+ # if not assume we were passed the key and set the string to the content
+ key_content = ::File.exist?(key_file) ? File.read(key_file) : key_file
+
+ begin
+ key = ::OpenSSL::PKey::RSA.new key_content, key_password
+ rescue ::OpenSSL::PKey::RSAError
+ return false
+ end
+ key.private?
+ end
+
+ # generate a dhparam file
+ # @param [String] key_length the length of the key
+ # @param [Integer] generator the dhparam generator to use
+ # @return [OpenSSL::PKey::DH]
+ def gen_dhparam(key_length, generator)
+ raise ArgumentError, "Key length must be a power of 2 greater than or equal to 1024" unless key_length_valid?(key_length)
+ raise TypeError, "Generator must be an integer" unless generator.is_a?(Integer)
+
+ ::OpenSSL::PKey::DH.new(key_length, generator)
+ end
+
+ # generate an RSA private key given key length
+ # @param [Integer] key_length the key length of the private key
+ # @return [OpenSSL::PKey::DH]
+ def gen_rsa_priv_key(key_length)
+ raise ArgumentError, "Key length must be a power of 2 greater than or equal to 1024" unless key_length_valid?(key_length)
+
+ ::OpenSSL::PKey::RSA.new(key_length)
+ end
+
+ # generate pem format of the public key given a private key
+ # @param [String] priv_key either the contents of the private key or the path to the file
+ # @param [String] priv_key_password optional password for the private key
+ # @return [String] pem format of the public key
+ def gen_rsa_pub_key(priv_key, priv_key_password = nil)
+ # if the file exists try to read the content
+ # if not assume we were passed the key and set the string to the content
+ key_content = ::File.exist?(priv_key) ? File.read(priv_key) : priv_key
+ key = ::OpenSSL::PKey::RSA.new key_content, priv_key_password
+ key.public_key.to_pem
+ end
+
+ # generate a pem file given a cipher, key, an optional key_password
+ # @param [OpenSSL::PKey::RSA] rsa_key the private key object
+ # @param [String] key_password the password for the private key
+ # @param [String] key_cipher the cipher to use
+ # @return [String] pem contents
+ def encrypt_rsa_key(rsa_key, key_password, key_cipher)
+ raise TypeError, "rsa_key must be a Ruby OpenSSL::PKey::RSA object" unless rsa_key.is_a?(::OpenSSL::PKey::RSA)
+ raise TypeError, "key_password must be a string" unless key_password.is_a?(String)
+ raise TypeError, "key_cipher must be a string" unless key_cipher.is_a?(String)
+ raise ArgumentError, "Specified key_cipher is not available on this system" unless ::OpenSSL::Cipher.ciphers.include?(key_cipher)
+
+ cipher = ::OpenSSL::Cipher.new(key_cipher)
+ rsa_key.to_pem(cipher, key_password)
+ end
+ end
+ end
+end
diff --git a/lib/chef/mixin/params_validate.rb b/lib/chef/mixin/params_validate.rb
index b16df41c8e..4ed4b3ab3b 100644
--- a/lib/chef/mixin/params_validate.rb
+++ b/lib/chef/mixin/params_validate.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +18,7 @@
require "chef/constants"
require "chef/property"
require "chef/delayed_evaluator"
+require "chef/exceptions"
class Chef
module Mixin
@@ -34,6 +35,8 @@ class Chef
# map options are:
#
# @param opts [Hash<Symbol,Object>] Validation opts.
+ # @option opts [String] :validation_message A custom message to return
+ # should validation fail.
# @option opts [Object,Array] :is An object, or list of
# objects, that must match the value using Ruby's `===` operator
# (`opts[:is].any? { |v| v === value }`). (See #_pv_is.)
@@ -90,20 +93,24 @@ class Chef
raise ArgumentError, "Options must be a hash" unless opts.kind_of?(Hash)
raise ArgumentError, "Validation Map must be a hash" unless map.kind_of?(Hash)
+ @validation_message ||= {}
+
map.each do |key, validation|
unless key.kind_of?(Symbol) || key.kind_of?(String)
raise ArgumentError, "Validation map keys must be symbols or strings!"
end
+
case validation
when true
_pv_required(opts, key)
when false
true
when Hash
+ @validation_message[key] = validation.delete(:validation_message) if validation.has_key?(:validation_message)
validation.each do |check, carg|
check_method = "_pv_#{check}"
- if self.respond_to?(check_method, true)
- self.send(check_method, opts, key, carg)
+ if respond_to?(check_method, true)
+ send(check_method, opts, key, carg)
else
raise ArgumentError, "Validation map has unknown check: #{check}"
end
@@ -124,8 +131,8 @@ class Chef
private
- def explicitly_allows_nil?(key, validation)
- validation.has_key?(:is) && _pv_is({ key => nil }, key, validation[:is], raise_error: false)
+ def _validation_message(key, default)
+ @validation_message.has_key?(key) ? @validation_message[key] : default
end
# Return the value of a parameter, or nil if it doesn't exist.
@@ -144,7 +151,7 @@ class Chef
if is_required
return true if opts.has_key?(key.to_s) && (explicitly_allows_nil || !opts[key.to_s].nil?)
return true if opts.has_key?(key.to_sym) && (explicitly_allows_nil || !opts[key.to_sym].nil?)
- raise Exceptions::ValidationFailed, "Required argument #{key.inspect} is missing!"
+ raise Exceptions::ValidationFailed, _validation_message(key, "Required argument #{key.inspect} is missing!")
end
true
end
@@ -167,7 +174,7 @@ class Chef
to_be.each do |tb|
return true if value == tb
end
- raise Exceptions::ValidationFailed, "Option #{key} must be equal to one of: #{to_be.join(", ")}! You passed #{value.inspect}."
+ raise Exceptions::ValidationFailed, _validation_message(key, "Option #{key} must be equal to one of: #{to_be.join(", ")}! You passed #{value.inspect}.")
end
end
@@ -186,7 +193,7 @@ class Chef
to_be.each do |tb|
return true if value.kind_of?(tb)
end
- raise Exceptions::ValidationFailed, "Option #{key} must be a kind of #{to_be}! You passed #{value.inspect}."
+ raise Exceptions::ValidationFailed, _validation_message(key, "Option #{key} must be a kind of #{to_be}! You passed #{value.inspect}.")
end
end
@@ -201,7 +208,7 @@ class Chef
unless value.nil?
Array(method_name_list).each do |method_name|
unless value.respond_to?(method_name)
- raise Exceptions::ValidationFailed, "Option #{key} must have a #{method_name} method!"
+ raise Exceptions::ValidationFailed, _validation_message(key, "Option #{key} must have a #{method_name} method!")
end
end
end
@@ -233,7 +240,7 @@ class Chef
if value.respond_to?(predicate_method)
if value.send(predicate_method)
- raise Exceptions::ValidationFailed, "Option #{key} cannot be #{predicate_method_base_name}"
+ raise Exceptions::ValidationFailed, _validation_message(key, "Option #{key} cannot be #{predicate_method_base_name}")
end
end
end
@@ -293,7 +300,7 @@ class Chef
Array(regex).flatten.each do |r|
return true if r.match(value.to_s)
end
- raise Exceptions::ValidationFailed, "Option #{key}'s value #{value} does not match regular expression #{regex.inspect}"
+ raise Exceptions::ValidationFailed, _validation_message(key, "Option #{key}'s value #{value} does not match regular expression #{regex.inspect}")
end
end
@@ -315,7 +322,7 @@ class Chef
if !value.nil?
callbacks.each do |message, zeproc|
unless zeproc.call(value)
- raise Exceptions::ValidationFailed, "Option #{key}'s value #{value} #{message}!"
+ raise Exceptions::ValidationFailed, _validation_message(key, "Option #{key}'s value #{value} #{message}!")
end
end
end
@@ -332,8 +339,8 @@ class Chef
def _pv_name_property(opts, key, is_name_property = true)
if is_name_property
if opts[key].nil?
- raise CannotValidateStaticallyError, "name_property cannot be evaluated without a resource." if self == Chef::Mixin::ParamsValidate
- opts[key] = self.instance_variable_get(:"@name")
+ raise Exceptions::CannotValidateStaticallyError, "name_property cannot be evaluated without a resource." if self == Chef::Mixin::ParamsValidate
+ opts[key] = instance_variable_get(:"@name")
end
end
end
@@ -396,7 +403,7 @@ class Chef
# x nil #=> invalid
# ```
#
- def _pv_is(opts, key, to_be, raise_error: true)
+ def _pv_is(opts, key, to_be)
return true if !opts.has_key?(key.to_s) && !opts.has_key?(key.to_sym)
value = _pv_opts_lookup(opts, key)
to_be = [ to_be ].flatten(1)
@@ -404,7 +411,7 @@ class Chef
passed = to_be.any? do |tb|
case tb
when Proc
- raise CannotValidateStaticallyError, "is: proc { } must be evaluated once for each resource" if self == Chef::Mixin::ParamsValidate
+ raise Exceptions::CannotValidateStaticallyError, "is: proc { } must be evaluated once for each resource" if self == Chef::Mixin::ParamsValidate
instance_exec(value, &tb)
when Property
begin
@@ -427,7 +434,7 @@ class Chef
unless errors.empty?
message << " Errors:\n#{errors.map { |m| "- #{m}" }.join("\n")}"
end
- raise Exceptions::ValidationFailed, message
+ raise Exceptions::ValidationFailed, _validation_message(key, message)
end
end
@@ -448,10 +455,10 @@ class Chef
#
def _pv_coerce(opts, key, coercer)
if opts.has_key?(key.to_s)
- raise CannotValidateStaticallyError, "coerce must be evaluated for each resource." if self == Chef::Mixin::ParamsValidate
+ raise Exceptions::CannotValidateStaticallyError, "coerce must be evaluated for each resource." if self == Chef::Mixin::ParamsValidate
opts[key.to_s] = instance_exec(opts[key], &coercer)
elsif opts.has_key?(key.to_sym)
- raise CannotValidateStaticallyError, "coerce must be evaluated for each resource." if self == Chef::Mixin::ParamsValidate
+ raise Exceptions::CannotValidateStaticallyError, "coerce must be evaluated for each resource." if self == Chef::Mixin::ParamsValidate
opts[key.to_sym] = instance_exec(opts[key], &coercer)
end
end
diff --git a/lib/chef/mixin/path_sanity.rb b/lib/chef/mixin/path_sanity.rb
index 6a8e017bcd..c441d0770a 100644
--- a/lib/chef/mixin/path_sanity.rb
+++ b/lib/chef/mixin/path_sanity.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright 2011-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,25 +22,23 @@ class Chef
def enforce_path_sanity(env = ENV)
if Chef::Config[:enforce_path_sanity]
- env["PATH"] = "" if env["PATH"].nil?
- path_separator = Chef::Platform.windows? ? ";" : ":"
- existing_paths = env["PATH"].split(path_separator)
- # ensure the Ruby and Gem bindirs are included
- # mainly for 'full-stack' Chef installs
- paths_to_add = []
- paths_to_add << ruby_bindir unless sane_paths.include?(ruby_bindir)
- paths_to_add << gem_bindir unless sane_paths.include?(gem_bindir)
- paths_to_add << sane_paths if sane_paths
- paths_to_add.flatten!.compact!
- paths_to_add.each do |sane_path|
- unless existing_paths.include?(sane_path)
- env_path = env["PATH"].dup
- env_path << path_separator unless env["PATH"].empty?
- env_path << sane_path
- env["PATH"] = env_path.encode("utf-8", invalid: :replace, undef: :replace)
- end
- end
+ env["PATH"] = sanitized_path(env)
+ end
+ end
+
+ def sanitized_path(env = ENV)
+ env_path = env["PATH"].nil? ? "" : env["PATH"].dup
+ path_separator = Chef::Platform.windows? ? ";" : ":"
+ # ensure the Ruby and Gem bindirs are included
+ # mainly for 'full-stack' Chef installs
+ new_paths = env_path.split(path_separator)
+ [ ruby_bindir, gem_bindir ].compact.each do |path|
+ new_paths = [ path ] + new_paths unless new_paths.include?(path)
+ end
+ sane_paths.each do |path|
+ new_paths << path unless new_paths.include?(path)
end
+ new_paths.join(path_separator).encode("utf-8", invalid: :replace, undef: :replace)
end
private
diff --git a/lib/chef/mixin/powershell_exec.rb b/lib/chef/mixin/powershell_exec.rb
new file mode 100644
index 0000000000..cdd5450fa3
--- /dev/null
+++ b/lib/chef/mixin/powershell_exec.rb
@@ -0,0 +1,105 @@
+#
+# Author:: Stuart Preston (<stuart@chef.io>)
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "chef/powershell"
+
+# The powershell_exec mixin provides in-process access to PowerShell engine via
+# a COM interop (installed by the Chef Client installer).
+#
+# powershell_exec returns a Chef::PowerShell object that provides 3 methods:
+#
+# .result - returns a hash representing the results returned by executing the
+# PowerShell script block
+# .errors - this is an array of string containing any messages written to the
+# PowerShell error stream during execution
+# .error? - returns true if there were error messages written to the PowerShell
+# error stream during execution
+#
+# Some examples of usage:
+#
+# > powershell_exec("(Get-Item c:\\windows\\system32\\w32time.dll).VersionInfo"
+# ).result["FileVersion"]
+# => "10.0.14393.0 (rs1_release.160715-1616)"
+#
+# > powershell_exec("(get-process ruby).Mainmodule").result["FileName"]
+# => C:\\opscode\\chef\\embedded\\bin\\ruby.exe"
+#
+# > powershell_exec("$a = $true; $a").result
+# => true
+#
+# > powershell_exec("not-found").errors
+# => ["ObjectNotFound: (not-found:String) [], CommandNotFoundException: The
+# term 'not-found' is not recognized as the name of a cmdlet, function, script
+# file, or operable program. Check the spelling of the name, or if a path was
+# included, verify that the path is correct and try again. (at <ScriptBlock>,
+# <No file>: line 1)"]
+#
+# > powershell_exec("not-found").error?
+# => true
+#
+# > powershell_exec("get-item c:\\notfound -erroraction stop")
+# WIN32OLERuntimeError: (in OLE method `ExecuteScript': )
+# OLE error code:80131501 in System.Management.Automation
+# The running command stopped because the preference variable
+# "ErrorActionPreference" or common parameter is set to Stop: Cannot find
+# path 'C:\notfound' because it does not exist.
+#
+# *Why use this and not powershell_out?* Startup time to invoke the PowerShell
+# engine is much faster (over 7X faster in tests) than writing the PowerShell
+# to disk, shelling out to powershell.exe and retrieving the .stdout or .stderr
+# methods afterwards. Additionally we are able to have a higher fidelity
+# conversation with PowerShell because we are now working with the objects that
+# are returned by the script, rather than having to parse the stdout of
+# powershell.exe to get a result.
+#
+# *How does this work?* In .NET terms, when you run a PowerShell script block
+# through the engine, behind the scenes you get a Collection<PSObject> returned
+# and simply we are serializing this, adding any errors that were generated to
+# a custom JSON string transferred in memory to Ruby. The easiest way to
+# develop for this approach is to imagine that the last thing that happens in
+# your PowerShell script block is "ConvertTo-Json". That's exactly what we are
+# doing here behind the scenes.
+#
+# There are a handful of current limitations with this approach:
+# - Windows UAC elevation is controlled by the token assigned to the account
+# that Ruby.exe is running under.
+# - Terminating errors will result in a WIN32OLERuntimeError and typically are
+# handled as an exception.
+# - There are no return/error codes, as we are not shelling out to
+# powershell.exe but calling a method inline, no errors codes are returned.
+# - There is no settable timeout on powershell_exec method execution.
+# - It is not possible to impersonate another user running powershell, the
+# credentials of the user running Chef Client are used.
+#
+
+class Chef
+ module Mixin
+ module PowershellExec
+ # Run a command under PowerShell via a managed (.NET) COM interop API.
+ # This implementation requires the managed dll to be registered on the
+ # target machine.
+ #
+ # Requires: .NET Framework 4.0 or higher on the target machine.
+ #
+ # @param script [String] script to run
+ # @return [Chef::PowerShell] output
+ def powershell_exec(script)
+ Chef::PowerShell.new(script)
+ end
+ end
+ end
+end
diff --git a/lib/chef/mixin/powershell_out.rb b/lib/chef/mixin/powershell_out.rb
index 74de85f86f..1a757074c2 100644
--- a/lib/chef/mixin/powershell_out.rb
+++ b/lib/chef/mixin/powershell_out.rb
@@ -74,7 +74,7 @@ class Chef
# Helper to build a powershell command around the script to run.
#
# @param script [String] script to run
- # @retrurn [String] powershell command to execute
+ # @return [String] powershell command to execute
def build_powershell_command(script)
flags = [
# Hides the copyright banner at startup.
@@ -91,7 +91,7 @@ class Chef
"-InputFormat None",
]
- "powershell.exe #{flags.join(' ')} -Command \"#{script}\""
+ "powershell.exe #{flags.join(' ')} -Command \"#{script.gsub('"', '\"')}\""
end
end
end
diff --git a/lib/chef/mixin/powershell_type_coercions.rb b/lib/chef/mixin/powershell_type_coercions.rb
index 381cbed637..792ec18842 100644
--- a/lib/chef/mixin/powershell_type_coercions.rb
+++ b/lib/chef/mixin/powershell_type_coercions.rb
@@ -21,31 +21,31 @@ class Chef
module Mixin
module PowershellTypeCoercions
- def type_coercions
- @type_coercions ||= {
- Fixnum => { :type => lambda { |x| x.to_s } },
- Float => { :type => lambda { |x| x.to_s } },
- FalseClass => { :type => lambda { |x| "$false" } },
- TrueClass => { :type => lambda { |x| "$true" } },
- Hash => { :type => Proc.new { |x| translate_hash(x) } },
- Array => { :type => Proc.new { |x| translate_array(x) } },
- Chef::Node::ImmutableMash => { :type => Proc.new { |x| translate_hash(x) } },
- Chef::Node::ImmutableArray => { :type => Proc.new { |x| translate_array(x) } },
- }
+ def type_coercion(value)
+ case value
+ when Integer, Float
+ value.to_s
+ when FalseClass
+ "$false"
+ when TrueClass
+ "$true"
+ when Hash, Chef::Node::ImmutableMash
+ translate_hash(value)
+ when Array, Chef::Node::ImmutableArray
+ translate_array(value)
+ end
end
- def translate_type(value)
- translation = type_coercions[value.class]
-
- if translation
- translation[:type].call(value)
- elsif value.respond_to? :to_psobject
+ def psobject_conversion(value)
+ if value.respond_to?(:to_psobject)
"(#{value.to_psobject})"
- else
- safe_string(value.to_s)
end
end
+ def translate_type(value)
+ type_coercion(value) || psobject_conversion(value) || safe_string(value.to_s)
+ end
+
private
def translate_hash(x)
@@ -63,7 +63,7 @@ class Chef
end
def unsafe?(s)
- ["'", '#', "`", '"'].any? do |x|
+ ["'", "#", "`", '"'].any? do |x|
s.include? x
end
end
diff --git a/lib/chef/mixin/properties.rb b/lib/chef/mixin/properties.rb
index ae2406f1ae..fb765fbb3a 100644
--- a/lib/chef/mixin/properties.rb
+++ b/lib/chef/mixin/properties.rb
@@ -75,10 +75,15 @@ class Chef
# will return if the user does not set one. If this is `lazy`, it will
# be run in the context of the instance (and able to access other
# properties).
+ # @option options [String] :description A description of the property.
+ # @option options [String] :introduced The release that introduced this property
# @option options [Boolean] :desired_state `true` if this property is
# part of desired state. Defaults to `true`.
# @option options [Boolean] :identity `true` if this property
# is part of object identity. Defaults to `false`.
+ # @option options [Boolean] :sensitive `true` if this property could
+ # contain sensitive information and whose value should be redacted
+ # in any resource reporting / auditing output. Defaults to `false`.
#
# @example Bare property
# property :x
@@ -144,6 +149,10 @@ class Chef
Property.derive(**options)
end
+ def deprecated_property_alias(from, to, message)
+ Property.emit_deprecated_alias(from, to, message, self)
+ end
+
#
# Create a lazy value for assignment to a default value.
#
@@ -298,6 +307,17 @@ class Chef
raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
property.reset(self)
end
+
+ #
+ # The description of the property
+ #
+ # @param name [Symbol] The name of the property.
+ # @return [String] The description of the property.
+ def property_description(name)
+ property = self.class.properties[name.to_sym]
+ raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
+ property.description
+ end
end
end
end
diff --git a/lib/chef/mixin/provides.rb b/lib/chef/mixin/provides.rb
index 43a726de8c..ca045d019e 100644
--- a/lib/chef/mixin/provides.rb
+++ b/lib/chef/mixin/provides.rb
@@ -4,7 +4,7 @@ require "chef/mixin/descendants_tracker"
class Chef
module Mixin
module Provides
- # TODO no longer needed, remove or deprecate?
+ # @todo no longer needed, remove or deprecate?
include Chef::Mixin::DescendantsTracker
def provides(short_name, opts = {})
@@ -12,7 +12,8 @@ class Chef
end
# Check whether this resource provides the resource_name DSL for the given
- # node. TODO remove this when we stop checking unregistered things.
+ # node.
+ # @todo remove this when we stop checking unregistered things.
# FIXME: yard with @yield
def provides?(node, resource)
raise NotImplementedError, :provides?
diff --git a/lib/chef/mixin/recipe_definition_dsl_core.rb b/lib/chef/mixin/recipe_definition_dsl_core.rb
deleted file mode 100644
index 6a9b12d31a..0000000000
--- a/lib/chef/mixin/recipe_definition_dsl_core.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-#--
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-###
-# NOTE: This file and constant are here only for backwards compatibility.
-# New code should use Chef::DSL::Recipe instead.
-#
-# This constant (module name) will eventually be deprecated and then removed.
-###
-
-require "chef/mixin/deprecation"
-
-class Chef
- module Mixin
- deprecate_constant(:RecipeDefinitionDSLCore, Chef::DSL::Recipe, <<-EOM)
-Chef::Mixin::RecipeDefinitionDSLCore is deprecated. Use Chef::DSL::Recipe instead.
-EOM
- end
-end
diff --git a/lib/chef/mixin/securable.rb b/lib/chef/mixin/securable.rb
index a88d534b37..788b236d94 100644
--- a/lib/chef/mixin/securable.rb
+++ b/lib/chef/mixin/securable.rb
@@ -43,7 +43,7 @@ class Chef
:mode,
arg,
:callbacks => {
- "not in valid numeric range" => lambda { |m|
+ "not in valid numeric range" => lambda do |m|
if m.kind_of?(String)
m =~ /^0/ || m = "0#{m}"
end
@@ -54,22 +54,19 @@ class Chef
else
Integer(m) <= 07777 && Integer(m) >= 0
end
- },
+ end,
}
)
end
- #==WindowsMacros
# Defines methods for adding attributes to a chef resource to describe
# Windows file security metadata.
#
# This module is meant to be used to extend a class (instead of
# `include`-ing). A class is automatically extended with this module when
# it includes WindowsSecurableAttributes.
- # --
- # TODO should this be separated into different files?
+ # @todo should this be separated into different files?
module WindowsMacros
- # === rights_attribute
# "meta-method" for dynamically creating rights attributes on resources.
#
# Multiple rights attributes can be declared. This enables resources to
@@ -110,7 +107,7 @@ class Chef
# equivalent to something like:
# def rights(permissions=nil, principals=nil, args_hash=nil)
define_method(name) do |permissions = nil, principals = nil, args_hash = nil|
- rights = self.instance_variable_get("@#{name}".to_sym)
+ rights = instance_variable_get("@#{name}".to_sym)
unless permissions.nil?
input = {
:permissions => permissions,
@@ -162,7 +159,6 @@ class Chef
end
end
- #==WindowsSecurableAttributes
# Defines #inherits to describe Windows file security ACLs on the
# including class
module WindowsSecurableAttributes
diff --git a/lib/chef/mixin/shell_out.rb b/lib/chef/mixin/shell_out.rb
index 1f7deb21d2..57d5be4adc 100644
--- a/lib/chef/mixin/shell_out.rb
+++ b/lib/chef/mixin/shell_out.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright 2010-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +16,84 @@
# limitations under the License.
require "mixlib/shellout"
+require "chef/mixin/path_sanity"
class Chef
module Mixin
module ShellOut
+ include Chef::Mixin::PathSanity
+
+ # PREFERRED APIS:
+ #
+ # shell_out_compact and shell_out_compact! flatten their array arguments and remove nils and pass
+ # the resultant array to shell_out. this actually eliminates spaces-in-args bugs because this:
+ #
+ # shell_out!("command #{arg}")
+ #
+ # becomes two arguments if arg has spaces and requires quotations:
+ #
+ # shell_out!("command '#{arg}'")
+ #
+ # using shell_out_compact! this becomes:
+ #
+ # shell_out_compact!("command", arg)
+ #
+ # and spaces in the arg just works and it does not become two arguments (and the shell quoting around
+ # the argument must actually be removed).
+ #
+ # there's also an implicit join between all the array elements, and nested arrays are flattened which
+ # means that odd where-do-i-put-the-spaces options handling just works, and instead of this:
+ #
+ # opts = "" # needs to be empty string for when foo and bar are both missing
+ # opts << " -foo" if needs_foo? # needs the leading space on both of these
+ # opts << " -bar" if needs_bar?
+ # shell_out!("cmd#{opts}") # have to think way too hard about why there's no space here
+ #
+ # becomes:
+ #
+ # opts = []
+ # opts << "-foo" if needs_foo?
+ # opts << "-bar" if needs_bar?
+ # shell_out_compact!("cmd", opts)
+ #
+ # and opts can be an empty array or nil and it'll work out fine.
+ #
+ # generally its best to use shell_out_compact! in code and setup expectations on shell_out! in tests
+ #
+
+ def shell_out_compact(*args, **options)
+ if options.empty?
+ shell_out(*clean_array(*args))
+ else
+ shell_out(*clean_array(*args), **options)
+ end
+ end
+
+ def shell_out_compact!(*args, **options)
+ if options.empty?
+ shell_out!(*clean_array(*args))
+ else
+ shell_out!(*clean_array(*args), **options)
+ end
+ end
+
+ # helper sugar for resources that support passing timeouts to shell_out
+
+ def shell_out_compact_timeout(*args, **options)
+ raise "object is not a resource that supports timeouts" unless respond_to?(:new_resource) && new_resource.respond_to?(:timeout)
+ options_dup = options.dup
+ options_dup[:timeout] = new_resource.timeout if new_resource.timeout
+ options_dup[:timeout] = 900 unless options_dup.key?(:timeout)
+ shell_out_compact(*args, **options_dup)
+ end
+
+ def shell_out_compact_timeout!(*args, **options)
+ raise "object is not a resource that supports timeouts" unless respond_to?(:new_resource) && new_resource.respond_to?(:timeout)
+ options_dup = options.dup
+ options_dup[:timeout] = new_resource.timeout if new_resource.timeout
+ options_dup[:timeout] = 900 unless options_dup.key?(:timeout)
+ shell_out_compact!(*args, **options_dup)
+ end
# shell_out! runs a command on the system and will raise an error if the command fails, which is what you want
# for debugging, shell_out and shell_out! both will display command output to the tty when the log level is debug
@@ -35,6 +109,7 @@ class Chef
"LC_ALL" => Chef::Config[:internal_locale],
"LANGUAGE" => Chef::Config[:internal_locale],
"LANG" => Chef::Config[:internal_locale],
+ env_path => sanitized_path,
}.update(options[env_key] || {})
shell_out_command(*args, **options)
end
@@ -56,41 +131,47 @@ class Chef
cmd
end
- DEPRECATED_OPTIONS =
- [ [:command_log_level, :log_level],
- [:command_log_prepend, :log_tag] ]
-
- # CHEF-3090: Deprecate command_log_level and command_log_prepend
- # Patterned after https://github.com/opscode/chef/commit/e1509990b559984b43e428d4d801c394e970f432
- def run_command_compatible_options(command_args)
- return command_args unless command_args.last.is_a?(Hash)
-
- my_command_args = command_args.dup
- my_options = my_command_args.last
-
- DEPRECATED_OPTIONS.each do |old_option, new_option|
- # Edge case: someone specifies :command_log_level and 'command_log_level' in the option hash
- next unless value = my_options.delete(old_option) || my_options.delete(old_option.to_s)
- deprecate_option old_option, new_option
- my_options[new_option] = value
- end
+ # Helper for subclasses to convert an array of string args into a string. It
+ # will compact nil or empty strings in the array and will join the array elements
+ # with spaces, without introducing any double spaces for nil/empty elements.
+ #
+ # @param args [String] variable number of string arguments
+ # @return [String] nicely concatenated string or empty string
+ def a_to_s(*args)
+ # can't quite deprecate this yet
+ #Chef.deprecated(:package_misc, "a_to_s is deprecated use shell_out_compact or shell_out_compact_timeout instead")
+ args.flatten.reject { |i| i.nil? || i == "" }.map(&:to_s).join(" ")
+ end
- return my_command_args
+ # Helper for subclasses to reject nil out of an array. It allows
+ # using the array form of shell_out (which avoids the need to surround arguments with
+ # quote marks to deal with shells).
+ #
+ # Usage:
+ # shell_out!(*clean_array("useradd", universal_options, useradd_options, new_resource.username))
+ #
+ # universal_options and useradd_options can be nil, empty array, empty string, strings or arrays
+ # and the result makes sense.
+ #
+ # keeping this separate from shell_out!() makes it a bit easier to write expectations against the
+ # shell_out args and be able to omit nils and such in the tests (and to test that the nils are
+ # being rejected correctly).
+ #
+ # @param args [String] variable number of string arguments
+ # @return [Array] array of strings with nil and null string rejection
+ def clean_array(*args)
+ args.flatten.compact.map(&:to_s)
end
private
def shell_out_command(*command_args)
- cmd = Mixlib::ShellOut.new(*run_command_compatible_options(command_args))
+ cmd = Mixlib::ShellOut.new(*command_args)
cmd.live_stream ||= io_for_live_stream
cmd.run_command
cmd
end
- def deprecate_option(old_option, new_option)
- Chef.log_deprecation "DEPRECATION: Chef::Mixin::ShellOut option :#{old_option} is deprecated. Use :#{new_option}"
- end
-
def io_for_live_stream
if STDOUT.tty? && !Chef::Config[:daemon] && Chef::Log.debug?
STDOUT
@@ -98,6 +179,14 @@ class Chef
nil
end
end
+
+ def env_path
+ if Chef::Platform.windows?
+ "Path"
+ else
+ "PATH"
+ end
+ end
end
end
end
diff --git a/lib/chef/mixin/template.rb b/lib/chef/mixin/template.rb
index b10e036c4e..bb811aa758 100644
--- a/lib/chef/mixin/template.rb
+++ b/lib/chef/mixin/template.rb
@@ -23,7 +23,6 @@ class Chef
module Mixin
module Template
- # == ChefContext
# ChefContext was previously used to mix behavior into Erubis::Context so
# that it would be available to templates. This behavior has now moved to
# TemplateContext, but this module is still mixed in to the
@@ -32,7 +31,6 @@ class Chef
module ChefContext
end
- # == TemplateContext
# TemplateContext is the base context class for all templates in Chef. It
# defines user-facing extensions to the base Erubis::Context to provide
# enhanced features. Individual instances of TemplateContext can be
@@ -72,7 +70,7 @@ class Chef
# @return [String] recipe path
attr_reader :recipe_path
- # line in the recipe containing the template reosurce, e.g.:
+ # line in the recipe containing the template resource, e.g.:
# 2
#
# @return [String] recipe line
diff --git a/lib/chef/mixin/unformatter.rb b/lib/chef/mixin/unformatter.rb
index 7dad56b270..5663749e58 100644
--- a/lib/chef/mixin/unformatter.rb
+++ b/lib/chef/mixin/unformatter.rb
@@ -22,9 +22,9 @@ class Chef
def write(message)
data = message.match(/(\[.+?\] )?([\w]+):(.*)$/)
- self.send(data[2].downcase.chomp.to_sym, data[3].strip)
+ send(data[2].downcase.chomp.to_sym, data[3].strip)
rescue NoMethodError
- self.send(:info, message)
+ send(:info, message)
end
end
diff --git a/lib/chef/mixin/uris.rb b/lib/chef/mixin/uris.rb
index 24e8a4f9ed..ea55eefd8f 100644
--- a/lib/chef/mixin/uris.rb
+++ b/lib/chef/mixin/uris.rb
@@ -17,6 +17,7 @@
#
require "uri"
+require "addressable/uri"
class Chef
module Mixin
@@ -30,12 +31,10 @@ class Chef
end
def as_uri(source)
- begin
- URI.parse(source)
- rescue URI::InvalidURIError
- Chef::Log.warn("#{source} was an invalid URI. Trying to escape invalid characters")
- URI.parse(URI.escape(source))
- end
+ URI.parse(source)
+ rescue URI::InvalidURIError
+ Chef::Log.warn("#{source} was an invalid URI. Trying to escape invalid characters")
+ URI.parse(Addressable::URI.encode(source))
end
end
diff --git a/lib/chef/mixin/user_context.rb b/lib/chef/mixin/user_context.rb
new file mode 100644
index 0000000000..f5c2da1389
--- /dev/null
+++ b/lib/chef/mixin/user_context.rb
@@ -0,0 +1,55 @@
+#
+# Author:: Adam Edwards (<adamed@chef.io>)
+# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/util/windows/logon_session" if Chef::Platform.windows?
+
+class Chef
+ module Mixin
+ module UserContext
+
+ # valid values for authentication => :remote, :local
+ # When authentication = :local, we use the credentials to create a logon session against the local system, and then try to access the files.
+ # When authentication = :remote, we continue with the current user but pass the provided credentials to the remote system.
+ def with_user_context(user, password, domain = nil, authentication = :remote, &block)
+ unless Chef::Platform.windows?
+ raise Exceptions::UnsupportedPlatform, "User context impersonation is supported only on the Windows platform"
+ end
+
+ if ! block_given?
+ raise ArgumentError, "You must supply a block to `with_user_context`"
+ end
+
+ logon_session = nil
+
+ begin
+ if user
+ logon_session = Chef::Util::Windows::LogonSession.new(user, password, domain, authentication)
+ logon_session.open
+ logon_session.set_user_context
+ end
+ yield
+ ensure
+ logon_session.close if logon_session
+ end
+ end
+
+ protected(:with_user_context)
+
+ end
+ end
+end
diff --git a/lib/chef/mixin/versioned_api.rb b/lib/chef/mixin/versioned_api.rb
new file mode 100644
index 0000000000..17c9838d29
--- /dev/null
+++ b/lib/chef/mixin/versioned_api.rb
@@ -0,0 +1,84 @@
+#--
+# Copyright:: Copyright 2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ module Mixin
+ module VersionedAPI
+
+ def minimum_api_version(version = nil)
+ if version
+ @minimum_api_version = version
+ else
+ @minimum_api_version
+ end
+ end
+
+ end
+
+ module VersionedAPIFactory
+
+ def versioned_interfaces
+ @versioned_interfaces ||= []
+ end
+
+ def add_versioned_api_class(klass)
+ versioned_interfaces << klass
+ end
+
+ def versioned_api_class
+ get_class_for(:max_server_version)
+ end
+
+ def get_class_for(type)
+ versioned_interfaces.select do |klass|
+ version = klass.send(:minimum_api_version)
+ # min and max versions will be nil if we've not made a request to the server yet,
+ # in which case we'll just start with the highest version and see what happens
+ ServerAPIVersions.instance.min_server_version.nil? || (version >= ServerAPIVersions.instance.min_server_version && version <= ServerAPIVersions.instance.send(type))
+ end
+ .sort { |a, b| a.send(:minimum_api_version) <=> b.send(:minimum_api_version) }
+ .last
+ end
+
+ def def_versioned_delegator(method)
+ line_no = __LINE__; str = %{
+ def self.#{method}(*args, &block)
+ versioned_api_class.__send__(:#{method}, *args, &block)
+ end
+ }
+ module_eval(str, __FILE__, line_no)
+ end
+
+ # When teeing up an HTTP request, we need to be able to ask which API version we should use.
+ # Something in Net::HTTP seems to expect to strip headers, so we return this as a string.
+ def best_request_version
+ klass = get_class_for(:max_server_version)
+ klass.minimum_api_version.to_s
+ end
+
+ def possible_requests
+ versioned_interfaces.length
+ end
+
+ def new(*args)
+ object = versioned_api_class.allocate
+ object.send(:initialize, *args)
+ object
+ end
+ end
+ end
+end
diff --git a/lib/chef/mixin/which.rb b/lib/chef/mixin/which.rb
index 63c84883d5..8a825f55c9 100644
--- a/lib/chef/mixin/which.rb
+++ b/lib/chef/mixin/which.rb
@@ -1,6 +1,6 @@
#--
# Author:: Lamont Granquist <lamont@chef.io>
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright 2010-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,19 +18,32 @@
class Chef
module Mixin
module Which
- def which(cmd, opts = {})
- extra_path =
- if opts[:extra_path].nil?
- [ "/bin", "/usr/bin", "/sbin", "/usr/sbin" ]
- else
- [ opts[:extra_path] ].flatten
- end
- paths = ENV["PATH"].split(File::PATH_SEPARATOR) + extra_path
- paths.each do |path|
- filename = File.join(path, cmd)
- return filename if File.executable?(Chef.path_to(filename))
- end
- false
+ def which(*cmds, extra_path: nil, &block)
+ where(*cmds, extra_path: extra_path, &block).first || false
+ end
+
+ def where(*cmds, extra_path: nil, &block)
+ # NOTE: unnecessarily duplicates function of path_sanity
+ extra_path ||= [ "/bin", "/usr/bin", "/sbin", "/usr/sbin" ]
+ paths = env_path.split(File::PATH_SEPARATOR) + Array(extra_path)
+ cmds.map do |cmd|
+ paths.map do |path|
+ filename = Chef.path_to(File.join(path, cmd))
+ filename if valid_executable?(filename, &block)
+ end.compact
+ end.flatten
+ end
+
+ private
+
+ # for test stubbing
+ def env_path
+ ENV["PATH"]
+ end
+
+ def valid_executable?(filename, &block)
+ return false unless File.executable?(filename) && !File.directory?(filename)
+ block ? yield(filename) : true
end
end
end
diff --git a/lib/chef/mixin/why_run.rb b/lib/chef/mixin/why_run.rb
index ea62527bdd..b27458feae 100644
--- a/lib/chef/mixin/why_run.rb
+++ b/lib/chef/mixin/why_run.rb
@@ -21,7 +21,6 @@ class Chef
module Mixin
module WhyRun
- # ==ConvergeActions
# ConvergeActions implements the logic for why run. A ConvergeActions
# object wraps a collection of actions, which consist of a descriptive
# string and a block/Proc. Actions are executed by calling #converge!
@@ -60,7 +59,6 @@ class Chef
end
end
- # == ResourceRequirements
# ResourceRequirements provides a framework for making assertions about
# the host system's state. It also provides a mechanism for making
# assumptions about what the system's state might have been when running
@@ -182,7 +180,7 @@ class Chef
# will be allowed to continue in both whyrun and non-whyrun
# mode
#
- # With a service resource that requires /etc/init.d/service-name to exist:
+ # @example With a service resource that requires /etc/init.d/service-name to exist:
# # in a provider
# assert(:start, :restart) do |a|
# a.assertion { ::File.exist?("/etc/init.d/service-name") }
@@ -264,8 +262,8 @@ class Chef
# Define a new Assertion.
#
# Takes a list of action names for which the assertion should be made.
- # ==== Examples:
- # A File provider that requires the parent directory to exist:
+ #
+ # @example A File provider that requires the parent directory to exist:
#
# assert(:create, :create_if_missing) do |a|
# parent_dir = File.basename(@new_resource.path)
@@ -275,7 +273,7 @@ class Chef
# a.why_run("assuming parent directory #{parent_dir} would have been previously created"
# end
#
- # A service provider that requires the init script to exist:
+ # @example A service provider that requires the init script to exist:
#
# assert(:start, :restart) do |a|
# a.assertion { ::File.exist?(@new_resource.init_script) }
@@ -286,17 +284,14 @@ class Chef
# end
# end
#
- # A File provider that will error out if you don't have permissions do
- # delete the file, *even in why run mode*:
- #
+ # @example A File provider that will error out if you don't have permissions do delete the file, *even in why run mode*:
# assert(:delete) do |a|
# a.assertion { ::File.writable?(@new_resource.path) }
# a.failure_message(Exceptions::InsufficientPrivileges,
# "You don't have sufficient privileges to delete #{@new_resource.path}")
# end
#
- # A Template provider that will prevent action execution but continue the run in
- # whyrun mode if the template source is not available.
+ # @example A Template provider that will prevent action execution but continue the run in whyrun mode if the template source is not available.
# assert(:create, :create_if_missing) do |a|
# a.assertion { File::exist?(@new_resource.source) }
# a.failure_message Chef::Exceptions::TemplateError, "Template #{@new_resource.source} could not be found exist."
diff --git a/lib/chef/mixin/wide_string.rb b/lib/chef/mixin/wide_string.rb
index 4342ef1650..ef7828e2d8 100644
--- a/lib/chef/mixin/wide_string.rb
+++ b/lib/chef/mixin/wide_string.rb
@@ -34,7 +34,7 @@ class Chef
ustring = (ustring + "").force_encoding("UTF-8") if ustring.respond_to?(:force_encoding) && ustring.encoding.name != "UTF-8"
# ensure we have the double-null termination Windows Wide likes
- ustring = ustring + "\000\000" if ustring.length == 0 || ustring[-1].chr != "\000"
+ ustring += "\000\000" if ustring.length == 0 || ustring[-1].chr != "\000"
# encode it all as UTF-16LE AKA Windows Wide Character AKA Windows Unicode
ustring = begin
diff --git a/lib/chef/mixin/windows_architecture_helper.rb b/lib/chef/mixin/windows_architecture_helper.rb
index 11e1b5559d..67c34a85a1 100644
--- a/lib/chef/mixin/windows_architecture_helper.rb
+++ b/lib/chef/mixin/windows_architecture_helper.rb
@@ -74,16 +74,15 @@ class Chef
def node_supports_windows_architecture?(node, desired_architecture)
assert_valid_windows_architecture!(desired_architecture)
- return (node_windows_architecture(node) == :x86_64 ||
- desired_architecture == :i386) ? true : false
+ ( node_windows_architecture(node) == :x86_64 ) || ( desired_architecture == :i386 )
end
def valid_windows_architecture?(architecture)
- return (architecture == :x86_64) || (architecture == :i386)
+ ( architecture == :x86_64 ) || ( architecture == :i386 )
end
def assert_valid_windows_architecture!(architecture)
- if ! valid_windows_architecture?(architecture)
+ if !valid_windows_architecture?(architecture)
raise Chef::Exceptions::Win32ArchitectureIncorrect,
"The specified architecture was not valid. It must be one of :i386 or :x86_64"
end
diff --git a/lib/chef/mixin/xml_escape.rb b/lib/chef/mixin/xml_escape.rb
index f9a41b9fd0..243bb7f044 100644
--- a/lib/chef/mixin/xml_escape.rb
+++ b/lib/chef/mixin/xml_escape.rb
@@ -103,11 +103,9 @@ class Chef
(0xE000..0xFFFD), (0x10000..0x10FFFF)]
def xml_escape(unescaped_str)
- begin
- unescaped_str.unpack("U*").map { |char| xml_escape_char!(char) }.join
- rescue
- unescaped_str.unpack("C*").map { |char| xml_escape_char!(char) }.join
- end
+ unescaped_str.unpack("U*").map { |char| xml_escape_char!(char) }.join
+ rescue
+ unescaped_str.unpack("C*").map { |char| xml_escape_char!(char) }.join
end
private
diff --git a/lib/chef/mixins.rb b/lib/chef/mixins.rb
index b980e81287..2306fb9e89 100644
--- a/lib/chef/mixins.rb
+++ b/lib/chef/mixins.rb
@@ -1,6 +1,5 @@
require "chef/mixin/shell_out"
require "chef/mixin/checksum"
-require "chef/mixin/command"
require "chef/mixin/convert_to_class_name"
require "chef/mixin/create_path"
require "chef/mixin/deep_merge"
diff --git a/lib/chef/monkey_patches/net-ssh-multi.rb b/lib/chef/monkey_patches/net-ssh-multi.rb
deleted file mode 100644
index 7b7b1bbf7f..0000000000
--- a/lib/chef/monkey_patches/net-ssh-multi.rb
+++ /dev/null
@@ -1,141 +0,0 @@
-#
-# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# == net-ssh-multi gem patch for concurrency
-# net-ssh-multi gem has 2 bugs associated with the use of
-# :concurrent_connections option.
-# 1-) There is a race condition while fetching the next_session when
-# :concurrent_connections are set. @open_connections is being
-# incremented by the connection thread and sometimes
-# realize_pending_connections!() method can create more than required
-# connection threads before the @open_connections is set by the
-# previously created threads.
-# 2-) When :concurrent_connections is set, server classes are setup
-# with PendingConnection objects that always return true to busy?
-# calls. If a connection fails when :concurrent_connections is set,
-# server ends up returning true to all busy? calls since the session
-# object is not replaced. Due to this, main event loop (process()
-# function) never gets terminated.
-#
-# See: https://github.com/net-ssh/net-ssh-multi/pull/4
-
-require "net/ssh/multi/version"
-
-if Net::SSH::Multi::Version::STRING == "1.1.0" || Net::SSH::Multi::Version::STRING == "1.2.0"
-
- require "net/ssh/multi"
-
- module Net
- module SSH
- module Multi
- class Server
-
- # Make sure that server returns false if the ssh connection
- # has failed.
- def busy?(include_invisible = false)
- !failed? && session && session.busy?(include_invisible)
- end
-
- end
-
- class Session
- def next_session(server, force = false) #:nodoc:
- # don't retry a failed attempt
- return nil if server.failed?
-
- @session_mutex.synchronize do
- if !force && concurrent_connections && concurrent_connections <= open_connections
- connection = PendingConnection.new(server)
- @pending_sessions << connection
- return connection
- end
-
- # ===== PATCH START
- # Only increment the open_connections count if the connection
- # is not being forced. Incase of a force, it will already be
- # incremented.
- if !force
- @open_connections += 1
- end
- # ===== PATCH END
- end
-
- begin
- server.new_session
-
- # I don't understand why this should be necessary--StandardError is a
- # subclass of Exception, after all--but without explicitly rescuing
- # StandardError, things like Errno::* and SocketError don't get caught
- # here!
- rescue Exception, StandardError => e
- server.fail!
- @session_mutex.synchronize { @open_connections -= 1 }
-
- case on_error
- when :ignore then
- # do nothing
- when :warn then
- warn("error connecting to #{server}: #{e.class} (#{e.message})")
- when Proc then
- go = catch(:go) { on_error.call(server); nil }
- case go
- when nil, :ignore then # nothing
- when :retry then retry
- when :raise then raise
- else warn "unknown 'go' command: #{go.inspect}"
- end
- else
- raise
- end
-
- return nil
- end
- end
-
- def realize_pending_connections! #:nodoc:
- return unless concurrent_connections
-
- server_list.each do |server|
- server.close if !server.busy?(true)
- server.update_session!
- end
-
- @connect_threads.delete_if { |t| !t.alive? }
-
- count = concurrent_connections ? (concurrent_connections - open_connections) : @pending_sessions.length
- count.times do
- session = @pending_sessions.pop
- break unless session
- # ===== PATCH START
- # Increment the open_connections count here to prevent
- # creation of connection thread again before that is
- # incremented by the thread.
- @session_mutex.synchronize { @open_connections += 1 }
- # ===== PATCH END
- @connect_threads << Thread.new do
- session.replace_with(next_session(session.server, true))
- end
- end
- end
-
- end
- end
- end
- end
-
-end
diff --git a/lib/chef/monkey_patches/net_http.rb b/lib/chef/monkey_patches/net_http.rb
index c1cb87bffd..15ccf0fff5 100644
--- a/lib/chef/monkey_patches/net_http.rb
+++ b/lib/chef/monkey_patches/net_http.rb
@@ -33,13 +33,13 @@ if Net::HTTP.instance_methods.map { |m| m.to_s }.include?("proxy_uri")
#
# ruby -r'net/http' -e 'Net::HTTP.new("::1", 80).proxy_uri'
# /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/generic.rb:214:in `initialize': the scheme http does not accept registry part: ::1:80 (or bad hostname?) (URI::InvalidURIError)
- # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/http.rb:84:in `initialize'
- # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/common.rb:214:in `new'
- # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/common.rb:214:in `parse'
- # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/common.rb:747:in `parse'
- # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/common.rb:994:in `URI'
- # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/net/http.rb:1027:in `proxy_uri'
- # from -e:1:in `<main>'
+ # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/http.rb:84:in `initialize'
+ # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/common.rb:214:in `new'
+ # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/common.rb:214:in `parse'
+ # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/common.rb:747:in `parse'
+ # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/common.rb:994:in `URI'
+ # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/net/http.rb:1027:in `proxy_uri'
+ # from -e:1:in `<main>'
#
# https://bugs.ruby-lang.org/issues/9129
#
diff --git a/lib/chef/monkey_patches/webrick-utils.rb b/lib/chef/monkey_patches/webrick-utils.rb
index 7880adb202..7f525f122f 100644
--- a/lib/chef/monkey_patches/webrick-utils.rb
+++ b/lib/chef/monkey_patches/webrick-utils.rb
@@ -31,7 +31,7 @@ module WEBrick
Socket::AI_PASSIVE) # flag
last_error = nil
sockets = []
- res.each {|ai|
+ res.each do |ai|
begin
logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger
sock = TCPServer.new(ai[3], port)
@@ -42,9 +42,9 @@ module WEBrick
logger.warn("TCPServer Error: #{ex}") if logger
last_error = ex
end
- }
+ end
raise last_error if sockets.empty?
- return sockets
+ sockets
end
module_function :create_listeners
end
diff --git a/lib/chef/monkey_patches/win32/registry.rb b/lib/chef/monkey_patches/win32/registry.rb
index 1c1a103432..a08d67becf 100644
--- a/lib/chef/monkey_patches/win32/registry.rb
+++ b/lib/chef/monkey_patches/win32/registry.rb
@@ -22,6 +22,17 @@ require "win32/registry"
module Win32
class Registry
+ # ::Win32::Registry#export_string is used when enumerating child
+ # keys and values and re encodes a UTF-16LE to the local codepage.
+ # This can result in encoding incompatibilities if the native codepage
+ # does not support the characters in the registry. There is an open bug
+ # in ruby at https://bugs.ruby-lang.org/issues/11410. Rather than converting
+ # the UTF-16LE originally returned by the win32 api, we encode to UTF-8
+ # which will likely not result in any conversion error.
+ def export_string(str, enc = Encoding.default_internal || "utf-8")
+ str.encode(enc)
+ end
+
module API
extend Chef::ReservedNames::Win32::API::Registry
diff --git a/lib/chef/monologger.rb b/lib/chef/monologger.rb
index 8bcdae1293..0bd262bd85 100644
--- a/lib/chef/monologger.rb
+++ b/lib/chef/monologger.rb
@@ -1,88 +1,4 @@
-require "logger"
+require "mixlib/log/logger"
require "pp"
-#== MonoLogger
-# A subclass of Ruby's stdlib Logger with all the mutex and logrotation stuff
-# ripped out.
-class MonoLogger < Logger
-
- #
- # === Synopsis
- #
- # Logger.new(name, shift_age = 7, shift_size = 1048576)
- # Logger.new(name, shift_age = 'weekly')
- #
- # === Args
- #
- # +logdev+::
- # The log device. This is a filename (String) or IO object (typically
- # +STDOUT+, +STDERR+, or an open file).
- # +shift_age+::
- # Number of old log files to keep, *or* frequency of rotation (+daily+,
- # +weekly+ or +monthly+).
- # +shift_size+::
- # Maximum logfile size (only applies when +shift_age+ is a number).
- #
- # === Description
- #
- # Create an instance.
- #
- def initialize(logdev)
- @progname = nil
- @level = DEBUG
- @default_formatter = Formatter.new
- @formatter = nil
- @logdev = nil
- if logdev
- @logdev = LocklessLogDevice.new(logdev)
- end
- end
-
- class LocklessLogDevice < LogDevice
-
- def initialize(log = nil)
- @dev = @filename = @shift_age = @shift_size = nil
- if log.respond_to?(:write) && log.respond_to?(:close)
- @dev = log
- else
- @dev = open_logfile(log)
- @filename = log
- end
- @dev.sync = true
- end
-
- def write(message)
- @dev.write(message)
- rescue Exception => ignored
- warn("log writing failed. #{ignored}")
- end
-
- def close
- @dev.close rescue nil
- end
-
- private
-
- def open_logfile(filename)
- if FileTest.exist?(filename)
- open(filename, (File::WRONLY | File::APPEND))
- else
- create_logfile(filename)
- end
- end
-
- def create_logfile(filename)
- logdev = open(filename, (File::WRONLY | File::APPEND | File::CREAT))
- add_log_header(logdev)
- logdev
- end
-
- def add_log_header(file)
- file.write(
- "# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName]
- )
- end
-
- end
-
-end
+MonoLogger = Mixlib::Log::Logger
diff --git a/lib/chef/node.rb b/lib/chef/node.rb
index 294358f405..f03e0ca0b1 100644
--- a/lib/chef/node.rb
+++ b/lib/chef/node.rb
@@ -2,7 +2,7 @@
# Author:: Christopher Brown (<cb@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Tim Hinderliter (<tim@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,7 +25,7 @@ require "chef/mixin/params_validate"
require "chef/mixin/from_file"
require "chef/mixin/deep_merge"
require "chef/dsl/include_attribute"
-require "chef/dsl/platform_introspection"
+require "chef/dsl/universal"
require "chef/environment"
require "chef/server_api"
require "chef/run_list"
@@ -34,6 +34,7 @@ require "chef/mash"
require "chef/json_compat"
require "chef/search/query"
require "chef/whitelist"
+require "chef/blacklist"
class Chef
class Node
@@ -43,10 +44,12 @@ class Chef
def_delegators :attributes, :keys, :each_key, :each_value, :key?, :has_key?
def_delegators :attributes, :rm, :rm_default, :rm_normal, :rm_override
def_delegators :attributes, :default!, :normal!, :override!, :force_default!, :force_override!
+ def_delegators :attributes, :default_unless, :normal_unless, :override_unless, :set_unless
+ def_delegators :attributes, :read, :read!, :write, :write!, :unlink, :unlink!
- attr_accessor :recipe_list, :run_state, :override_runlist
+ attr_accessor :recipe_list, :run_state
- attr_accessor :chef_server_rest
+ attr_reader :logger
# RunContext will set itself as run_context via this setter when
# initialized. This is needed so DSL::IncludeAttribute (in particular,
@@ -58,16 +61,17 @@ class Chef
include Chef::Mixin::FromFile
include Chef::DSL::IncludeAttribute
- include Chef::DSL::PlatformIntrospection
+ include Chef::DSL::Universal
include Chef::Mixin::ParamsValidate
NULL_ARG = Object.new
# Create a new Chef::Node object.
- def initialize(chef_server_rest: nil)
+ def initialize(chef_server_rest: nil, logger: nil)
@chef_server_rest = chef_server_rest
@name = nil
+ @logger = logger || Chef::Log.with_child(subsystem: "node")
@chef_environment = "_default"
@primary_runlist = Chef::RunList.new
@@ -76,7 +80,7 @@ class Chef
@policy_name = nil
@policy_group = nil
- @attributes = Chef::Node::Attribute.new({}, {}, {}, {})
+ @attributes = Chef::Node::Attribute.new({}, {}, {}, {}, self)
@run_state = {}
end
@@ -109,7 +113,7 @@ class Chef
# Set the name of this Node, or return the current name.
def name(arg = nil)
- if arg != nil
+ if !arg.nil?
validate(
{ :name => arg },
{ :name => { :kind_of => String,
@@ -195,52 +199,18 @@ class Chef
# Set a normal attribute of this node, but auto-vivify any Mashes that
# might be missing
def normal
- attributes.top_level_breadcrumb = nil
- attributes.set_unless_value_present = false
attributes.normal
end
- alias_method :set, :normal
-
- # Set a normal attribute of this node, auto-vivifying any mashes that are
- # missing, but if the final value already exists, don't set it
- def normal_unless
- attributes.top_level_breadcrumb = nil
- attributes.set_unless_value_present = true
- attributes.normal
- end
-
- alias_method :set_unless, :normal_unless
-
# Set a default of this node, but auto-vivify any Mashes that might
# be missing
def default
- attributes.top_level_breadcrumb = nil
- attributes.set_unless_value_present = false
- attributes.default
- end
-
- # Set a default attribute of this node, auto-vivifying any mashes that are
- # missing, but if the final value already exists, don't set it
- def default_unless
- attributes.top_level_breadcrumb = nil
- attributes.set_unless_value_present = true
attributes.default
end
# Set an override attribute of this node, but auto-vivify any Mashes that
# might be missing
def override
- attributes.top_level_breadcrumb = nil
- attributes.set_unless_value_present = false
- attributes.override
- end
-
- # Set an override attribute of this node, auto-vivifying any mashes that
- # are missing, but if the final value already exists, don't set it
- def override_unless
- attributes.top_level_breadcrumb = nil
- attributes.set_unless_value_present = true
attributes.override
end
@@ -261,8 +231,6 @@ class Chef
end
def automatic_attrs
- attributes.top_level_breadcrumb = nil
- attributes.set_unless_value_present = false
attributes.automatic
end
@@ -290,8 +258,14 @@ class Chef
end
# Only works for attribute fetches, setting is no longer supported
- def method_missing(symbol, *args)
- attributes.send(symbol, *args)
+ # XXX: this should be deprecated
+ def method_missing(method, *args, &block)
+ attributes.public_send(method, *args, &block)
+ end
+
+ # Fix respond_to + method so that it works with method_missing delegation
+ def respond_to_missing?(method, include_private = false)
+ attributes.respond_to?(method, false)
end
# Returns true if this Node expects a given recipe, false if not.
@@ -314,14 +288,18 @@ class Chef
end
# Returns true if this Node expects a given role, false if not.
+ #
+ # @param role_name [String] Role to check for
+ # @return [Boolean]
def role?(role_name)
- run_list.include?("role[#{role_name}]")
+ Array(self[:roles]).include?(role_name)
end
def primary_runlist
@primary_runlist
end
+ attr_writer :override_runlist
def override_runlist(*args)
args.length > 0 ? @override_runlist.reset!(args) : @override_runlist
end
@@ -349,24 +327,34 @@ class Chef
# Consume data from ohai and Attributes provided as JSON on the command line.
def consume_external_attrs(ohai_data, json_cli_attrs)
- Chef::Log.debug("Extracting run list from JSON attributes provided on command line")
+ # FIXME(log): should be trace
+ logger.debug("Extracting run list from JSON attributes provided on command line")
consume_attributes(json_cli_attrs)
self.automatic_attrs = ohai_data
platform, version = Chef::Platform.find_platform_and_version(self)
- Chef::Log.debug("Platform is #{platform} version #{version}")
- self.automatic[:platform] = platform
- self.automatic[:platform_version] = version
+ # FIXME(log): should be trace
+ logger.debug("Platform is #{platform} version #{version}")
+ automatic[:platform] = platform
+ automatic[:platform_version] = version
+ automatic[:chef_guid] = Chef::Config[:chef_guid]
+ automatic[:name] = name
+ automatic[:chef_environment] = chef_environment
+ end
+
+ def consume_ohai_data(ohai_data)
+ self.automatic_attrs = Chef::Mixin::DeepMerge.merge(automatic_attrs, ohai_data)
end
# Consumes the combined run_list and other attributes in +attrs+
def consume_attributes(attrs)
normal_attrs_to_merge = consume_run_list(attrs)
normal_attrs_to_merge = consume_chef_environment(normal_attrs_to_merge)
- Chef::Log.debug("Applying attributes from json file")
+ # FIXME(log): should be trace
+ logger.debug("Applying attributes from json file")
self.normal_attrs = Chef::Mixin::DeepMerge.merge(normal_attrs, normal_attrs_to_merge)
- self.tags # make sure they're defined
+ tags # make sure they're defined
end
# Lazy initializer for tags attribute
@@ -390,7 +378,7 @@ class Chef
if attrs.key?("recipes") || attrs.key?("run_list")
raise Chef::Exceptions::AmbiguousRunlistSpecification, "please set the node's run list using the 'run_list' attribute only."
end
- Chef::Log.info("Setting the run_list to #{new_run_list} from CLI options")
+ logger.info("Setting the run_list to #{new_run_list} from CLI options")
run_list(new_run_list)
end
attrs
@@ -417,8 +405,8 @@ class Chef
# Clear defaults and overrides, so that any deleted attributes
# between runs are still gone.
def reset_defaults_and_overrides
- self.default.clear
- self.override.clear
+ default.clear
+ override.clear
end
# Expands the node's run list and sets the default and override
@@ -437,7 +425,7 @@ class Chef
expansion = run_list.expand(chef_environment, data_source)
raise Chef::Exceptions::MissingRole, expansion if expansion.errors?
- self.tags # make sure they're defined
+ tags # make sure they're defined
automatic_attrs[:recipes] = expansion.recipes.with_duplicate_names
automatic_attrs[:expanded_run_list] = expansion.recipes.with_fully_qualified_names_and_version_constraints
@@ -445,6 +433,7 @@ class Chef
apply_expansion_attributes(expansion)
+ automatic_attrs[:chef_environment] = chef_environment
expansion
end
@@ -530,17 +519,20 @@ class Chef
self
end
- # Create a Chef::Node from JSON
- def self.json_create(o)
- Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please use Chef::Node#from_hash")
- from_hash(o)
- end
-
def self.from_hash(o)
return o if o.kind_of? Chef::Node
node = new
node.name(o["name"])
- node.chef_environment(o["chef_environment"])
+
+ node.policy_name = o["policy_name"] if o.has_key?("policy_name")
+ node.policy_group = o["policy_group"] if o.has_key?("policy_group")
+
+ unless node.policy_group.nil?
+ node.chef_environment(o["policy_group"])
+ else
+ node.chef_environment(o["chef_environment"])
+ end
+
if o.has_key?("attributes")
node.normal_attrs = o["attributes"]
end
@@ -555,9 +547,6 @@ class Chef
o["recipes"].each { |r| node.recipes << r }
end
- node.policy_name = o["policy_name"] if o.has_key?("policy_name")
- node.policy_group = o["policy_group"] if o.has_key?("policy_group")
-
node
end
@@ -615,18 +604,13 @@ class Chef
# so then POST to create.
begin
if Chef::Config[:why_run]
- Chef::Log.warn("In why-run mode, so NOT performing node save.")
+ logger.warn("In why-run mode, so NOT performing node save.")
else
chef_server_rest.put("nodes/#{name}", data_for_save)
end
rescue Net::HTTPServerException => e
if e.response.code == "404"
chef_server_rest.post("nodes", data_for_save)
- # Chef Server before 12.3 rejects node JSON with 'policy_name' or
- # 'policy_group' keys, but 'policy_name' will be detected first.
- # Backcompat can be removed in 13.0
- elsif e.response.code == "400" && e.response.body.include?("Invalid key policy_name")
- save_without_policyfile_attrs
else
raise
end
@@ -655,14 +639,14 @@ class Chef
def ==(other)
if other.kind_of?(self.class)
- self.name == other.name
+ name == other.name
else
false
end
end
- def <=>(other_node)
- self.name <=> other_node.name
+ def <=>(other)
+ name <=> other.name
end
private
@@ -689,9 +673,16 @@ class Chef
whitelist_config_option = "#{level}_attribute_whitelist".to_sym
whitelist = Chef::Config[whitelist_config_option]
unless whitelist.nil? # nil => save everything
- Chef::Log.info("Whitelisting #{level} node attributes for save.")
+ logger.info("Whitelisting #{level} node attributes for save.")
data[level] = Chef::Whitelist.filter(data[level], whitelist)
end
+
+ blacklist_config_option = "#{level}_attribute_blacklist".to_sym
+ blacklist = Chef::Config[blacklist_config_option]
+ unless blacklist.nil? # nil => remove nothing
+ logger.info("Blacklisting #{level} node attributes for save")
+ data[level] = Chef::Blacklist.filter(data[level], blacklist)
+ end
end
data
end
diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb
index ab97cf99bf..cd6061e24f 100644
--- a/lib/chef/node/attribute.rb
+++ b/lib/chef/node/attribute.rb
@@ -1,7 +1,7 @@
#--
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,8 +17,12 @@
# limitations under the License.
#
+require "chef/node/mixin/deep_merge_cache"
+require "chef/node/mixin/immutablize_hash"
+require "chef/node/mixin/state_tracking"
require "chef/node/immutable_collections"
require "chef/node/attribute_collections"
+require "chef/decorator/unchain"
require "chef/mixin/deep_merge"
require "chef/log"
@@ -33,9 +37,18 @@ class Chef
class Attribute < Mash
include Immutablize
-
+ # FIXME: what is include Enumerable doing up here, when down below we delegate
+ # most of the Enumerable/Hash things to the underlying merged ImmutableHash. That
+ # is, in fact, the correct, thing to do, while including Enumerable to try to create
+ # a hash-like API gets lots of things wrong because of the difference between the
+ # Hash `each do |key, value|` vs the Array-like `each do |value|` API that Enumerable
+ # expects. This include should probably be deleted?
include Enumerable
+ include Chef::Node::Mixin::DeepMergeCache
+ include Chef::Node::Mixin::StateTracking
+ include Chef::Node::Mixin::ImmutablizeHash
+
# List of the component attribute hashes, in order of precedence, low to
# high.
COMPONENTS = [
@@ -132,6 +145,7 @@ class Chef
:take,
:take_while,
:to_a,
+ :to_h,
:to_hash,
:to_set,
:value?,
@@ -173,146 +187,101 @@ class Chef
# return the automatic level attribute component
attr_reader :automatic
- # This is used to track the top level key as we descend through method chaining into
- # a precedence level (e.g. node.default['foo']['bar']['baz']= results in 'foo' here). We
- # need this so that when we hit the end of a method chain which results in a mutator method
- # that we can invalidate the whole top-level deep merge cache for the top-level key. It is
- # the responsibility of the accessor on the Chef::Node object to reset this to nil, and then
- # the first VividMash#[] call can ||= and set this to the first key we encounter.
- attr_accessor :top_level_breadcrumb
-
- # Cache of deep merged values by top-level key. This is a simple hash which has keys that are the
- # top-level keys of the node object, and we save the computed deep-merge for that key here. There is
- # no cache of subtrees.
- attr_accessor :deep_merge_cache
+ def initialize(normal, default, override, automatic, node = nil)
+ @default = VividMash.new(default, self, node, :default)
+ @env_default = VividMash.new({}, self, node, :env_default)
+ @role_default = VividMash.new({}, self, node, :role_default)
+ @force_default = VividMash.new({}, self, node, :force_default)
- def initialize(normal, default, override, automatic)
- @set_unless_present = false
+ @normal = VividMash.new(normal, self, node, :normal)
- @default = VividMash.new(self, default)
- @env_default = VividMash.new(self, {})
- @role_default = VividMash.new(self, {})
- @force_default = VividMash.new(self, {})
+ @override = VividMash.new(override, self, node, :override)
+ @role_override = VividMash.new({}, self, node, :role_override)
+ @env_override = VividMash.new({}, self, node, :env_override)
+ @force_override = VividMash.new({}, self, node, :force_override)
- @normal = VividMash.new(self, normal)
+ @automatic = VividMash.new(automatic, self, node, :automatic)
- @override = VividMash.new(self, override)
- @role_override = VividMash.new(self, {})
- @env_override = VividMash.new(self, {})
- @force_override = VividMash.new(self, {})
-
- @automatic = VividMash.new(self, automatic)
-
- @merged_attributes = nil
- @combined_override = nil
- @combined_default = nil
- @top_level_breadcrumb = nil
- @deep_merge_cache = {}
+ super(nil, self, node, :merged)
end
# Debug what's going on with an attribute. +args+ is a path spec to the
# attribute you're interested in. For example, to debug where the value
# of `node[:network][:default_interface]` is coming from, use:
# debug_value(:network, :default_interface).
- # The return value is an Array of Arrays. The first element is
- # `["set_unless_enabled?", Boolean]`, which describes whether the
- # attribute collection is in "set_unless" mode. The rest of the Arrays
+ # The return value is an Array of Arrays. The Arrays
# are pairs of `["precedence_level", value]`, where precedence level is
# the component, such as role default, normal, etc. and value is the
# attribute value set at that precedence level. If there is no value at
# that precedence level, +value+ will be the symbol +:not_present+.
def debug_value(*args)
- components = COMPONENTS.map do |component|
- ivar = instance_variable_get(component)
- value = args.inject(ivar) do |so_far, key|
- if so_far == :not_present
- :not_present
- elsif so_far.has_key?(key)
- so_far[key]
- else
+ COMPONENTS.map do |component|
+ value =
+ begin
+ instance_variable_get(component).read!(*args)
+ rescue
:not_present
end
- end
[component.to_s.sub(/^@/, ""), value]
end
- [["set_unless_enabled?", @set_unless_present]] + components
- end
-
- # Enables or disables `||=`-like attribute setting. See, e.g., Node#set_unless
- def set_unless_value_present=(setting)
- @set_unless_present = setting
- end
-
- # Invalidate a key in the deep_merge_cache. If called with nil, or no arg, this will invalidate
- # the entire deep_merge cache. In the case of the user doing node.default['foo']['bar']['baz']=
- # that eventually results in a call to reset_cache('foo') here. A node.default=hash_thing call
- # must invalidate the entire cache and re-deep-merge the entire node object.
- def reset_cache(path = nil)
- if path.nil?
- @deep_merge_cache = {}
- else
- deep_merge_cache.delete(path.to_s)
- end
end
- alias :reset :reset_cache
-
# Set the cookbook level default attribute component to +new_data+.
def default=(new_data)
reset
- @default = VividMash.new(self, new_data)
+ @default = VividMash.new(new_data, self, __node__, :default)
end
# Set the role level default attribute component to +new_data+
def role_default=(new_data)
reset
- @role_default = VividMash.new(self, new_data)
+ @role_default = VividMash.new(new_data, self, __node__, :role_default)
end
# Set the environment level default attribute component to +new_data+
def env_default=(new_data)
reset
- @env_default = VividMash.new(self, new_data)
+ @env_default = VividMash.new(new_data, self, __node__, :env_default)
end
# Set the force_default (+default!+) level attributes to +new_data+
def force_default=(new_data)
reset
- @force_default = VividMash.new(self, new_data)
+ @force_default = VividMash.new(new_data, self, __node__, :force_default)
end
# Set the normal level attribute component to +new_data+
def normal=(new_data)
reset
- @normal = VividMash.new(self, new_data)
+ @normal = VividMash.new(new_data, self, __node__, :normal)
end
# Set the cookbook level override attribute component to +new_data+
def override=(new_data)
reset
- @override = VividMash.new(self, new_data)
+ @override = VividMash.new(new_data, self, __node__, :override)
end
# Set the role level override attribute component to +new_data+
def role_override=(new_data)
reset
- @role_override = VividMash.new(self, new_data)
+ @role_override = VividMash.new(new_data, self, __node__, :role_override)
end
# Set the environment level override attribute component to +new_data+
def env_override=(new_data)
reset
- @env_override = VividMash.new(self, new_data)
+ @env_override = VividMash.new(new_data, self, __node__, :env_override)
end
def force_override=(new_data)
reset
- @force_override = VividMash.new(self, new_data)
+ @force_override = VividMash.new(new_data, self, __node__, :force_override)
end
def automatic=(new_data)
reset
- @automatic = VividMash.new(self, new_data)
+ @automatic = VividMash.new(new_data, self, __node__, :automatic)
end
#
@@ -321,94 +290,104 @@ class Chef
# clears attributes from all precedence levels
def rm(*args)
- reset(args[0])
- # just easier to compute our retval, rather than collect+merge sub-retvals
- ret = args.inject(merged_attributes) do |attr, arg|
- if attr.nil? || !attr.respond_to?(:[])
- nil
- else
- begin
- attr[arg]
- rescue TypeError
- raise TypeError, "Wrong type in index of attribute (did you use a Hash index on an Array?)"
- end
- end
+ with_deep_merged_return_value(self, *args) do
+ rm_default(*args)
+ rm_normal(*args)
+ rm_override(*args)
end
- rm_default(*args)
- rm_normal(*args)
- rm_override(*args)
- ret
end
- # does <level>['foo']['bar'].delete('baz')
- def remove_from_precedence_level(level, *args, key)
- multimash = level.element(*args)
- multimash.nil? ? nil : multimash.delete(key)
- end
-
- private :remove_from_precedence_level
-
# clears attributes from all default precedence levels
#
- # equivalent to: force_default!['foo']['bar'].delete('baz')
+ # similar to: force_default!['foo']['bar'].delete('baz')
+ # - does not autovivify
+ # - does not trainwreck if interior keys do not exist
def rm_default(*args)
- reset(args[0])
- remove_from_precedence_level(force_default!(autovivify: false), *args)
+ with_deep_merged_return_value(combined_default, *args) do
+ default.unlink(*args)
+ role_default.unlink(*args)
+ env_default.unlink(*args)
+ force_default.unlink(*args)
+ end
end
# clears attributes from normal precedence
#
# equivalent to: normal!['foo']['bar'].delete('baz')
+ # - does not autovivify
+ # - does not trainwreck if interior keys do not exist
def rm_normal(*args)
- reset(args[0])
- remove_from_precedence_level(normal!(autovivify: false), *args)
+ normal.unlink(*args)
end
# clears attributes from all override precedence levels
#
# equivalent to: force_override!['foo']['bar'].delete('baz')
+ # - does not autovivify
+ # - does not trainwreck if interior keys do not exist
def rm_override(*args)
- reset(args[0])
- remove_from_precedence_level(force_override!(autovivify: false), *args)
+ with_deep_merged_return_value(combined_override, *args) do
+ override.unlink(*args)
+ role_override.unlink(*args)
+ env_override.unlink(*args)
+ force_override.unlink(*args)
+ end
+ end
+
+ def with_deep_merged_return_value(obj, *path, last)
+ hash = obj.read(*path)
+ return nil unless hash.is_a?(Hash)
+ ret = hash[last]
+ yield
+ ret
end
+ private :with_deep_merged_return_value
+
#
# Replacing attributes without merging
#
# sets default attributes without merging
- def default!(opts = {})
- # FIXME: do not flush whole cache
- reset
- MultiMash.new(self, @default, [], opts)
+ #
+ # - this API autovivifies (and cannot trainwreck)
+ def default!(*args)
+ return Decorator::Unchain.new(self, :default!) unless args.length > 0
+ write(:default, *args)
end
# sets normal attributes without merging
- def normal!(opts = {})
- # FIXME: do not flush whole cache
- reset
- MultiMash.new(self, @normal, [], opts)
+ #
+ # - this API autovivifies (and cannot trainwreck)
+ def normal!(*args)
+ return Decorator::Unchain.new(self, :normal!) unless args.length > 0
+ write(:normal, *args)
end
# sets override attributes without merging
- def override!(opts = {})
- # FIXME: do not flush whole cache
- reset
- MultiMash.new(self, @override, [], opts)
+ #
+ # - this API autovivifies (and cannot trainwreck)
+ def override!(*args)
+ return Decorator::Unchain.new(self, :override!) unless args.length > 0
+ write(:override, *args)
end
# clears from all default precedence levels and then sets force_default
- def force_default!(opts = {})
- # FIXME: do not flush whole cache
- reset
- MultiMash.new(self, @force_default, [@default, @env_default, @role_default], opts)
+ #
+ # - this API autovivifies (and cannot trainwreck)
+ def force_default!(*args)
+ return Decorator::Unchain.new(self, :force_default!) unless args.length > 0
+ value = args.pop
+ rm_default(*args)
+ write(:force_default, *args, value)
end
# clears from all override precedence levels and then sets force_override
- def force_override!(opts = {})
- # FIXME: do not flush whole cache
- reset
- MultiMash.new(self, @force_override, [@override, @env_override, @role_override], opts)
+ def force_override!(*args)
+ return Decorator::Unchain.new(self, :force_override!) unless args.length > 0
+ value = args.pop
+ rm_override(*args)
+ write(:force_override, *args, value)
end
#
@@ -420,9 +399,7 @@ class Chef
#
def merged_attributes(*path)
- # immutablize(
merge_all(path)
- # )
end
def combined_override(*path)
@@ -433,18 +410,19 @@ class Chef
immutablize(merge_defaults(path))
end
- def [](key)
- if deep_merge_cache.has_key?(key.to_s)
- # return the cache of the deep merged values by top-level key
- deep_merge_cache[key.to_s]
- else
- # save all the work of computing node[key]
- deep_merge_cache[key.to_s] = merged_attributes(key)
- end
+ def normal_unless(*args)
+ return Decorator::Unchain.new(self, :normal_unless) unless args.length > 0
+ write(:normal, *args) if normal.read(*args[0...-1]).nil?
+ end
+
+ def default_unless(*args)
+ return Decorator::Unchain.new(self, :default_unless) unless args.length > 0
+ write(:default, *args) if default.read(*args[0...-1]).nil?
end
- def []=(key, value)
- raise Exceptions::ImmutableAttributeModification
+ def override_unless(*args)
+ return Decorator::Unchain.new(self, :override_unless) unless args.length > 0
+ write(:override, *args) if override.read(*args[0...-1]).nil?
end
def has_key?(key)
@@ -452,6 +430,35 @@ class Chef
instance_variable_get(component_ivar).has_key?(key)
end
end
+ # method-style access to attributes (has to come after the prepended ImmutablizeHash)
+
+ def read(*path)
+ merged_attributes.read(*path)
+ end
+
+ def read!(*path)
+ merged_attributes.read!(*path)
+ end
+
+ def exist?(*path)
+ merged_attributes.exist?(*path)
+ end
+
+ def write(level, *args, &block)
+ send(level).write(*args, &block)
+ end
+
+ def write!(level, *args, &block)
+ send(level).write!(*args, &block)
+ end
+
+ def unlink(level, *path)
+ send(level).unlink(*path)
+ end
+
+ def unlink!(level, *path)
+ send(level).unlink!(*path)
+ end
alias :attribute? :has_key?
alias :member? :has_key?
@@ -460,36 +467,17 @@ class Chef
alias :each_attribute :each
- def method_missing(symbol, *args)
- if args.empty?
- if key?(symbol)
- self[symbol]
- else
- raise NoMethodError, "Undefined method or attribute `#{symbol}' on `node'"
- end
- elsif symbol.to_s =~ /=$/
- key_to_set = symbol.to_s[/^(.+)=$/, 1]
- self[key_to_set] = (args.length == 1 ? args[0] : args)
- else
- raise NoMethodError, "Undefined node attribute or method `#{symbol}' on `node'"
- end
- end
-
def to_s
merged_attributes.to_s
end
def inspect
- "#<#{self.class} " << (COMPONENTS + [:@merged_attributes, :@properties]).map {|iv|
+ "#<#{self.class} " << (COMPONENTS + [:@merged_attributes, :@properties]).map do |iv|
"#{iv}=#{instance_variable_get(iv).inspect}"
- }.join(", ") << ">"
- end
-
- def set_unless?
- @set_unless_present
+ end.join(", ") << ">"
end
- private
+ private
# Helper method for merge_all/merge_defaults/merge_overrides.
#
@@ -543,15 +531,10 @@ class Chef
apply_path(@automatic, path),
]
- components.map! do |component|
- safe_dup(component)
- end
-
- return nil if components.compact.empty?
-
- components.inject(ImmutableMash.new({})) do |merged, component|
- Chef::Mixin::DeepMerge.hash_only_merge!(merged, component)
+ ret = components.inject(NIL) do |merged, component|
+ hash_only_merge!(merged, component)
end
+ ret == NIL ? nil : ret
end
# Deep merge the default attribute levels with array merging.
@@ -561,10 +544,11 @@ class Chef
# @param path [Array] Array of args to method chain to descend into the node object
# @return [attr] Deep Merged values (may be VividMash, Hash, Array, etc) from the node object
def merge_defaults(path)
- DEFAULT_COMPONENTS.inject(nil) do |merged, component_ivar|
+ ret = DEFAULT_COMPONENTS.inject(NIL) do |merged, component_ivar|
component_value = apply_path(instance_variable_get(component_ivar), path)
- Chef::Mixin::DeepMerge.deep_merge(component_value, merged)
+ deep_merge!(merged, component_value)
end
+ ret == NIL ? nil : ret
end
# Deep merge the override attribute levels with array merging.
@@ -574,13 +558,88 @@ class Chef
# @param path [Array] Array of args to method chain to descend into the node object
# @return [attr] Deep Merged values (may be VividMash, Hash, Array, etc) from the node object
def merge_overrides(path)
- OVERRIDE_COMPONENTS.inject(nil) do |merged, component_ivar|
+ ret = OVERRIDE_COMPONENTS.inject(NIL) do |merged, component_ivar|
component_value = apply_path(instance_variable_get(component_ivar), path)
- Chef::Mixin::DeepMerge.deep_merge(component_value, merged)
+ deep_merge!(merged, component_value)
end
+ ret == NIL ? nil : ret
end
- end
+ # needed for __path__
+ def convert_key(key)
+ key.kind_of?(Symbol) ? key.to_s : key
+ end
+
+ NIL = Object.new
+
+ # @api private
+ def deep_merge!(merge_onto, merge_with)
+ # If there are two Hashes, recursively merge.
+ if merge_onto.kind_of?(Hash) && merge_with.kind_of?(Hash)
+ merge_with.each do |key, merge_with_value|
+ value =
+ if merge_onto.has_key?(key)
+ deep_merge!(safe_dup(merge_onto[key]), merge_with_value)
+ else
+ merge_with_value
+ end
+
+ # internal_set bypasses converting keys, does convert values and allows writing to immutable mashes
+ merge_onto.internal_set(key, value)
+ end
+ merge_onto
+
+ elsif merge_onto.kind_of?(Array) && merge_with.kind_of?(Array)
+ merge_onto |= merge_with
+
+ # If merge_with is nil, don't replace merge_onto
+ elsif merge_with.nil?
+ merge_onto
+
+ # In all other cases, replace merge_onto with merge_with
+ else
+ if merge_with.kind_of?(Hash)
+ Chef::Node::VividMash.new(merge_with)
+ elsif merge_with.kind_of?(Array)
+ Chef::Node::AttrArray.new(merge_with)
+ else
+ merge_with
+ end
+ end
+ end
+
+ # @api private
+ def hash_only_merge!(merge_onto, merge_with)
+ # If there are two Hashes, recursively merge.
+ if merge_onto.kind_of?(Hash) && merge_with.kind_of?(Hash)
+ merge_with.each do |key, merge_with_value|
+ value =
+ if merge_onto.has_key?(key)
+ hash_only_merge!(safe_dup(merge_onto[key]), merge_with_value)
+ else
+ merge_with_value
+ end
+
+ # internal_set bypasses converting keys, does convert values and allows writing to immutable mashes
+ merge_onto.internal_set(key, value)
+ end
+ merge_onto
+
+ # If merge_with is nil, don't replace merge_onto
+ elsif merge_with.nil?
+ merge_onto
+ # In all other cases, replace merge_onto with merge_with
+ else
+ if merge_with.kind_of?(Hash)
+ Chef::Node::ImmutableMash.new(merge_with)
+ elsif merge_with.kind_of?(Array)
+ Chef::Node::ImmutableArray.new(merge_with)
+ else
+ merge_with
+ end
+ end
+ end
+ end
end
end
diff --git a/lib/chef/node/attribute_collections.rb b/lib/chef/node/attribute_collections.rb
index 68f3a69756..2836473b1d 100644
--- a/lib/chef/node/attribute_collections.rb
+++ b/lib/chef/node/attribute_collections.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright 2012-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,16 @@
# limitations under the License.
#
+require "chef/node/common_api"
+require "chef/node/mixin/state_tracking"
+
class Chef
class Node
-
# == AttrArray
# AttrArray is identical to Array, except that it keeps a reference to the
# "root" (Chef::Node::Attribute) object, and will trigger a cache
# invalidation on that object when mutated.
class AttrArray < Array
-
MUTATOR_METHODS = [
:<<,
:[]=,
@@ -33,7 +34,6 @@ class Chef
:compact!,
:default=,
:default_proc=,
- :delete,
:delete_at,
:delete_if,
:fill,
@@ -62,16 +62,20 @@ class Chef
# Node::Attribute object.
MUTATOR_METHODS.each do |mutator|
define_method(mutator) do |*args, &block|
- root.reset_cache(root.top_level_breadcrumb)
- super(*args, &block)
+ ret = super(*args, &block)
+ send_reset_cache
+ ret
end
end
- attr_reader :root
+ def delete(key, &block)
+ send_reset_cache(__path__, key)
+ super
+ end
- def initialize(root, data)
- @root = root
+ def initialize(data = [])
super(data)
+ map! { |e| convert_value(e) }
end
# For elements like Fixnums, true, nil...
@@ -85,6 +89,29 @@ class Chef
Array.new(map { |e| safe_dup(e) })
end
+ private
+
+ def convert_value(value)
+ case value
+ when VividMash
+ value
+ when AttrArray
+ value
+ when Hash
+ VividMash.new(value, __root__, __node__, __precedence__)
+ when Array
+ AttrArray.new(value, __root__, __node__, __precedence__)
+ else
+ value
+ end
+ end
+
+ # needed for __path__
+ def convert_key(key)
+ key
+ end
+
+ prepend Chef::Node::Mixin::StateTracking
end
# == VividMash
@@ -96,20 +123,15 @@ class Chef
# in the creation of a new VividMash for that key. (This only works when
# using the element reference method, `[]` -- other methods, such as
# #fetch, work as normal).
- # * It supports a set_unless flag (via the root Attribute object) which
- # allows `||=` style behavior (`||=` does not work with
- # auto-vivification). This is only implemented for #[]=; methods such as
- # #store work as normal.
# * attr_accessor style element set and get are supported via method_missing
class VividMash < Mash
- attr_reader :root
+ include CommonAPI
# Methods that mutate a VividMash. Each of them is overridden so that it
# also invalidates the cached merged_attributes on the root Attribute
# object.
MUTATOR_METHODS = [
:clear,
- :delete,
:delete_if,
:keep_if,
:merge!,
@@ -123,23 +145,27 @@ class Chef
# For all of the mutating methods on Mash, override them so that they
# also invalidate the cached `merged_attributes` on the root Attribute
# object.
+
+ def delete(key, &block)
+ send_reset_cache(__path__, key)
+ super
+ end
+
MUTATOR_METHODS.each do |mutator|
define_method(mutator) do |*args, &block|
- root.reset_cache(root.top_level_breadcrumb)
+ send_reset_cache
super(*args, &block)
end
end
- def initialize(root, data = {})
- @root = root
+ def initialize(data = {})
super(data)
end
def [](key)
- root.top_level_breadcrumb ||= key
value = super
if !key?(key)
- value = self.class.new(root)
+ value = self.class.new({}, __root__)
self[key] = value
else
value
@@ -147,39 +173,13 @@ class Chef
end
def []=(key, value)
- root.top_level_breadcrumb ||= key
- if set_unless? && key?(key) && !self[key].nil?
- self[key]
- else
- root.reset_cache(root.top_level_breadcrumb)
- super
- end
+ ret = super
+ send_reset_cache(__path__, key)
+ ret # rubocop:disable Lint/Void
end
alias :attribute? :has_key?
- def method_missing(symbol, *args)
- # Calling `puts arg` implicitly calls #to_ary on `arg`. If `arg` does
- # not implement #to_ary, ruby recognizes it as a single argument, and
- # if it returns an Array, then ruby prints each element. If we don't
- # account for that here, we'll auto-vivify a VividMash for the key
- # :to_ary which creates an unwanted key and raises a TypeError.
- if symbol == :to_ary
- super
- elsif args.empty?
- self[symbol]
- elsif symbol.to_s =~ /=$/
- key_to_set = symbol.to_s[/^(.+)=$/, 1]
- self[key_to_set] = (args.length == 1 ? args[0] : args)
- else
- raise NoMethodError, "Undefined node attribute or method `#{symbol}' on `node'. To set an attribute, use `#{symbol}=value' instead."
- end
- end
-
- def set_unless?
- @root.set_unless?
- end
-
def convert_key(key)
super
end
@@ -192,10 +192,12 @@ class Chef
case value
when VividMash
value
+ when AttrArray
+ value
when Hash
- VividMash.new(root, value)
+ VividMash.new(value, __root__, __node__, __precedence__)
when Array
- AttrArray.new(root, value)
+ AttrArray.new(value, __root__, __node__, __precedence__)
else
value
end
@@ -205,119 +207,7 @@ class Chef
Mash.new(self)
end
+ prepend Chef::Node::Mixin::StateTracking
end
-
- # == MultiMash
- # This is a Hash-like object that contains multiple VividMashes in it. Its
- # purpose is so that the user can descend into the mash and delete a subtree
- # from all of the Mash objects (used to delete all values in a subtree from
- # default, force_default, role_default and env_default at the same time). The
- # assignment operator strictly does assignment (does no merging) and works
- # by deleting the subtree and then assigning to the last mash which passed in
- # the initializer.
- #
- # A lot of the complexity of this class comes from the fact that at any key
- # value some or all of the mashes may walk off their ends and become nil or
- # true or something. The schema may change so that one precidence leve may
- # be 'true' object and another may be a VividMash. It is also possible that
- # one or many of them may transition from VividMashes to Hashes or Arrays.
- #
- # It also supports the case where you may be deleting a key using node.rm
- # in which case if intermediate keys all walk off into nil then you don't want
- # to be autovivifying keys as you go. On the other hand you may be using
- # node.force_default! in which case you'll wind up with a []= operator at the
- # end and you want autovivification, so we conditionally have to support either
- # operation.
- #
- # @todo: can we have an autovivify class that decorates a class that doesn't
- # autovivify or something so that the code is less awful?
- #
- class MultiMash
- attr_reader :root
- attr_reader :mashes
- attr_reader :opts
- attr_reader :primary_mash
-
- # Initialize with an array of mashes. For the delete return value to work
- # properly the mashes must come from the same attribute level (i.e. all
- # override or all default, but not a mix of both).
- def initialize(root, primary_mash, mashes, opts = {})
- @root = root
- @primary_mash = primary_mash
- @mashes = mashes
- @opts = opts
- @opts[:autovivify] = true if @opts[:autovivify].nil?
- end
-
- def [](key)
- # handle the secondary mashes
- new_mashes = []
- mashes.each do |mash|
- new_mash = safe_evalute_key(mash, key)
- # secondary mashes never autovivify so once they fall into nil, we just stop tracking them
- new_mashes.push(new_mash) unless new_mash.nil?
- end
-
- new_primary_mash = safe_evalute_key(primary_mash, key)
-
- if new_primary_mash.nil? && @opts[:autovivify]
- primary_mash[key] = VividMash.new(root)
- new_primary_mash = primary_mash[key]
- end
-
- MultiMash.new(root, new_primary_mash, new_mashes, opts)
- end
-
- def []=(key, value)
- if primary_mash.nil?
- # This theoretically should never happen since node#force_default! setter methods will autovivify and
- # node#rm methods do not end in #[]= operators.
- raise TypeError, "No autovivification was specified initially on a method chain ending in assignment"
- end
- ret = delete(key)
- primary_mash[key] = value
- ret
- end
-
- # mash.element('foo', 'bar') is the same as mash['foo']['bar']
- def element(key = nil, *subkeys)
- return self if key.nil?
- submash = self[key]
- subkeys.empty? ? submash : submash.element(*subkeys)
- end
-
- def delete(key)
- # the return value is a deep merge which is correct semantics when
- # merging between attributes on the same level (this would be incorrect
- # if passed both override and default attributes which would need hash_only
- # merging).
- ret = mashes.inject(Mash.new) do |merged, mash|
- Chef::Mixin::DeepMerge.merge(merged, mash)
- end
- ret = Chef::Mixin::DeepMerge.merge(ret, primary_mash)
- mashes.each do |mash|
- mash.delete(key) if mash.respond_to?(:delete)
- end
- primary_mash.delete(key) if primary_mash.respond_to?(:delete)
- ret[key]
- end
-
- private
-
- def safe_evalute_key(mash, key)
- if mash.respond_to?(:[])
- if mash.respond_to?(:has_key?)
- if mash.has_key?(key)
- return mash[key] if mash[key].respond_to?(:[])
- end
- elsif !mash[key].nil?
- return mash[key] if mash[key].respond_to?(:[])
- end
- end
- return nil
- end
-
- end
-
end
end
diff --git a/lib/chef/node/common_api.rb b/lib/chef/node/common_api.rb
new file mode 100644
index 0000000000..a703c1ef54
--- /dev/null
+++ b/lib/chef/node/common_api.rb
@@ -0,0 +1,121 @@
+#--
+# Copyright:: Copyright 2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ class Node
+ # shared API between VividMash and ImmutableMash, writer code can be
+ # 'shared' to keep it logically in this file by adding them to the
+ # block list in ImmutableMash.
+ module CommonAPI
+ # method-style access to attributes
+
+ def valid_container?(obj, key)
+ obj.is_a?(Hash) || (obj.is_a?(Array) && key.is_a?(Integer))
+ end
+
+ private :valid_container?
+
+ # - autovivifying / autoreplacing writer
+ # - non-container-ey intermediate objects are replaced with hashes
+ def write(*args, &block)
+ value = block_given? ? yield : args.pop
+ last = args.pop
+ prev_memo = prev_key = nil
+ chain = args.inject(self) do |memo, key|
+ if !valid_container?(memo, key)
+ prev_memo[prev_key] = {}
+ memo = prev_memo[prev_key]
+ end
+ prev_memo = memo
+ prev_key = key
+ memo[key]
+ end
+ if !valid_container?(chain, last)
+ prev_memo[prev_key] = {}
+ chain = prev_memo[prev_key]
+ end
+ chain[last] = value
+ end
+
+ # this autovivifies, but can throw NoSuchAttribute when trying to access #[] on
+ # something that is not a container ("schema violation" issues).
+ #
+ def write!(*args, &block)
+ value = block_given? ? yield : args.pop
+ last = args.pop
+ obj = args.inject(self) do |memo, key|
+ raise Chef::Exceptions::AttributeTypeMismatch unless valid_container?(memo, key)
+ memo[key]
+ end
+ raise Chef::Exceptions::AttributeTypeMismatch unless valid_container?(obj, last)
+ obj[last] = value
+ end
+
+ # FIXME:(?) does anyone need a non-autovivifying writer for attributes that throws exceptions?
+
+ # return true or false based on if the attribute exists
+ def exist?(*path)
+ path.inject(self) do |memo, key|
+ return false unless valid_container?(memo, key)
+ if memo.is_a?(Hash)
+ if memo.key?(key)
+ memo[key]
+ else
+ return false
+ end
+ elsif memo.is_a?(Array)
+ if memo.length > key
+ memo[key]
+ else
+ return false
+ end
+ end
+ end
+ true
+ end
+
+ # this is a safe non-autovivifying reader that returns nil if the attribute does not exist
+ def read(*path)
+ read!(*path)
+ rescue Chef::Exceptions::NoSuchAttribute
+ nil
+ end
+
+ # non-autovivifying reader that throws an exception if the attribute does not exist
+ def read!(*path)
+ raise Chef::Exceptions::NoSuchAttribute unless exist?(*path)
+ path.inject(self) do |memo, key|
+ memo[key]
+ end
+ end
+
+ # FIXME:(?) does anyone really like the autovivifying reader that we have and wants the same behavior? readers that write? ugh...
+
+ def unlink(*path, last)
+ hash = path.empty? ? self : read(*path)
+ return nil unless hash.is_a?(Hash) || hash.is_a?(Array)
+ hash.delete(last)
+ end
+
+ def unlink!(*path)
+ raise Chef::Exceptions::NoSuchAttribute unless exist?(*path)
+ unlink(*path)
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/node/immutable_collections.rb b/lib/chef/node/immutable_collections.rb
index b5fd86fa72..c12ee33268 100644
--- a/lib/chef/node/immutable_collections.rb
+++ b/lib/chef/node/immutable_collections.rb
@@ -1,18 +1,51 @@
+#--
+# Copyright:: Copyright 2012-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/node/common_api"
+require "chef/node/mixin/state_tracking"
+require "chef/node/mixin/immutablize_array"
+require "chef/node/mixin/immutablize_hash"
class Chef
class Node
-
module Immutablize
- def immutablize(value)
+ # For elements like Fixnums, true, nil...
+ def safe_dup(e)
+ e.dup
+ rescue TypeError
+ e
+ end
+
+ def convert_value(value)
case value
when Hash
- ImmutableMash.new(value)
+ ImmutableMash.new(value, __root__, __node__, __precedence__)
when Array
- ImmutableArray.new(value)
- else
+ ImmutableArray.new(value, __root__, __node__, __precedence__)
+ when ImmutableMash, ImmutableArray
value
+ else
+ safe_dup(value).freeze
end
end
+
+ def immutablize(value)
+ convert_value(value)
+ end
end
# == ImmutableArray
@@ -31,55 +64,12 @@ class Chef
alias :internal_push :<<
private :internal_push
- # A list of methods that mutate Array. Each of these is overridden to
- # raise an error, making this instances of this class more or less
- # immutable.
- DISALLOWED_MUTATOR_METHODS = [
- :<<,
- :[]=,
- :clear,
- :collect!,
- :compact!,
- :default=,
- :default_proc=,
- :delete,
- :delete_at,
- :delete_if,
- :fill,
- :flatten!,
- :insert,
- :keep_if,
- :map!,
- :merge!,
- :pop,
- :push,
- :update,
- :reject!,
- :reverse!,
- :replace,
- :select!,
- :shift,
- :slice!,
- :sort!,
- :sort_by!,
- :uniq!,
- :unshift,
- ]
-
- def initialize(array_data)
+ def initialize(array_data = [])
array_data.each do |value|
internal_push(immutablize(value))
end
end
- # Redefine all of the methods that mutate a Hash to raise an error when called.
- # This is the magic that makes this object "Immutable"
- DISALLOWED_MUTATOR_METHODS.each do |mutator_method_name|
- define_method(mutator_method_name) do |*args, &block|
- raise Exceptions::ImmutableAttributeModification
- end
- end
-
# For elements like Fixnums, true, nil...
def safe_dup(e)
e.dup
@@ -92,21 +82,29 @@ class Chef
end
def to_a
- a = Array.new
- each do |v|
- a <<
- case v
- when ImmutableArray
- v.to_a
- when ImmutableMash
- v.to_hash
- else
- v
- end
- end
- a
+ Array.new(map do |v|
+ case v
+ when ImmutableArray
+ v.to_a
+ when ImmutableMash
+ v.to_h
+ else
+ safe_dup(v)
+ end
+ end)
+ end
+
+ alias_method :to_array, :to_a
+
+ private
+
+ # needed for __path__
+ def convert_key(key)
+ key
end
+ prepend Chef::Node::Mixin::StateTracking
+ prepend Chef::Node::Mixin::ImmutablizeArray
end
# == ImmutableMash
@@ -122,71 +120,22 @@ class Chef
# it is stale.
# * Values can be accessed in attr_reader-like fashion via method_missing.
class ImmutableMash < Mash
-
include Immutablize
+ include CommonAPI
- alias :internal_set :[]=
- private :internal_set
-
- DISALLOWED_MUTATOR_METHODS = [
- :[]=,
- :clear,
- :collect!,
- :default=,
- :default_proc=,
- :delete,
- :delete_if,
- :keep_if,
- :map!,
- :merge!,
- :update,
- :reject!,
- :replace,
- :select!,
- :shift,
- ]
-
- def initialize(mash_data)
- mash_data.each do |key, value|
- internal_set(key, immutablize(value))
- end
- end
-
- def public_method_that_only_deep_merge_should_use(key, value)
- internal_set(key, immutablize(value))
+ # this is for deep_merge usage, chef users must never touch this API
+ # @api private
+ def internal_set(key, value)
+ regular_writer(key, convert_value(value))
end
- alias :attribute? :has_key?
-
- # Redefine all of the methods that mutate a Hash to raise an error when called.
- # This is the magic that makes this object "Immutable"
- DISALLOWED_MUTATOR_METHODS.each do |mutator_method_name|
- define_method(mutator_method_name) do |*args, &block|
- raise Exceptions::ImmutableAttributeModification
- end
- end
-
- def method_missing(symbol, *args)
- if args.empty?
- if key?(symbol)
- self[symbol]
- else
- raise NoMethodError, "Undefined method or attribute `#{symbol}' on `node'"
- end
- # This will raise a ImmutableAttributeModification error:
- elsif symbol.to_s =~ /=$/
- key_to_set = symbol.to_s[/^(.+)=$/, 1]
- self[key_to_set] = (args.length == 1 ? args[0] : args)
- else
- raise NoMethodError, "Undefined node attribute or method `#{symbol}' on `node'"
+ def initialize(mash_data = {})
+ mash_data.each do |key, value|
+ internal_set(key, value)
end
end
- # Mash uses #convert_value to mashify values on input.
- # Since we're handling this ourselves, override it to be a no-op
- def convert_value(value)
- value
- end
+ alias :attribute? :has_key?
# NOTE: #default and #default= are likely to be pretty confusing. For a
# regular ruby Hash, they control what value is returned for, e.g.,
@@ -194,26 +143,40 @@ class Chef
# Of course, 'default' has a specific meaning in Chef-land
def dup
- Mash.new(self)
+ h = Mash.new
+ each_pair do |k, v|
+ h[k] = safe_dup(v)
+ end
+ h
end
- def to_hash
+ def to_h
h = Hash.new
each_pair do |k, v|
h[k] =
case v
when ImmutableMash
- v.to_hash
+ v.to_h
when ImmutableArray
v.to_a
else
- v
+ safe_dup(v)
end
end
h
end
- end
+ alias_method :to_hash, :to_h
+ # For elements like Fixnums, true, nil...
+ def safe_dup(e)
+ e.dup
+ rescue TypeError
+ e
+ end
+
+ prepend Chef::Node::Mixin::StateTracking
+ prepend Chef::Node::Mixin::ImmutablizeHash
+ end
end
end
diff --git a/lib/chef/node/mixin/deep_merge_cache.rb b/lib/chef/node/mixin/deep_merge_cache.rb
new file mode 100644
index 0000000000..b18d6b10cb
--- /dev/null
+++ b/lib/chef/node/mixin/deep_merge_cache.rb
@@ -0,0 +1,61 @@
+#--
+# Copyright:: Copyright 2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ class Node
+ module Mixin
+ module DeepMergeCache
+ # Cache of deep merged values by top-level key. This is a simple hash which has keys that are the
+ # top-level keys of the node object, and we save the computed deep-merge for that key here. There is
+ # no cache of subtrees.
+ attr_accessor :deep_merge_cache
+
+ def initialize
+ @merged_attributes = nil
+ @combined_override = nil
+ @combined_default = nil
+ @deep_merge_cache = {}
+ end
+
+ # Invalidate a key in the deep_merge_cache. If called with nil, or no arg, this will invalidate
+ # the entire deep_merge cache. In the case of the user doing node.default['foo']['bar']['baz']=
+ # that eventually results in a call to reset_cache('foo') here. A node.default=hash_thing call
+ # must invalidate the entire cache and re-deep-merge the entire node object.
+ def reset_cache(path = nil)
+ if path.nil?
+ deep_merge_cache.clear
+ else
+ deep_merge_cache.delete(path.to_s)
+ end
+ end
+
+ alias :reset :reset_cache
+
+ def [](key)
+ if deep_merge_cache.has_key?(key.to_s)
+ # return the cache of the deep merged values by top-level key
+ deep_merge_cache[key.to_s]
+ else
+ # save all the work of computing node[key]
+ deep_merge_cache[key.to_s] = merged_attributes(key)
+ end
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/node/mixin/immutablize_array.rb b/lib/chef/node/mixin/immutablize_array.rb
new file mode 100644
index 0000000000..ba59385d68
--- /dev/null
+++ b/lib/chef/node/mixin/immutablize_array.rb
@@ -0,0 +1,175 @@
+#--
+# Copyright:: Copyright 2016-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ class Node
+ module Mixin
+ module ImmutablizeArray
+ # Allowed methods that MUST NOT mutate the object
+ # (if any of these methods mutate the underlying object that is a bug that needs to be fixed)
+ ALLOWED_METHODS = [
+ :&,
+ :*,
+ :+,
+ :-,
+ :[],
+ :abbrev,
+ :all?,
+ :any?,
+ :assoc,
+ :at,
+ :bsearch,
+ :bsearch_index,
+ :chunk,
+ :chunk_while,
+ :collect,
+ :collect_concat,
+ :combination,
+ :compact,
+ :count,
+ :cycle,
+ :detect,
+ :dig,
+ :drop,
+ :drop_while,
+ :each,
+ :each_cons,
+ :each_entry,
+ :each_index,
+ :each_slice,
+ :each_with_index,
+ :each_with_object,
+ :empty?,
+ :entries,
+ :fetch,
+ :find,
+ :find_all,
+ :find_index,
+ :first,
+ :flat_map,
+ :flatten,
+ :grep,
+ :grep_v,
+ :group_by,
+ :include?,
+ :index,
+ :inject,
+ :join,
+ :last,
+ :lazy,
+ :length,
+ :map,
+ :max,
+ :max_by,
+ :member?,
+ :min,
+ :min_by,
+ :minmax,
+ :minmax_by,
+ :none?,
+ :one?,
+ :pack,
+ :partition,
+ :permutation,
+ :product,
+ :rassoc,
+ :reduce,
+ :reject,
+ :repeated_combination,
+ :repeated_permutation,
+ :reverse,
+ :reverse_each,
+ :rindex,
+ :rotate,
+ :sample,
+ :save_plist,
+ :select,
+ :shelljoin,
+ :shuffle,
+ :size,
+ :slice,
+ :slice_after,
+ :slice_before,
+ :slice_when,
+ :sort,
+ :sort_by,
+ :sum,
+ :take,
+ :take_while,
+ :to_a,
+ :to_ary,
+ :to_csv,
+ :to_h,
+ :to_plist,
+ :to_set,
+ :transpose,
+ :uniq,
+ :values_at,
+ :zip,
+ :|,
+ ]
+ # A list of methods that mutate Array. Each of these is overridden to
+ # raise an error, making this instances of this class more or less
+ # immutable.
+ DISALLOWED_MUTATOR_METHODS = [
+ :<<,
+ :[]=,
+ :append,
+ :clear,
+ :collect!,
+ :compact!,
+ :concat,
+ :default=,
+ :default_proc=,
+ :delete,
+ :delete_at,
+ :delete_if,
+ :fill,
+ :flatten!,
+ :insert,
+ :keep_if,
+ :map!,
+ :merge!,
+ :pop,
+ :prepend,
+ :push,
+ :reject!,
+ :replace,
+ :reverse!,
+ :rotate!,
+ :select!,
+ :shift,
+ :shuffle!,
+ :slice!,
+ :sort!,
+ :sort_by!,
+ :uniq!,
+ :unshift,
+ :update,
+ ]
+
+ # Redefine all of the methods that mutate a Hash to raise an error when called.
+ # This is the magic that makes this object "Immutable"
+ DISALLOWED_MUTATOR_METHODS.each do |mutator_method_name|
+ define_method(mutator_method_name) do |*args, &block|
+ raise Exceptions::ImmutableAttributeModification
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/node/mixin/immutablize_hash.rb b/lib/chef/node/mixin/immutablize_hash.rb
new file mode 100644
index 0000000000..71d2f754a4
--- /dev/null
+++ b/lib/chef/node/mixin/immutablize_hash.rb
@@ -0,0 +1,162 @@
+#--
+# Copyright:: Copyright 2016-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ class Node
+ module Mixin
+ module ImmutablizeHash
+ # allowed methods that MUST NOT mutate the object
+ # (if any of these methods mutate the underlying object that is a bug that needs to be fixed)
+ ALLOWED_METHODS = [
+ :<,
+ :<=,
+ :>,
+ :>=,
+ :[],
+ :all?,
+ :any?,
+ :assoc,
+ :chunk,
+ :chunk_while,
+ :collect,
+ :collect_concat,
+ :compact,
+ :compare_by_identity,
+ :compare_by_identity?,
+ :count,
+ :cycle,
+ :default,
+ :default_proc,
+ :detect,
+ :dig,
+ :drop,
+ :drop_while,
+ :each,
+ :each_cons,
+ :each_entry,
+ :each_key,
+ :each_pair,
+ :each_slice,
+ :each_value,
+ :each_with_index,
+ :each_with_object,
+ :empty?,
+ :entries,
+ :fetch,
+ :fetch_values,
+ :find,
+ :find_all,
+ :find_index,
+ :first,
+ :flat_map,
+ :flatten,
+ :grep,
+ :grep_v,
+ :group_by,
+ :has_key?,
+ :has_value?,
+ :include?,
+ :index,
+ :inject,
+ :invert,
+ :key,
+ :key?,
+ :keys,
+ :lazy,
+ :length,
+ :map,
+ :max,
+ :max_by,
+ :member?,
+ :merge,
+ :min,
+ :min_by,
+ :minmax,
+ :minmax_by,
+ :none?,
+ :normalize_param,
+ :one?,
+ :partition,
+ :rassoc,
+ :reduce,
+ :reject,
+ :reverse_each,
+ :save_plist,
+ :select,
+ :size,
+ :slice,
+ :slice_after,
+ :slice_before,
+ :slice_when,
+ :sort,
+ :sort_by,
+ :sum,
+ :take,
+ :take_while,
+ :to_a,
+ :to_h,
+ :to_hash,
+ :to_plist,
+ :to_proc,
+ :to_set,
+ :to_xml_attributes,
+ :transform_keys,
+ :transform_values,
+ :uniq,
+ :value?,
+ :values,
+ :values_at,
+ :zip,
+ ]
+ DISALLOWED_MUTATOR_METHODS = [
+ :[]=,
+ :clear,
+ :collect!,
+ :compact!,
+ :default=,
+ :default_proc=,
+ :delete,
+ :delete_if,
+ :keep_if,
+ :map!,
+ :merge!,
+ :rehash,
+ :reject!,
+ :replace,
+ :select!,
+ :shift,
+ :store,
+ :transform_keys!,
+ :transform_values!,
+ :unlink!,
+ :unlink,
+ :update,
+ :write!,
+ :write,
+ ]
+
+ # Redefine all of the methods that mutate a Hash to raise an error when called.
+ # This is the magic that makes this object "Immutable"
+ DISALLOWED_MUTATOR_METHODS.each do |mutator_method_name|
+ define_method(mutator_method_name) do |*args, &block|
+ raise Exceptions::ImmutableAttributeModification
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/node/mixin/state_tracking.rb b/lib/chef/node/mixin/state_tracking.rb
new file mode 100644
index 0000000000..5958973024
--- /dev/null
+++ b/lib/chef/node/mixin/state_tracking.rb
@@ -0,0 +1,96 @@
+#--
+# Copyright:: Copyright 2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ class Node
+ module Mixin
+ module StateTracking
+ attr_reader :__path__
+ attr_reader :__root__
+ attr_reader :__node__
+ attr_reader :__precedence__
+
+ def initialize(data = nil, root = self, node = nil, precedence = nil)
+ # __path__ and __root__ must be nil when we call super so it knows
+ # to avoid resetting the cache on construction
+ data.nil? ? super() : super(data)
+ @__path__ = []
+ @__root__ = root
+ @__node__ = node
+ @__precedence__ = precedence
+ end
+
+ def [](*args)
+ ret = super
+ key = args.first
+ next_path = [ __path__, convert_key(key) ].flatten.compact
+ copy_state_to(ret, next_path)
+ end
+
+ def []=(*args)
+ ret = super
+ key = args.first
+ value = args.last
+ next_path = [ __path__, convert_key(key) ].flatten.compact
+ send_attribute_changed_event(next_path, value)
+ copy_state_to(ret, next_path)
+ end
+
+ protected
+
+ def __path__=(path)
+ @__path__ = path
+ end
+
+ def __root__=(root)
+ @__root__ = root
+ end
+
+ def __precedence__=(precedence)
+ @__precedence__ = precedence
+ end
+
+ def __node__=(node)
+ @__node__ = node
+ end
+
+ private
+
+ def send_attribute_changed_event(next_path, value)
+ if __node__ && __node__.run_context && __node__.run_context.events
+ __node__.run_context.events.attribute_changed(__precedence__, next_path, value)
+ end
+ end
+
+ def send_reset_cache(path = nil, key = nil)
+ next_path = [ path, key ].flatten.compact
+ __root__.reset_cache(next_path.first) if !__root__.nil? && __root__.respond_to?(:reset_cache) && !next_path.nil?
+ end
+
+ def copy_state_to(ret, next_path)
+ if ret.is_a?(StateTracking)
+ ret.__path__ = next_path
+ ret.__root__ = __root__
+ ret.__node__ = __node__
+ ret.__precedence__ = __precedence__
+ end
+ ret
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/node_map.rb b/lib/chef/node_map.rb
index 5eac63d380..0406b3c1d6 100644
--- a/lib/chef/node_map.rb
+++ b/lib/chef/node_map.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,6 +16,25 @@
# limitations under the License.
#
+#
+# example of a NodeMap entry for the user resource (as typed on the DSL):
+#
+# :user=>
+# [{:klass=>Chef::Resource::User::AixUser, :os=>"aix"},
+# {:klass=>Chef::Resource::User::DsclUser, :os=>"darwin"},
+# {:klass=>Chef::Resource::User::PwUser, :os=>"freebsd"},
+# {:klass=>Chef::Resource::User::LinuxUser, :os=>"linux"},
+# {:klass=>Chef::Resource::User::SolarisUser,
+# :os=>["omnios", "solaris2"]},
+# {:klass=>Chef::Resource::User::WindowsUser, :os=>"windows"}],
+#
+# the entries in the array are pre-sorted into priority order (blocks/platform_version/platform/platform_family/os/none) so that
+# the first entry's :klass that matches the filter is returned when doing a get.
+#
+# note that as this examples show filter values may be a scalar string or an array of scalar strings.
+#
+# XXX: confusingly, in the *_priority_map the :klass may be an array of Strings of class names
+#
class Chef
class NodeMap
@@ -31,16 +50,12 @@ class Chef
#
# @return [NodeMap] Returns self for possible chaining
#
- def set(key, value, platform: nil, platform_version: nil, platform_family: nil, os: nil, on_platform: nil, on_platforms: nil, canonical: nil, override: nil, &block)
- Chef.log_deprecation("The on_platform option to node_map has been deprecated") if on_platform
- Chef.log_deprecation("The on_platforms option to node_map has been deprecated") if on_platforms
- platform ||= on_platform || on_platforms
- filters = {}
- filters[:platform] = platform if platform
- filters[:platform_version] = platform_version if platform_version
- filters[:platform_family] = platform_family if platform_family
- filters[:os] = os if os
- new_matcher = { value: value, filters: filters }
+ def set(key, klass, platform: nil, platform_version: nil, platform_family: nil, os: nil, canonical: nil, override: nil, &block)
+ new_matcher = { klass: klass }
+ new_matcher[:platform] = platform if platform
+ new_matcher[:platform_version] = platform_version if platform_version
+ new_matcher[:platform_family] = platform_family if platform_family
+ new_matcher[:os] = os if os
new_matcher[:block] = block if block
new_matcher[:canonical] = canonical if canonical
new_matcher[:override] = override if override
@@ -51,7 +66,10 @@ class Chef
map[key] ||= []
map[key].each_with_index do |matcher, index|
cmp = compare_matchers(key, new_matcher, matcher)
- insert_at ||= index if cmp && cmp <= 0
+ if cmp && cmp <= 0
+ insert_at = index
+ break
+ end
end
if insert_at
map[key].insert(insert_at, new_matcher)
@@ -71,11 +89,14 @@ class Chef
# @param canonical [Boolean] `true` or `false` to match canonical or
# non-canonical values only. `nil` to ignore canonicality. Default: `nil`
#
- # @return [Object] Value
+ # @return [Object] Class
#
def get(node, key, canonical: nil)
- raise ArgumentError, "first argument must be a Chef::Node" unless node.is_a?(Chef::Node) || node.nil?
- list(node, key, canonical: canonical).first
+ return nil unless map.has_key?(key)
+ map[key].map do |matcher|
+ return matcher[:klass] if node_matches?(node, matcher) && canonical_matches?(canonical, matcher)
+ end
+ nil
end
#
@@ -88,23 +109,48 @@ class Chef
# @param canonical [Boolean] `true` or `false` to match canonical or
# non-canonical values only. `nil` to ignore canonicality. Default: `nil`
#
- # @return [Object] Value
+ # @return [Object] Class
#
def list(node, key, canonical: nil)
- raise ArgumentError, "first argument must be a Chef::Node" unless node.is_a?(Chef::Node) || node.nil?
return [] unless map.has_key?(key)
map[key].select do |matcher|
node_matches?(node, matcher) && canonical_matches?(canonical, matcher)
- end.map { |matcher| matcher[:value] }
+ end.map { |matcher| matcher[:klass] }
+ end
+
+ # Remove a class from all its matchers in the node_map, will remove mappings completely if its the last matcher left
+ #
+ # Note that this leaks the internal structure out a bit, but the main consumer of this (poise/halite) cares only about
+ # the keys in the returned Hash.
+ #
+ # @param klass [Class] the class to seek and destroy
+ #
+ # @return [Hash] deleted entries in the same format as the @map
+ def delete_class(klass)
+ raise "please use a Class type for the klass argument" unless klass.is_a?(Class)
+ deleted = {}
+ map.each do |key, matchers|
+ deleted_matchers = []
+ matchers.delete_if do |matcher|
+ # because matcher[:klass] may be a string (which needs to die), coerce both to strings to compare somewhat canonically
+ if matcher[:klass].to_s == klass.to_s
+ deleted_matchers << matcher
+ true
+ end
+ end
+ deleted[key] = deleted_matchers unless deleted_matchers.empty?
+ map.delete(key) if matchers.empty?
+ end
+ deleted
end
# Seriously, don't use this, it's nearly certain to change on you
# @return remaining
# @api private
- def delete_canonical(key, value)
+ def delete_canonical(key, klass)
remaining = map[key]
if remaining
- remaining.delete_if { |matcher| matcher[:canonical] && Array(matcher[:value]) == Array(value) }
+ remaining.delete_if { |matcher| matcher[:canonical] && Array(matcher[:klass]) == Array(klass) }
if remaining.empty?
map.delete(key)
remaining = nil
@@ -113,7 +159,7 @@ class Chef
remaining
end
- protected
+ private
#
# Succeeds if:
@@ -146,7 +192,7 @@ class Chef
filter_values.empty? ||
Array(filter_values).any? do |v|
- Chef::VersionConstraint::Platform.new(v).include?(value)
+ Gem::Requirement.new(v).satisfied_by?(Gem::Version.new(value))
end
end
@@ -164,7 +210,7 @@ class Chef
def node_matches?(node, matcher)
return true if !node
- filters_match?(node, matcher[:filters]) && block_matches?(node, matcher[:block])
+ filters_match?(node, matcher) && block_matches?(node, matcher[:block])
end
def canonical_matches?(canonical, matcher)
@@ -172,47 +218,40 @@ class Chef
!!canonical == !!matcher[:canonical]
end
+ #
+ # "provides" lines with identical filters sort by class name (ascending).
+ #
def compare_matchers(key, new_matcher, matcher)
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:block] }
+ cmp = compare_matcher_properties(new_matcher[:block], matcher[:block])
return cmp if cmp != 0
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:filters][:platform_version] }
+ cmp = compare_matcher_properties(new_matcher[:platform_version], matcher[:platform_version])
return cmp if cmp != 0
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:filters][:platform] }
+ cmp = compare_matcher_properties(new_matcher[:platform], matcher[:platform])
return cmp if cmp != 0
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:filters][:platform_family] }
+ cmp = compare_matcher_properties(new_matcher[:platform_family], matcher[:platform_family])
return cmp if cmp != 0
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:filters][:os] }
+ cmp = compare_matcher_properties(new_matcher[:os], matcher[:os])
return cmp if cmp != 0
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:override] }
+ cmp = compare_matcher_properties(new_matcher[:override], matcher[:override])
return cmp if cmp != 0
# If all things are identical, return 0
0
end
- def compare_matcher_properties(new_matcher, matcher)
- a = yield(new_matcher)
- b = yield(matcher)
+ def compare_matcher_properties(a, b)
+ # falsity comparisons here handle both "nil" and "false"
+ return 1 if !a && b
+ return -1 if !b && a
+ return 0 if !a && !b
- # Check for blcacklists ('!windows'). Those always come *after* positive
+ # Check for blacklists ('!windows'). Those always come *after* positive
# whitelists.
a_negated = Array(a).any? { |f| f.is_a?(String) && f.start_with?("!") }
b_negated = Array(b).any? { |f| f.is_a?(String) && f.start_with?("!") }
- if a_negated != b_negated
- return 1 if a_negated
- return -1 if b_negated
- end
+ return 1 if a_negated && !b_negated
+ return -1 if b_negated && !a_negated
- # We treat false / true and nil / not-nil with the same comparison
- a = nil if a == false
- b = nil if b == false
- cmp = a <=> b
- # This is the case where one is non-nil, and one is nil. The one that is
- # nil is "greater" (i.e. it should come last).
- if cmp.nil?
- return 1 if a.nil?
- return -1 if b.nil?
- end
- cmp
+ a <=> b
end
def map
diff --git a/lib/chef/null_logger.rb b/lib/chef/null_logger.rb
index 927cfc0c08..5781d8e055 100644
--- a/lib/chef/null_logger.rb
+++ b/lib/chef/null_logger.rb
@@ -42,6 +42,9 @@ class Chef
def debug(message, &block)
end
+ def trace(message, &block)
+ end
+
def add(severity, message = nil, progname = nil)
end
@@ -68,5 +71,9 @@ class Chef
false
end
+ def trace?
+ false
+ end
+
end
end
diff --git a/lib/chef/org.rb b/lib/chef/org.rb
index a148e37aea..73a129c7c1 100644
--- a/lib/chef/org.rb
+++ b/lib/chef/org.rb
@@ -73,15 +73,15 @@ class Chef
end
def create
- payload = { :name => self.name, :full_name => self.full_name }
+ payload = { :name => name, :full_name => full_name }
new_org = chef_rest.post("organizations", payload)
- Chef::Org.from_hash(self.to_hash.merge(new_org))
+ Chef::Org.from_hash(to_hash.merge(new_org))
end
def update
- payload = { :name => self.name, :full_name => self.full_name }
+ payload = { :name => name, :full_name => full_name }
new_org = chef_rest.put("organizations/#{name}", payload)
- Chef::Org.from_hash(self.to_hash.merge(new_org))
+ Chef::Org.from_hash(to_hash.merge(new_org))
end
def destroy
@@ -89,14 +89,12 @@ class Chef
end
def save
- begin
- create
- rescue Net::HTTPServerException => e
- if e.response.code == "409"
- update
- else
- raise e
- end
+ create
+ rescue Net::HTTPServerException => e
+ if e.response.code == "409"
+ update
+ else
+ raise e
end
end
@@ -124,11 +122,6 @@ class Chef
Chef::Org.from_hash(Chef::JSONCompat.from_json(json))
end
- def self.json_create(json)
- Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please use Chef::Org#from_json or Chef::Org#load.")
- Chef::Org.from_json(json)
- end
-
def self.load(org_name)
response = Chef::ServerAPI.new(Chef::Config[:chef_server_root]).get("organizations/#{org_name}")
Chef::Org.from_hash(response)
diff --git a/lib/chef/platform/provider_handler_map.rb b/lib/chef/platform/provider_handler_map.rb
index 26acf150df..4f87a36d1d 100644
--- a/lib/chef/platform/provider_handler_map.rb
+++ b/lib/chef/platform/provider_handler_map.rb
@@ -17,12 +17,12 @@
#
require "singleton"
-require "chef/platform/handler_map"
+require "chef/node_map"
class Chef
class Platform
# @api private
- class ProviderHandlerMap < Chef::Platform::HandlerMap
+ class ProviderHandlerMap < Chef::NodeMap
include Singleton
end
end
diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb
index 40474242f0..5dbe0bdab4 100644
--- a/lib/chef/platform/provider_mapping.rb
+++ b/lib/chef/platform/provider_mapping.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,44 +26,6 @@ class Chef
class Platform
class << self
- attr_writer :platforms
-
- def platforms
- @platforms ||= { default: {} }
- end
-
- include Chef::Mixin::ParamsValidate
-
- def find(name, version)
- provider_map = platforms[:default].clone
-
- name_sym = name
- if name.kind_of?(String)
- name = name.downcase
- name.gsub!(/\s/, "_")
- name_sym = name.to_sym
- end
-
- if platforms.has_key?(name_sym)
- platform_versions = platforms[name_sym].select { |k, v| k != :default }
- if platforms[name_sym].has_key?(:default)
- provider_map.merge!(platforms[name_sym][:default])
- end
- platform_versions.each do |platform_version, provider|
- begin
- version_constraint = Chef::VersionConstraint::Platform.new(platform_version)
- if version_constraint.include?(version)
- Chef::Log.debug("Platform #{name} version #{version} found")
- provider_map.merge!(provider)
- end
- rescue Chef::Exceptions::InvalidPlatformVersion
- Chef::Log.debug("Chef::Version::Comparable does not know how to parse the platform version: #{version}")
- end
- end
- end
- provider_map
- end
-
def find_platform_and_version(node)
platform = nil
version = nil
@@ -86,129 +48,8 @@ class Chef
raise ArgumentError, "Cannot find a version for #{node}" unless version
- return platform, version
- end
-
- def provider_for_resource(resource, action = :nothing)
- node = resource.run_context && resource.run_context.node
- raise ArgumentError, "Cannot find the provider for a resource with no run context set" unless node
- provider = find_provider_for_node(node, resource).new(resource, resource.run_context)
- provider.action = action
- provider
- end
-
- def provider_for_node(node, resource_type)
- raise NotImplementedError, "#{self.class.name} no longer supports #provider_for_node"
- end
-
- def find_provider_for_node(node, resource_type)
- platform, version = find_platform_and_version(node)
- find_provider(platform, version, resource_type)
- end
-
- def set(args)
- validate(
- args,
- {
- :platform => {
- :kind_of => Symbol,
- :required => false,
- },
- :version => {
- :kind_of => String,
- :required => false,
- },
- :resource => {
- :kind_of => Symbol,
- },
- :provider => {
- :kind_of => [ String, Symbol, Class ],
- },
- }
- )
- if args.has_key?(:platform)
- if args.has_key?(:version)
- if platforms.has_key?(args[:platform])
- if platforms[args[:platform]].has_key?(args[:version])
- platforms[args[:platform]][args[:version]][args[:resource].to_sym] = args[:provider]
- else
- platforms[args[:platform]][args[:version]] = {
- args[:resource].to_sym => args[:provider],
- }
- end
- else
- platforms[args[:platform]] = {
- args[:version] => {
- args[:resource].to_sym => args[:provider],
- },
- }
- end
- else
- if platforms.has_key?(args[:platform])
- if platforms[args[:platform]].has_key?(:default)
- platforms[args[:platform]][:default][args[:resource].to_sym] = args[:provider]
- elsif args[:platform] == :default
- platforms[:default][args[:resource].to_sym] = args[:provider]
- else
- platforms[args[:platform]] = { :default => { args[:resource].to_sym => args[:provider] } }
- end
- else
- platforms[args[:platform]] = {
- :default => {
- args[:resource].to_sym => args[:provider],
- },
- }
- end
- end
- else
- if platforms.has_key?(:default)
- platforms[:default][args[:resource].to_sym] = args[:provider]
- else
- platforms[:default] = {
- args[:resource].to_sym => args[:provider],
- }
- end
- end
- end
-
- def find_provider(platform, version, resource_type)
- provider_klass = explicit_provider(platform, version, resource_type) ||
- platform_provider(platform, version, resource_type) ||
- resource_matching_provider(platform, version, resource_type)
-
- raise Chef::Exceptions::ProviderNotFound, "Cannot find a provider for #{resource_type} on #{platform} version #{version}" if provider_klass.nil?
-
- provider_klass
- end
-
- private
-
- def explicit_provider(platform, version, resource_type)
- resource_type.kind_of?(Chef::Resource) ? resource_type.provider : nil
+ [platform, version]
end
-
- def platform_provider(platform, version, resource_type)
- pmap = Chef::Platform.find(platform, version)
- rtkey = resource_type.kind_of?(Chef::Resource) ? resource_type.resource_name.to_sym : resource_type
- pmap.has_key?(rtkey) ? pmap[rtkey] : nil
- end
-
- include Chef::Mixin::ConvertToClassName
-
- def resource_matching_provider(platform, version, resource_type)
- if resource_type.kind_of?(Chef::Resource)
- class_name = resource_type.class.name ? resource_type.class.name.split("::").last :
- convert_to_class_name(resource_type.resource_name.to_s)
-
- if Chef::Provider.const_defined?(class_name, false)
- Chef::Log.warn("Class Chef::Provider::#{class_name} does not declare 'provides #{convert_to_snake_case(class_name).to_sym.inspect}'.")
- Chef::Log.warn("This will no longer work in Chef 13: you must use 'provides' to use the resource's DSL.")
- return Chef::Provider.const_get(class_name, false)
- end
- end
- nil
- end
-
end
end
end
diff --git a/lib/chef/platform/query_helpers.rb b/lib/chef/platform/query_helpers.rb
index 7d522072a3..b49010efc0 100644
--- a/lib/chef/platform/query_helpers.rb
+++ b/lib/chef/platform/query_helpers.rb
@@ -24,18 +24,6 @@ class Chef
ChefConfig.windows?
end
- def windows_server_2003?
- # WMI startup shouldn't be performed unless we're on Windows.
- return false unless windows?
- require "wmi-lite/wmi"
-
- wmi = WmiLite::Wmi.new
- host = wmi.first_of("Win32_OperatingSystem")
- is_server_2003 = (host["version"] && host["version"].start_with?("5.2"))
-
- is_server_2003
- end
-
def windows_nano_server?
return false unless windows?
require "win32/registry"
@@ -55,7 +43,7 @@ class Chef
# If accessing the registry key failed, then we're probably not on
# nano. Fail through.
end
- return nano == 1
+ nano == 1
end
def supports_msi?
diff --git a/lib/chef/platform/rebooter.rb b/lib/chef/platform/rebooter.rb
index c678b60dd1..430d175869 100644
--- a/lib/chef/platform/rebooter.rb
+++ b/lib/chef/platform/rebooter.rb
@@ -19,6 +19,7 @@
require "chef/dsl/reboot_pending"
require "chef/log"
require "chef/platform"
+require "chef/application/exit_code"
class Chef
class Platform
@@ -27,19 +28,34 @@ class Chef
class << self
+ include Chef::DSL::RebootPending
+
def reboot!(node)
reboot_info = node.run_context.reboot_info
- cmd = if Chef::Platform.windows?
+ cmd = case
+ when Chef::Platform.windows?
# should this do /f as well? do we then need a minimum delay to let apps quit?
- "shutdown /r /t #{reboot_info[:delay_mins] * 60} /c \"#{reboot_info[:reason]}\""
+ # Use explicit path to shutdown.exe, to protect against https://github.com/chef/chef/issues/5594
+ windows_shutdown_path = "#{ENV['SYSTEMROOT']}/System32/shutdown.exe"
+ "#{windows_shutdown_path} /r /t #{reboot_info[:delay_mins] * 60} /c \"#{reboot_info[:reason]}\""
+ when node["os"] == "solaris2"
+ # SysV-flavored shutdown
+ "shutdown -i6 -g#{reboot_info[:delay_mins]} -y \"#{reboot_info[:reason]}\" &"
else
- # probably Linux-only.
- "shutdown -r +#{reboot_info[:delay_mins]} \"#{reboot_info[:reason]}\""
+ # Linux/BSD/Mac/AIX and other systems with BSD-ish shutdown
+ "shutdown -r +#{reboot_info[:delay_mins]} \"#{reboot_info[:reason]}\" &"
end
- Chef::Log.warn "Rebooting server at a recipe's request. Details: #{reboot_info.inspect}"
- shell_out!(cmd)
+ msg = "Rebooting server at a recipe's request. Details: #{reboot_info.inspect}"
+ begin
+ Chef::Log.warn msg
+ shell_out!(cmd)
+ rescue Mixlib::ShellOut::ShellCommandFailed => e
+ raise Chef::Exceptions::RebootFailed.new(e.message)
+ end
+
+ raise Chef::Exceptions::Reboot.new(msg)
end
# this is a wrapper function so Chef::Client only needs a single line of code.
diff --git a/lib/chef/platform/resource_handler_map.rb b/lib/chef/platform/resource_handler_map.rb
index be1b9c28f5..0aab04212f 100644
--- a/lib/chef/platform/resource_handler_map.rb
+++ b/lib/chef/platform/resource_handler_map.rb
@@ -17,12 +17,12 @@
#
require "singleton"
-require "chef/platform/handler_map"
+require "chef/node_map"
class Chef
class Platform
# @api private
- class ResourceHandlerMap < Chef::Platform::HandlerMap
+ class ResourceHandlerMap < Chef::NodeMap
include Singleton
end
end
diff --git a/lib/chef/platform/service_helpers.rb b/lib/chef/platform/service_helpers.rb
index 87b87d4c72..02d293449c 100644
--- a/lib/chef/platform/service_helpers.rb
+++ b/lib/chef/platform/service_helpers.rb
@@ -69,7 +69,7 @@ class Chef
configs = []
if ::File.exist?(Chef.path_to("/etc/init.d/#{service_name}"))
- configs << :initd
+ configs += [ :initd, :systemd ]
end
if ::File.exist?(Chef.path_to("/etc/init/#{service_name}.conf"))
diff --git a/lib/chef/policy_builder/dynamic.rb b/lib/chef/policy_builder/dynamic.rb
index 389f124f84..84021ea611 100644
--- a/lib/chef/policy_builder/dynamic.rb
+++ b/lib/chef/policy_builder/dynamic.rb
@@ -63,10 +63,10 @@ class Chef
# @return [Chef::Node] the loaded node.
def load_node
events.node_load_start(node_name, config)
- Chef::Log.debug("Building node object for #{node_name}")
+ Chef::Log.trace("Building node object for #{node_name}")
@node =
- if Chef::Config[:solo]
+ if Chef::Config[:solo_legacy_mode]
Chef::Node.build(node_name)
else
Chef::Node.find_or_create(node_name)
diff --git a/lib/chef/policy_builder/expand_node_object.rb b/lib/chef/policy_builder/expand_node_object.rb
index 980de60dd5..b390042ea7 100644
--- a/lib/chef/policy_builder/expand_node_object.rb
+++ b/lib/chef/policy_builder/expand_node_object.rb
@@ -3,7 +3,7 @@
# Author:: Tim Hinderliter (<tim@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2008-2016 Chef Software, Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -69,7 +69,7 @@ class Chef
end
def setup_run_context(specific_recipes = nil)
- if Chef::Config[:solo]
+ if Chef::Config[:solo_legacy_mode]
Chef::Cookbook::FileVendor.fetch_from_disk(Chef::Config[:cookbook_path])
cl = Chef::CookbookLoader.new(Chef::Config[:cookbook_path])
cl.load_cookbooks
@@ -103,33 +103,6 @@ class Chef
run_context
end
- # DEPRECATED: As of Chef 12.5, chef selects either policyfile mode or
- # "expand node" mode dynamically, based on the content of the node
- # object, first boot JSON, and config. This happens in
- # PolicyBuilder::Dynamic, which selects the implementation during
- # #load_node and then delegates to either ExpandNodeObject or Policyfile
- # implementations as appropriate. Tools authors should update their code
- # to create a PolicyBuilder::Dynamc policy builder and allow it to select
- # the proper implementation.
- def load_node
- Chef.log_deprecation("ExpandNodeObject#load_node is deprecated. Please use Chef::PolicyBuilder::Dynamic instead of using ExpandNodeObject directly")
-
- events.node_load_start(node_name, config)
- Chef::Log.debug("Building node object for #{node_name}")
-
- @node =
- if Chef::Config[:solo]
- Chef::Node.build(node_name)
- else
- Chef::Node.find_or_create(node_name)
- end
- finish_load_node(node)
- node
- rescue Exception => e
- events.node_load_failed(node_name, e, config)
- raise
- end
-
def finish_load_node(node)
@node = node
end
@@ -167,7 +140,7 @@ class Chef
# Expands the node's run list. Stores the run_list_expansion object for later use.
def expand_run_list
- @run_list_expansion = if Chef::Config[:solo]
+ @run_list_expansion = if Chef::Config[:solo_legacy_mode]
node.expand!("disk")
else
node.expand!("server")
@@ -197,7 +170,7 @@ class Chef
# === Returns
# Hash:: The hash of cookbooks with download URLs as given by the server
def sync_cookbooks
- Chef::Log.debug("Synchronizing cookbooks")
+ Chef::Log.trace("Synchronizing cookbooks")
begin
events.cookbook_resolution_start(@expanded_run_list_with_versions)
@@ -266,7 +239,8 @@ class Chef
end
def api_service
- @api_service ||= Chef::ServerAPI.new(config[:chef_server_url])
+ @api_service ||= Chef::ServerAPI.new(config[:chef_server_url],
+ { version_class: Chef::CookbookManifestVersions })
end
def config
diff --git a/lib/chef/policy_builder/policyfile.rb b/lib/chef/policy_builder/policyfile.rb
index 05e5c2f3ef..80ecda1850 100644
--- a/lib/chef/policy_builder/policyfile.rb
+++ b/lib/chef/policy_builder/policyfile.rb
@@ -51,7 +51,32 @@ class Chef
class PolicyfileError < StandardError; end
- RunListExpansionIsh = Struct.new(:recipes, :roles)
+ RunListExpansionIsh = Struct.new(:recipes, :roles) do
+ # Implementing the parts of the RunListExpansion
+ # interface we need to properly send this through to
+ # events.run_list_expanded as it is expecting a RunListExpansion
+ # object.
+ def to_hash
+ # It looks like version only gets populated in the expanded_run_list when
+ # using a little used feature of roles to version lock cookbooks, so
+ # version is not reliable in here anyway (places like Automate UI are
+ # not getting version out of here.
+ #
+ # Skipped will always be false as it can only be true when two expanded
+ # roles contain the same recipe.
+ expanded_run_list = recipes.map do |r|
+ { type: "recipe", name: r, skipped: false, version: nil }
+ end
+ data_collector_hash = {}
+ data_collector_hash[:id] = "_policy_node"
+ data_collector_hash[:run_list] = expanded_run_list
+ data_collector_hash
+ end
+
+ def to_json(*opts)
+ to_hash.to_json(*opts)
+ end
+ end
attr_reader :events
attr_reader :node
@@ -68,7 +93,7 @@ class Chef
@node = nil
- if Chef::Config[:solo]
+ if Chef::Config[:solo_legacy_mode]
raise UnsupportedFeature, "Policyfile does not support chef-solo. Use chef-client local mode instead."
end
@@ -137,6 +162,12 @@ class Chef
Chef::Log.info("Run List expands to [#{run_list_with_versions_for_display.join(', ')}]")
events.node_load_completed(node, run_list_with_versions_for_display, Chef::Config)
+ events.run_list_expanded(run_list_expansion_ish)
+
+ # we must do this after `node.consume_external_attrs`
+ node.automatic_attrs[:policy_name] = node.policy_name
+ node.automatic_attrs[:policy_group] = node.policy_group
+ node.automatic_attrs[:chef_environment] = node.policy_group
node
rescue Exception => e
@@ -149,7 +180,7 @@ class Chef
#
# @return [Chef::RunContext]
def setup_run_context(specific_recipes = nil)
- Chef::Cookbook::FileVendor.fetch_from_remote(http_api)
+ Chef::Cookbook::FileVendor.fetch_from_remote(api_service)
sync_cookbooks
cookbook_collection = Chef::CookbookCollection.new(cookbooks_to_sync)
cookbook_collection.validate!
@@ -173,6 +204,7 @@ class Chef
CookbookCacheCleaner.instance.skip_removal = true if named_run_list_requested?
node.run_list(run_list)
+ node.automatic_attrs[:policy_revision] = revision_id
node.automatic_attrs[:roles] = []
node.automatic_attrs[:recipes] = run_list_expansion_ish.recipes
run_list_expansion_ish
@@ -184,7 +216,7 @@ class Chef
# @return [Hash{String => Chef::CookbookManifest}] A map of
# CookbookManifest objects by cookbook name.
def sync_cookbooks
- Chef::Log.debug("Synchronizing cookbooks")
+ Chef::Log.trace("Synchronizing cookbooks")
synchronizer = Chef::CookbookSynchronizer.new(cookbooks_to_sync, events)
synchronizer.sync_cookbooks
@@ -236,6 +268,16 @@ class Chef
def apply_policyfile_attributes
node.attributes.role_default = policy["default_attributes"]
node.attributes.role_override = policy["override_attributes"]
+ hoist_policyfile_attributes(node.policy_group) if node.policy_group
+ end
+
+ # @api private
+ #
+ # Hoists attributes from role_X[policy_group] up to the equivalent role_X level
+ def hoist_policyfile_attributes(policy_group)
+ Chef::Log.trace("Running attribute Hoist for group #{policy_group}")
+ Chef::Mixin::DeepMerge.hash_only_merge!(node.role_default, node.role_default[policy_group]) if node.role_default.include?(policy_group)
+ Chef::Mixin::DeepMerge.hash_only_merge!(node.role_override, node.role_override[policy_group]) if node.role_override.include?(policy_group)
end
# @api private
@@ -266,7 +308,7 @@ class Chef
# @api private
def policy
- @policy ||= http_api.get(policyfile_location)
+ @policy ||= api_service.get(policyfile_location)
rescue Net::HTTPServerException => e
raise ConfigurationError, "Error loading policyfile from `#{policyfile_location}': #{e.class} - #{e.message}"
end
@@ -363,6 +405,7 @@ class Chef
node.policy_name = policy_name_to_set
node.policy_group = policy_group_to_set
+ node.chef_environment = policy_group_to_set
Chef::Config[:policy_name] = policy_name_to_set
Chef::Config[:policy_group] = policy_group_to_set
@@ -451,8 +494,9 @@ class Chef
end
# @api private
- def http_api
- @api_service ||= Chef::ServerAPI.new(config[:chef_server_url])
+ def api_service
+ @api_service ||= Chef::ServerAPI.new(config[:chef_server_url],
+ { version_class: Chef::CookbookManifestVersions })
end
# @api private
@@ -495,7 +539,7 @@ class Chef
def compat_mode_manifest_for(cookbook_name, lock_data)
xyz_version = lock_data["dotted_decimal_identifier"]
rel_url = "cookbooks/#{cookbook_name}/#{xyz_version}"
- inflate_cbv_object(http_api.get(rel_url))
+ inflate_cbv_object(api_service.get(rel_url))
rescue Exception => e
message = "Error loading cookbook #{cookbook_name} at version #{xyz_version} from #{rel_url}: #{e.class} - #{e.message}"
err = Chef::Exceptions::CookbookNotFound.new(message)
@@ -506,7 +550,7 @@ class Chef
def artifact_manifest_for(cookbook_name, lock_data)
identifier = lock_data["identifier"]
rel_url = "cookbook_artifacts/#{cookbook_name}/#{identifier}"
- inflate_cbv_object(http_api.get(rel_url))
+ inflate_cbv_object(api_service.get(rel_url))
rescue Exception => e
message = "Error loading cookbook #{cookbook_name} with identifier #{identifier} from #{rel_url}: #{e.class} - #{e.message}"
err = Chef::Exceptions::CookbookNotFound.new(message)
diff --git a/lib/chef/powershell.rb b/lib/chef/powershell.rb
new file mode 100644
index 0000000000..0d3f82590a
--- /dev/null
+++ b/lib/chef/powershell.rb
@@ -0,0 +1,55 @@
+#
+# Author:: Stuart Preston (<stuart@chef.io>)
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "chef/json_compat"
+require "win32ole" if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+
+class Chef
+ class PowerShell
+
+ attr_reader :result
+ attr_reader :errors
+
+ # Run a command under PowerShell via a managed (.NET) COM interop API.
+ # This implementation requires the managed dll to be registered on the
+ # target machine.
+ #
+ # Requires: .NET Framework 4.0 or higher on the target machine.
+ #
+ # @param script [String] script to run
+ # @return [Object] output
+ def initialize(script)
+ raise "Chef::PowerShell can only be used on the Windows platform." unless RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ exec(script)
+ end
+
+ def error?
+ return true if errors.count > 0
+ false
+ end
+
+ private
+
+ def exec(script)
+ ps = WIN32OLE.new("Chef.PowerShell")
+ outcome = ps.ExecuteScript(script)
+ hashed_outcome = Chef::JSONCompat.parse(outcome)
+ @result = Chef::JSONCompat.parse(hashed_outcome["result"])
+ @errors = hashed_outcome["errors"]
+ end
+ end
+end
diff --git a/lib/chef/property.rb b/lib/chef/property.rb
index 45ab4dd522..3ad28fad10 100644
--- a/lib/chef/property.rb
+++ b/lib/chef/property.rb
@@ -51,6 +51,27 @@ class Chef
new(**options)
end
+ # This is to support #deprecated_property_alias, by emitting an alias and a
+ # deprecatation warning when called.
+ #
+ # @param from [String] Name of the deprecated property
+ # @param to [String] Name of the correct property
+ # @param message [String] Deprecation message to show to the cookbook author
+ # @param declared_in [Class] Class this property comes from
+ #
+ def self.emit_deprecated_alias(from, to, message, declared_in)
+ declared_in.class_eval <<-EOM, __FILE__, __LINE__ + 1
+ def #{from}(value=NOT_PASSED)
+ Chef.deprecated(:property, "#{message}")
+ #{to}(value)
+ end
+ def #{from}=(value)
+ Chef.deprecated(:property, "#{message}")
+ #{to} = value
+ end
+ EOM
+ end
+
#
# Create a new property.
#
@@ -60,10 +81,12 @@ class Chef
# options).
# @option options [Symbol] :name The name of this property.
# @option options [Class] :declared_in The class this property comes from.
+ # @option options [String] :description A description of the property.
# @option options [Symbol] :instance_variable_name The instance variable
# tied to this property. Must include a leading `@`. Defaults to `@<name>`.
# `nil` means the property is opaque and not tied to a specific instance
# variable.
+ # @option options [String] :introduced The release that introduced this property
# @option options [Boolean] :desired_state `true` if this property is part of desired
# state. Defaults to `true`.
# @option options [Boolean] :identity `true` if this property is part of object
@@ -74,7 +97,7 @@ class Chef
# return `true` if the property is set *or* if `name` is set.
# @option options [Boolean] :nillable `true` opt-in to Chef-13 style behavior where
# attempting to set a nil value will really set a nil value instead of issuing
- # a warning and operating like a getter
+ # a warning and operating like a getter [DEPRECATED]
# @option options [Object] :default The value this property
# will return if the user does not set one. If this is `lazy`, it will
# be run in the context of the instance (and able to access other
@@ -88,6 +111,8 @@ class Chef
# @option options [Boolean] :required `true` if this property
# must be present; `false` otherwise. This is checked after the resource
# is fully initialized.
+ # @option options [String] :deprecated If set, this property is deprecated and
+ # will create a deprecation warning.
#
def initialize(**options)
options = options.inject({}) { |memo, (key, value)| memo[key.to_sym] = value; memo }
@@ -99,24 +124,29 @@ class Chef
if options.has_key?(:name_attribute)
# If we have both name_attribute and name_property and they differ, raise an error
if options.has_key?(:name_property)
- raise ArgumentError, "Cannot specify both name_property and name_attribute together on property #{self}."
+ raise ArgumentError, "name_attribute and name_property are functionally identical and both cannot be specified on a property at once. Use just one on property #{self}"
end
# replace name_property with name_attribute in place
options = Hash[options.map { |k, v| k == :name_attribute ? [ :name_property, v ] : [ k, v ] }]
@options = options
end
- # Only pick the first of :default, :name_property and :name_attribute if
- # more than one is specified.
- if options.has_key?(:default) && options[:name_property]
- if options[:default].nil? || options.keys.index(:name_property) < options.keys.index(:default)
- options.delete(:default)
- preferred_default = :name_property
- else
- options.delete(:name_property)
- preferred_default = :default
+ if options.has_key?(:default) && options.has_key?(:name_property)
+ raise ArgumentError, "A property cannot be both a name_property/name_attribute and have a default value. Use one or the other on property #{self}"
+ end
+
+ # Recursively freeze the default if it isn't a lazy value.
+ unless default.is_a?(DelayedEvaluator)
+ visitor = lambda do |obj|
+ case obj
+ when Hash
+ obj.each_value { |value| visitor.call(value) }
+ when Array
+ obj.each { |value| visitor.call(value) }
+ end
+ obj.freeze
end
- Chef.log_deprecation("Cannot specify both default and name_property together on property #{self}. Only one (#{preferred_default}) will be obeyed. In Chef 13, this will become an error. Please remove one or the other from the property.")
+ visitor.call(default)
end
# Validate the default early, so the user gets a good error message, and
@@ -153,6 +183,24 @@ class Chef
end
#
+ # A description of this property.
+ #
+ # @return [String]
+ #
+ def description
+ options[:description]
+ end
+
+ #
+ # When this property was introduced
+ #
+ # @return [String]
+ #
+ def introduced
+ options[:introduced]
+ end
+
+ #
# The instance variable associated with this property.
#
# Defaults to `@<name>`
@@ -230,14 +278,25 @@ class Chef
end
#
+ # Whether this property is sensitive or not.
+ #
+ # Defaults to false.
+ #
+ # @return [Boolean]
+ #
+ def sensitive?
+ options.fetch(:sensitive, false)
+ end
+
+ #
# Validation options. (See Chef::Mixin::ParamsValidate#validate.)
#
# @return [Hash<Symbol,Object>]
#
def validation_options
- @validation_options ||= options.reject { |k, v|
- [:declared_in, :name, :instance_variable_name, :desired_state, :identity, :default, :name_property, :coerce, :required, :nillable].include?(k)
- }
+ @validation_options ||= options.reject do |k, v|
+ [:declared_in, :name, :instance_variable_name, :desired_state, :identity, :default, :name_property, :coerce, :required, :nillable, :sensitive, :description, :introduced, :deprecated].include?(k)
+ end
end
#
@@ -262,32 +321,8 @@ class Chef
#
def call(resource, value = NOT_PASSED)
if value == NOT_PASSED
- return get(resource)
- end
-
- if value.nil? && !nillable?
- # In Chef 12, value(nil) does a *get* instead of a set, so we
- # warn if the value would have been changed. In Chef 13, it will be
- # equivalent to value = nil.
- result = get(resource, nil_set: true)
-
- # Warn about this becoming a set in Chef 13.
- begin
- input_to_stored_value(resource, value)
- # If nil is valid, and it would change the value, warn that this will change to a set.
- if !result.nil?
- Chef.log_deprecation("An attempt was made to change #{name} from #{result.inspect} to nil by calling #{name}(nil). In Chef 12, this does a get rather than a set. In Chef 13, this will change to set the value to nil.")
- end
- rescue Chef::Exceptions::DeprecatedFeatureError
- raise
- rescue
- # If nil is invalid, warn that this will become an error.
- Chef.log_deprecation("nil is an invalid value for #{self}. In Chef 13, this warning will change to an error. Error: #{$!}")
- end
-
- result
+ get(resource)
else
- # Anything else, such as myprop(value) is a set
set(resource, value)
end
end
@@ -316,38 +351,14 @@ class Chef
#
def get(resource, nil_set: false)
# If it's set, return it (and evaluate any lazy values)
+ value = nil
+
if is_set?(resource)
value = get_value(resource)
value = stored_value_to_output(resource, value)
-
else
# We are getting the default value.
- # If the user does something like this:
- #
- # ```
- # class MyResource < Chef::Resource
- # property :content
- # action :create do
- # file '/x.txt' do
- # content content
- # end
- # end
- # end
- # ```
- #
- # It won't do what they expect. This checks whether you try to *read*
- # `content` while we are compiling the resource.
- if !nil_set &&
- resource.respond_to?(:resource_initializing) &&
- resource.resource_initializing &&
- resource.respond_to?(:enclosing_provider) &&
- resource.enclosing_provider &&
- resource.enclosing_provider.new_resource &&
- resource.enclosing_provider.new_resource.respond_to?(name)
- Chef::Log.warn("#{Chef::Log.caller_location}: property #{name} is declared in both #{resource} and #{resource.enclosing_provider}. Use new_resource.#{name} instead. At #{Chef::Log.caller_location}")
- end
-
if has_default?
# If we were able to cache the stored_default, grab it.
if defined?(@stored_default)
@@ -356,7 +367,7 @@ class Chef
# Otherwise, we have to validate it now.
value = input_to_stored_value(resource, default, is_default: true)
end
- value = stored_value_to_output(resource, value, is_default: true)
+ value = stored_value_to_output(resource, value)
# If the value is mutable (non-frozen), we set it on the instance
# so that people can mutate it. (All constant default values are
@@ -364,13 +375,14 @@ class Chef
if !value.frozen? && !value.nil?
set_value(resource, value)
end
-
- value
-
- elsif required?
- raise Chef::Exceptions::ValidationFailed, "#{name} is required"
end
end
+
+ if value.nil? && required?
+ raise Chef::Exceptions::ValidationFailed, "#{name} is a required property"
+ else
+ value
+ end
end
#
@@ -389,7 +401,17 @@ class Chef
# this property.
#
def set(resource, value)
- set_value(resource, input_to_stored_value(resource, value))
+ value = set_value(resource, input_to_stored_value(resource, value))
+
+ if options.has_key?(:deprecated)
+ Chef.deprecated(:property, options[:deprecated])
+ end
+
+ if value.nil? && required?
+ raise Chef::Exceptions::ValidationFailed, "#{name} is a required property"
+ else
+ value
+ end
end
#
@@ -442,8 +464,8 @@ class Chef
#
def coerce(resource, value)
if options.has_key?(:coerce)
- # If we have no default value, `nil` is never coerced or validated
- unless !has_default? && value.nil?
+ # nil is never coerced
+ unless value.nil?
value = exec_in_resource(resource, options[:coerce], value)
end
end
@@ -457,15 +479,15 @@ class Chef
# options.
#
# @param resource [Chef::Resource] The resource we're validating against
- # (to provide context for the validate).
+ # (to provide context for the validation).
# @param value The value to validate.
#
# @raise Chef::Exceptions::ValidationFailed If the value is invalid for
# this property.
#
def validate(resource, value)
- # If we have no default value, `nil` is never coerced or validated
- unless value.nil? && !has_default?
+ # nils are not validated unless we have an explicit default value
+ if !value.nil? || has_default?
if resource
resource.validate({ name => value }, { name => validation_options })
else
@@ -507,32 +529,30 @@ class Chef
# be using the existing getter/setter to manipulate it instead.
return if !instance_variable_name
+ # Properties may override existing properties up the inheritance heirarchy, but
+ # properties must not override inherited methods like Object#hash. When the Resource is
+ # placed into the resource collection the ruby Hash object will call the
+ # Object#hash method on the resource, and overriding that with a property will cause
+ # very confusing results.
+ if property_redefines_method?
+ resource_name = declared_in.respond_to?(:resource_name) ? declared_in.resource_name : declared_in
+ raise ArgumentError, "Property `#{name}` of resource `#{resource_name}` overwrites an existing method. A different name should be used for this property."
+ end
+
# We prefer this form because the property name won't show up in the
# stack trace if you use `define_method`.
declared_in.class_eval <<-EOM, __FILE__, __LINE__ + 1
def #{name}(value=NOT_PASSED)
- raise "Property #{name} of \#{self} cannot be passed a block! If you meant to create a resource named #{name} instead, you'll need to first rename the property." if block_given?
+ raise "Property `#{name}` of `\#{self}` was incorrectly passed a block. Possible property-resource collision. To call a resource named `#{name}` either rename the property or else use `declare_resource(:#{name}, ...)`" if block_given?
self.class.properties[#{name.inspect}].call(self, value)
end
def #{name}=(value)
- raise "Property #{name} of \#{self} cannot be passed a block! If you meant to create a resource named #{name} instead, you'll need to first rename the property." if block_given?
+ raise "Property `#{name}` of `\#{self}` was incorrectly passed a block. Possible property-resource collision. To call a resource named `#{name}` either rename the property or else use `declare_resource(:#{name}, ...)`" if block_given?
self.class.properties[#{name.inspect}].set(self, value)
end
EOM
- rescue SyntaxError
- # If the name is not a valid ruby name, we use define_method.
- declared_in.define_method(name) do |value = NOT_PASSED, &block|
- raise "Property #{name} of #{self} cannot be passed a block! If you meant to create a resource named #{name} instead, you'll need to first rename the property." if block
- self.class.properties[name].call(self, value)
- end
- declared_in.define_method("#{name}=") do |value, &block|
- raise "Property #{name} of #{self} cannot be passed a block! If you meant to create a resource named #{name} instead, you'll need to first rename the property." if block
- self.class.properties[name].set(self, value)
- end
end
- protected
-
#
# The options this Property will use for get/set behavior and validation.
#
@@ -580,9 +600,12 @@ class Chef
# @api private
def explicitly_accepts_nil?(resource)
options.has_key?(:coerce) ||
- (options.has_key?(:is) && resource.send(:_pv_is, { name => nil }, name, options[:is], raise_error: false))
+ (options.has_key?(:is) && Chef::Mixin::ParamsValidate.send(:_pv_is, { name => nil }, name, options[:is]))
+ rescue Chef::Exceptions::ValidationFailed, Chef::Exceptions::CannotValidateStaticallyError
+ false
end
+ # @api private
def get_value(resource)
if instance_variable_name
resource.instance_variable_get(instance_variable_name)
@@ -591,6 +614,7 @@ class Chef
end
end
+ # @api private
def set_value(resource, value)
if instance_variable_name
resource.instance_variable_set(instance_variable_name, value)
@@ -599,6 +623,7 @@ class Chef
end
end
+ # @api private
def value_is_set?(resource)
if instance_variable_name
resource.instance_variable_defined?(instance_variable_name)
@@ -607,6 +632,7 @@ class Chef
end
end
+ # @api private
def reset_value(resource)
if instance_variable_name
if value_is_set?(resource)
@@ -617,6 +643,27 @@ class Chef
end
end
+ private
+
+ def property_redefines_method?
+ # We only emit deprecations if this property already exists as an instance method.
+ # Weeding out class methods avoids unnecessary deprecations such Chef::Resource
+ # defining a `name` property when there's an already-existing `name` method
+ # for a Module.
+ return false unless declared_in.instance_methods.include?(name)
+
+ # Only emit deprecations for some well-known classes. This will still
+ # allow more advanced users to subclass their own custom resources and
+ # override their own properties.
+ return false unless [ Object, BasicObject, Kernel, Chef::Resource ].include?(declared_in.instance_method(name).owner)
+
+ # Allow top-level Chef::Resource proprties, such as `name`, to be overridden.
+ # As of this writing, `name` is the only Chef::Resource property created with the
+ # `property` definition, but this will allow for future properties to be extended
+ # as needed.
+ !Chef::Resource.properties.keys.include?(name)
+ end
+
def exec_in_resource(resource, proc, *args)
if resource
if proc.arity > args.size
@@ -631,51 +678,30 @@ class Chef
end
def input_to_stored_value(resource, value, is_default: false)
+ if value.nil? && !is_default && !explicitly_accepts_nil?(resource)
+ value = default
+ end
unless value.is_a?(DelayedEvaluator)
- value = coerce_and_validate(resource, value, is_default: is_default)
+ value = coerce_and_validate(resource, value)
end
value
end
- def stored_value_to_output(resource, value, is_default: false)
+ def stored_value_to_output(resource, value)
# Crack open lazy values before giving the result to the user
if value.is_a?(DelayedEvaluator)
value = exec_in_resource(resource, value)
- value = coerce_and_validate(resource, value, is_default: is_default)
+ value = coerce_and_validate(resource, value)
end
value
end
- # Coerces and validates the value. If the value is a default, it will warn
- # the user that invalid defaults are bad mmkay, and return it as if it were
- # valid.
- def coerce_and_validate(resource, value, is_default: false)
+ # Coerces and validates the value.
+ def coerce_and_validate(resource, value)
result = coerce(resource, value)
- begin
- # If the input is from a default, we need to emit an invalid default warning on validate.
- validate(resource, result)
- rescue Chef::Exceptions::CannotValidateStaticallyError
- # This one gets re-raised
- raise
- rescue
- # Anything else is just an invalid default: in those cases, we just
- # warn and return the (possibly coerced) value to the user.
- if is_default
- if value.nil?
- Chef.log_deprecation("Default value nil is invalid for property #{self}. Possible fixes: 1. Remove 'default: nil' if nil means 'undefined'. 2. Set a valid default value if there is a reasonable one. 3. Allow nil as a valid value of your property (for example, 'property #{name.inspect}, [ String, nil ], default: nil'). Error: #{$!}")
- else
- Chef.log_deprecation("Default value #{value.inspect} is invalid for property #{self}. In Chef 13 this will become an error: #{$!}.")
- end
- else
- raise
- end
- end
+ validate(resource, result)
result
end
-
- def nillable?
- !!options[:nillable]
- end
end
end
diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb
index 03b546c09d..1751c35129 100644
--- a/lib/chef/provider.rb
+++ b/lib/chef/provider.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2008-2016, 2009-2016 Chef Software, Inc.
+# Copyright:: Copyright 2008-2016, 2009-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -38,10 +38,11 @@ class Chef
attr_accessor :run_context
attr_reader :recipe_name
- attr_reader :cookbook_name
+ attr_reader :logger
include Chef::Mixin::WhyRun
extend Chef::Mixin::Provides
+ extend Forwardable
# includes the "core" DSL and not the "recipe" DSL by design
include Chef::DSL::Core
@@ -51,6 +52,36 @@ class Chef
true
end
+ # Defines an action method on the provider, running the block to compile the
+ # resources, converging them, and then checking if any were updated (and
+ # updating new-resource if so)
+ #
+ # @since 13.0
+ # @param name [String, Symbol] Name of the action to define.
+ # @param block [Proc] Body of the action.
+ # @return [void]
+ def self.action(name, &block)
+ # We need the block directly in a method so that `super` works.
+ define_method("compile_action_#{name}", &block)
+ class_eval <<-EOM
+ def action_#{name}
+ compile_and_converge_action { compile_action_#{name} }
+ end
+ EOM
+ end
+
+ # Deprecation stub for the old use_inline_resources mode.
+ #
+ # @return [void]
+ def self.use_inline_resources
+ # Uncomment this in Chef 13.6.
+ # Chef.deprecated(:use_inline_resources, "The use_inline_resources mode is no longer optional and the line enabling it can be removed")
+ end
+
+ # delegate to the resource
+ #
+ def_delegators :@new_resource, :property_is_set?
+
#--
# TODO: this should be a reader, and the action should be passed in the
# constructor; however, many/most subclasses override the constructor so
@@ -65,6 +96,12 @@ class Chef
@run_context = run_context
@converge_actions = nil
+ @logger = if run_context
+ run_context.logger.with_child({ resource: new_resource.name, cookbook: cookbook_name, recipe: recipe_name })
+ else
+ Chef::Log.with_child({ resource: new_resource.name, cookbook: cookbook_name, recipe: recipe_name })
+ end
+
@recipe_name = nil
@cookbook_name = nil
self.class.include_resource_dsl_module(new_resource)
@@ -75,7 +112,7 @@ class Chef
end
def whyrun_supported?
- false
+ true
end
def node
@@ -105,7 +142,7 @@ class Chef
end
def action_nothing
- Chef::Log.debug("Doing nothing for #{@new_resource}")
+ logger.trace("Doing nothing for #{@new_resource}")
true
end
@@ -172,10 +209,40 @@ class Chef
@requirements ||= ResourceRequirements.new(@new_resource, run_context)
end
+ def description(description = "NOT_PASSED")
+ if description != "NOT_PASSED"
+ @description = description
+ end
+ @description
+ end
+
+ def introduced(introduced = "NOT_PASSED")
+ if introduced != "NOT_PASSED"
+ @introduced = introduced
+ end
+ @introduced
+ end
+
def converge_by(descriptions, &block)
converge_actions.add_action(descriptions, &block)
end
+ # Create a child run_context, compile the block, and converge it.
+ #
+ # @api private
+ def compile_and_converge_action(&block)
+ old_run_context = run_context
+ @run_context = run_context.create_child
+ return_value = instance_eval(&block)
+ Chef::Runner.new(run_context).converge
+ return_value
+ ensure
+ if run_context.resource_collection.any? { |r| r.updated? }
+ new_resource.updated_by_last_action(true)
+ end
+ @run_context = old_run_context
+ end
+
#
# Handle patchy convergence safely.
#
@@ -205,19 +272,25 @@ class Chef
specified_properties = properties.select { |property| new_resource.property_is_set?(property) }
modified = specified_properties.select { |p| new_resource.send(p) != current_resource.send(p) }
if modified.empty?
- properties_str = if sensitive
+ properties_str = if new_resource.sensitive
specified_properties.join(", ")
else
- specified_properties.map { |p| "#{p}=#{new_resource.send(p).inspect}" }.join(", ")
+ specified_properties.map do |property|
+ "#{property}=" << if new_resource.class.properties[property].sensitive?
+ "(suppressed sensitive property)"
+ else
+ new_resource.send(property).inspect
+ end
+ end.join(", ")
end
- Chef::Log.debug("Skipping update of #{new_resource}: has not changed any of the specified properties #{properties_str}.")
+ logger.debug("Skipping update of #{new_resource}: has not changed any of the specified properties #{properties_str}.")
return false
end
# Print the pretty green text and run the block
property_size = modified.map { |p| p.size }.max
modified.map! do |p|
- properties_str = if sensitive
+ properties_str = if new_resource.sensitive || new_resource.class.properties[p].sensitive?
"(suppressed sensitive property)"
else
"#{new_resource.send(p).inspect} (was #{current_resource.send(p).inspect})"
@@ -232,7 +305,7 @@ class Chef
property_size = properties.map { |p| p.size }.max
created = properties.map do |property|
default = " (default value)" unless new_resource.property_is_set?(property)
- properties_str = if sensitive
+ properties_str = if new_resource.sensitive || new_resource.class.properties[property].sensitive?
"(suppressed sensitive property)"
else
new_resource.send(property).inspect
@@ -267,132 +340,36 @@ class Chef
# @param include_resource_dsl [Boolean] Whether to include resource DSL or
# not (defaults to `false`).
#
- def self.include_resource_dsl(include_resource_dsl)
- @include_resource_dsl = include_resource_dsl
+ def self.include_resource_dsl?
+ false
end
# Create the resource DSL module that forwards resource methods to new_resource
#
# @api private
def self.include_resource_dsl_module(resource)
- if @include_resource_dsl && !defined?(@included_resource_dsl_module)
+ if include_resource_dsl? && !defined?(@included_resource_dsl_module)
provider_class = self
@included_resource_dsl_module = Module.new do
extend Forwardable
define_singleton_method(:to_s) { "forwarder module for #{provider_class}" }
define_singleton_method(:inspect) { to_s }
- # Add a delegator for each explicit property that will get the *current* value
- # of the property by default instead of the *actual* value.
- resource.class.properties.each do |name, property|
- class_eval(<<-EOM, __FILE__, __LINE__)
- def #{name}(*args, &block)
- # If no arguments were passed, we process "get" by defaulting
- # the value to current_resource, not new_resource. This helps
- # avoid issues where resources accidentally overwrite perfectly
- # valid stuff with default values.
- if args.empty? && !block
- if !new_resource.property_is_set?(__method__) && current_resource
- return current_resource.public_send(__method__)
- end
- end
- new_resource.public_send(__method__, *args, &block)
- end
- EOM
- end
+ # this magic, stated simply, is that any instance method declared directly on
+ # the resource we are building, will be accessible from the action_class(provider)
+ # instance. methods declared on Chef::Resource and properties are not inherited.
dsl_methods =
resource.class.public_instance_methods +
resource.class.protected_instance_methods -
provider_class.instance_methods -
- resource.class.properties.keys
+ resource.class.properties.keys -
+ resource.class.properties.keys.map { |k| "#{k}=".to_sym } -
+ Chef::Resource.instance_methods
def_delegators(:new_resource, *dsl_methods)
end
include @included_resource_dsl_module
end
end
- # Enables inline evaluation of resources in provider actions.
- #
- # Without this option, any resources declared inside the Provider are added
- # to the resource collection after the current position at the time the
- # action is executed. Because they are added to the primary resource
- # collection for the chef run, they can notify other resources outside
- # the Provider, and potentially be notified by resources outside the Provider
- # (but this is complicated by the fact that they don't exist until the
- # provider executes). In this mode, it is impossible to correctly set the
- # updated_by_last_action flag on the parent Provider resource, since it
- # executes and returns before its component resources are run.
- #
- # With this option enabled, each action creates a temporary run_context
- # with its own resource collection, evaluates the action's code in that
- # context, and then converges the resources created. If any resources
- # were updated, then this provider's new_resource will be marked updated.
- #
- # In this mode, resources created within the Provider cannot interact with
- # external resources via notifies, though notifications to other
- # resources within the Provider will work. Delayed notifications are executed
- # at the conclusion of the provider's action, *not* at the end of the
- # main chef run.
- #
- # This mode of evaluation is experimental, but is believed to be a better
- # set of tradeoffs than the append-after mode, so it will likely become
- # the default in a future major release of Chef.
- #
- def self.use_inline_resources
- extend InlineResources::ClassMethods
- include InlineResources
- end
-
- # Chef::Provider::InlineResources
- # Implementation of inline resource convergence for providers. See
- # Provider.use_inline_resources for a longer explanation.
- #
- # This code is restricted to a module so that it can be selectively
- # applied to providers on an opt-in basis.
- #
- # @api private
- module InlineResources
-
- # Create a child run_context, compile the block, and converge it.
- #
- # @api private
- def compile_and_converge_action(&block)
- old_run_context = run_context
- @run_context = run_context.create_child
- return_value = instance_eval(&block)
- Chef::Runner.new(run_context).converge
- return_value
- ensure
- if run_context.resource_collection.any? { |r| r.updated? }
- new_resource.updated_by_last_action(true)
- end
- @run_context = old_run_context
- end
-
- # Class methods for InlineResources. Overrides the `action` DSL method
- # with one that enables inline resource convergence.
- #
- # @api private
- module ClassMethods
- # Defines an action method on the provider, running the block to
- # compile the resources, converging them, and then checking if any
- # were updated (and updating new-resource if so)
- def action(name, &block)
- # We need the block directly in a method so that `super` works
- define_method("compile_action_#{name}", &block)
- # We try hard to use `def` because define_method doesn't show the method name in the stack.
- begin
- class_eval <<-EOM
- def action_#{name}
- compile_and_converge_action { compile_action_#{name} }
- end
- EOM
- rescue SyntaxError
- define_method("action_#{name}") { send("compile_action_#{name}") }
- end
- end
- end
- end
-
protected
def converge_actions
@@ -421,34 +398,6 @@ class Chef
end
end
- module DeprecatedLWRPClass
- def const_missing(class_name)
- if deprecated_constants[class_name.to_sym]
- Chef.log_deprecation("Using an LWRP provider by its name (#{class_name}) directly is no longer supported in Chef 12 and will be removed. Use Chef::ProviderResolver.new(node, resource, action) instead.")
- deprecated_constants[class_name.to_sym]
- else
- raise NameError, "uninitialized constant Chef::Provider::#{class_name}"
- end
- end
-
- # @api private
- def register_deprecated_lwrp_class(provider_class, class_name)
- # Register Chef::Provider::MyProvider with deprecation warnings if you
- # try to access it
- if Chef::Provider.const_defined?(class_name, false)
- Chef::Log.warn "Chef::Provider::#{class_name} already exists! Cannot create deprecation class for #{provider_class}"
- else
- deprecated_constants[class_name.to_sym] = provider_class
- end
- end
-
- private
-
- def deprecated_constants
- @deprecated_constants ||= {}
- end
- end
- extend DeprecatedLWRPClass
end
end
diff --git a/lib/chef/provider/apt_preference.rb b/lib/chef/provider/apt_preference.rb
new file mode 100644
index 0000000000..416a1c0d1d
--- /dev/null
+++ b/lib/chef/provider/apt_preference.rb
@@ -0,0 +1,94 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: 2016-2017, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/provider"
+require "chef/dsl/declare_resource"
+require "chef/provider/noop"
+require "chef/log"
+
+class Chef
+ class Provider
+ class AptPreference < Chef::Provider
+ provides :apt_preference, platform_family: "debian"
+
+ APT_PREFERENCE_DIR = "/etc/apt/preferences.d".freeze
+
+ def load_current_resource
+ end
+
+ action :add do
+ preference = build_pref(
+ new_resource.glob || new_resource.package_name,
+ new_resource.pin,
+ new_resource.pin_priority
+ )
+
+ declare_resource(:directory, APT_PREFERENCE_DIR) do
+ mode "0755"
+ action :create
+ end
+
+ sanitized_prefname = safe_name(new_resource.package_name)
+
+ # cleanup any existing pref files w/o the sanitized name (created by old apt cookbook)
+ if (sanitized_prefname != new_resource.package_name) && ::File.exist?("#{APT_PREFERENCE_DIR}/#{new_resource.package_name}.pref")
+ logger.warn "Replacing legacy #{new_resource.package_name}.pref with #{sanitized_prefname}.pref in #{APT_PREFERENCE_DIR}"
+ declare_resource(:file, "#{APT_PREFERENCE_DIR}/#{new_resource.package_name}.pref") do
+ action :delete
+ end
+ end
+
+ # cleanup any existing pref files without the .pref extension (created by old apt cookbook)
+ if ::File.exist?("#{APT_PREFERENCE_DIR}/#{new_resource.package_name}")
+ logger.warn "Replacing legacy #{new_resource.package_name} with #{sanitized_prefname}.pref in #{APT_PREFERENCE_DIR}"
+ declare_resource(:file, "#{APT_PREFERENCE_DIR}/#{new_resource.package_name}") do
+ action :delete
+ end
+ end
+
+ declare_resource(:file, "#{APT_PREFERENCE_DIR}/#{sanitized_prefname}.pref") do
+ mode "0644"
+ content preference
+ action :create
+ end
+ end
+
+ action :remove do
+ sanitized_prefname = safe_name(new_resource.package_name)
+
+ if ::File.exist?("#{APT_PREFERENCE_DIR}/#{sanitized_prefname}.pref")
+ logger.info "Un-pinning #{sanitized_prefname} from #{APT_PREFERENCE_DIR}"
+ declare_resource(:file, "#{APT_PREFERENCE_DIR}/#{sanitized_prefname}.pref") do
+ action :delete
+ end
+ end
+ end
+
+ # Build preferences.d file contents
+ def build_pref(package_name, pin, pin_priority)
+ "Package: #{package_name}\nPin: #{pin}\nPin-Priority: #{pin_priority}\n"
+ end
+
+ def safe_name(name)
+ name.tr(".", "_").gsub("*", "wildcard")
+ end
+ end
+ end
+end
+
+Chef::Provider::Noop.provides :apt_preference
diff --git a/lib/chef/provider/apt_repository.rb b/lib/chef/provider/apt_repository.rb
index 1e7db80620..973c10e94a 100644
--- a/lib/chef/provider/apt_repository.rb
+++ b/lib/chef/provider/apt_repository.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) 2016-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,27 +25,25 @@ require "chef/provider/noop"
class Chef
class Provider
class AptRepository < Chef::Provider
- use_inline_resources
-
include Chef::Mixin::ShellOut
- provides :apt_repository do
- uses_apt?
- end
+ provides :apt_repository, platform_family: "debian"
- def whyrun_supported?
- true
- end
+ LIST_APT_KEY_FINGERPRINTS = "apt-key adv --list-public-keys --with-fingerprint --with-colons".freeze
def load_current_resource
end
action :add do
- unless new_resource.key.nil?
- if is_key_id?(new_resource.key) && !has_cookbook_file?(new_resource.key)
- install_key_from_keyserver
- else
- install_key_from_uri
+ if new_resource.key.nil?
+ logger.debug "No 'key' property specified skipping key import"
+ else
+ new_resource.key.each do |k|
+ if is_key_id?(k) && !has_cookbook_file?(k)
+ install_key_from_keyserver(k)
+ else
+ install_key_from_uri(k)
+ end
end
end
@@ -59,22 +57,18 @@ class Chef
action :nothing
end
- components = if is_ppa_url?(new_resource.uri) && new_resource.components.empty?
- "main"
- else
- new_resource.components
- end
+ cleanup_legacy_file!
repo = build_repo(
new_resource.uri,
new_resource.distribution,
- components,
+ repo_components,
new_resource.trusted,
new_resource.arch,
new_resource.deb_src
)
- declare_resource(:file, "/etc/apt/sources.list.d/#{new_resource.name}.list") do
+ declare_resource(:file, "/etc/apt/sources.list.d/#{new_resource.repo_name}.list") do
owner "root"
group "root"
mode "0644"
@@ -87,9 +81,10 @@ class Chef
end
action :remove do
- if ::File.exist?("/etc/apt/sources.list.d/#{new_resource.name}.list")
- converge_by "Removing #{new_resource.name} repository from /etc/apt/sources.list.d/" do
- declare_resource(:file, "/etc/apt/sources.list.d/#{new_resource.name}.list") do
+ cleanup_legacy_file!
+ if ::File.exist?("/etc/apt/sources.list.d/#{new_resource.repo_name}.list")
+ converge_by "Removing #{new_resource.repo_name} repository from /etc/apt/sources.list.d/" do
+ declare_resource(:file, "/etc/apt/sources.list.d/#{new_resource.repo_name}.list") do
sensitive new_resource.sensitive
action :delete
notifies :update, "apt_update[#{new_resource.name}]", :immediately if new_resource.cache_rebuild
@@ -99,94 +94,133 @@ class Chef
ignore_failure true
action :nothing
end
-
end
+ else
+ logger.trace("/etc/apt/sources.list.d/#{new_resource.repo_name}.list does not exist. Nothing to do")
end
end
- def self.uses_apt?
- ENV["PATH"] ||= ""
- paths = %w{ /bin /usr/bin /sbin /usr/sbin } + ENV["PATH"].split(::File::PATH_SEPARATOR)
- paths.any? { |path| ::File.executable?(::File.join(path, "apt-get")) }
- end
-
+ # is the provided ID a key ID from a keyserver. Looks at length and HEX only values
+ # @param [String] id the key value passed by the user that *may* be an ID
def is_key_id?(id)
id = id[2..-1] if id.start_with?("0x")
id =~ /^\h+$/ && [8, 16, 40].include?(id.length)
end
+ # run the specified command and extract the fingerprints from the output
+ # accepts a command so it can be used to extract both the current key's fingerprints
+ # and the fingerprint of the new key
+ # @param [String] cmd the command to run
+ #
+ # @return [Array] an array of fingerprints
def extract_fingerprints_from_cmd(cmd)
so = shell_out(cmd)
- so.run_command
so.stdout.split(/\n/).map do |t|
- if z = t.match(/^ +Key fingerprint = ([0-9A-F ]+)/)
+ if z = t.match(/^fpr:+([0-9A-F]+):/)
z[1].split.join
end
end.compact
end
- def key_is_valid?(cmd, key)
+ # validate the key against the apt keystore to see if that version is expired
+ # @param [String] key
+ #
+ # @return [Boolean] is the key valid or not
+ def key_is_valid?(key)
valid = true
- so = shell_out(cmd)
- so.run_command
+ so = shell_out("apt-key list")
so.stdout.split(/\n/).map do |t|
if t =~ %r{^\/#{key}.*\[expired: .*\]$}
- Chef::Log.debug "Found expired key: #{t}"
+ logger.debug "Found expired key: #{t}"
valid = false
break
end
end
- Chef::Log.debug "key #{key} #{valid ? "is valid" : "is not valid"}"
+ logger.debug "key #{key} #{valid ? "is valid" : "is not valid"}"
valid
end
+ # return the specified cookbook name or the cookbook containing the
+ # resource.
+ #
+ # @return [String] name of the cookbook
def cookbook_name
new_resource.cookbook || new_resource.cookbook_name
end
+ # determine if a cookbook file is available in the run
+ # @param [String] fn the path to the cookbook file
+ #
+ # @return [Boolean] cookbook file exists or doesn't
def has_cookbook_file?(fn)
run_context.has_cookbook_file_in_cookbook?(cookbook_name, fn)
end
+ # determine if there are any new keys by comparing the fingerprints of installed
+ # keys to those of the passed file
+ # @param [String] file the keyfile of the new repository
+ #
+ # @return [Boolean] true: no new keys in the file. false: there are new keys
def no_new_keys?(file)
- installed_keys = extract_fingerprints_from_cmd("apt-key finger")
- proposed_keys = extract_fingerprints_from_cmd("gpg --with-fingerprint #{file}")
+ # Now we are using the option --with-colons that works across old os versions
+ # as well as the latest (16.10). This for both `apt-key` and `gpg` commands
+ installed_keys = extract_fingerprints_from_cmd(LIST_APT_KEY_FINGERPRINTS)
+ proposed_keys = extract_fingerprints_from_cmd("gpg --with-fingerprint --with-colons #{file}")
(installed_keys & proposed_keys).sort == proposed_keys.sort
end
- def install_key_from_uri
- key_name = new_resource.key.split(%r{\/}).last
+ # Given the provided key URI determine what kind of chef resource we need
+ # to fetch the key
+ # @param [String] uri the uri of the gpg key (local path or http URL)
+ #
+ # @raise [Chef::Exceptions::FileNotFound] Key isn't remote or found in the current run
+ #
+ # @return [Symbol] :remote_file or :cookbook_file
+ def key_type(uri)
+ if uri.start_with?("http")
+ :remote_file
+ elsif has_cookbook_file?(uri)
+ :cookbook_file
+ else
+ raise Chef::Exceptions::FileNotFound, "Cannot locate key file: #{uri}"
+ end
+ end
+
+ # Fetch the key using either cookbook_file or remote_file, validate it,
+ # and install it with apt-key add
+ # @param [String] key the key to install
+ #
+ # @raise [RuntimeError] Invalid key which can't verify the apt repository
+ #
+ # @return [void]
+ def install_key_from_uri(key)
+ key_name = key.gsub(/[^0-9A-Za-z\-]/, "_")
cached_keyfile = ::File.join(Chef::Config[:file_cache_path], key_name)
- type = if new_resource.key.start_with?("http")
- :remote_file
- elsif has_cookbook_file?(new_resource.key)
- :cookbook_file
- else
- raise Chef::Exceptions::FileNotFound, "Cannot locate key file"
- end
- declare_resource(type, cached_keyfile) do
- source new_resource.key
+ declare_resource(key_type(key), cached_keyfile) do
+ source key
mode "0644"
sensitive new_resource.sensitive
action :create
+ verify "gpg %{path}"
end
- raise "The key #{cached_keyfile} is invalid and cannot be used to verify an apt repository." unless key_is_valid?("gpg #{cached_keyfile}", "")
-
declare_resource(:execute, "apt-key add #{cached_keyfile}") do
sensitive new_resource.sensitive
action :run
- not_if do
- no_new_keys?(cached_keyfile)
- end
+ not_if { no_new_keys?(cached_keyfile) }
notifies :run, "execute[apt-cache gencaches]", :immediately
end
end
- def install_key_from_keyserver(key = new_resource.key, keyserver = new_resource.keyserver)
+ # build the apt-key command to install the keyserver
+ # @param [String] key the key to install
+ # @param [String] keyserver the key server to use
+ #
+ # @return [String] the full apt-key command to run
+ def keyserver_install_cmd(key, keyserver)
cmd = "apt-key adv --recv"
cmd << " --keyserver-options http-proxy=#{new_resource.key_proxy}" if new_resource.key_proxy
cmd << " --keyserver "
@@ -197,22 +231,37 @@ class Chef
end
cmd << " #{key}"
+ cmd
+ end
+ # @param [String] key
+ # @param [String] keyserver
+ #
+ # @raise [RuntimeError] Invalid key which can't verify the apt repository
+ #
+ # @return [void]
+ def install_key_from_keyserver(key, keyserver = new_resource.keyserver)
declare_resource(:execute, "install-key #{key}") do
- command cmd
+ command keyserver_install_cmd(key, keyserver)
sensitive new_resource.sensitive
not_if do
- present = extract_fingerprints_from_cmd("apt-key finger").any? do |fp|
+ present = extract_fingerprints_from_cmd(LIST_APT_KEY_FINGERPRINTS).any? do |fp|
fp.end_with? key.upcase
end
- present && key_is_valid?("apt-key list", key.upcase)
+ present && key_is_valid?(key.upcase)
end
notifies :run, "execute[apt-cache gencaches]", :immediately
end
- raise "The key #{key} is invalid and cannot be used to verify an apt repository." unless key_is_valid?("apt-key list", key.upcase)
+ raise "The key #{key} is invalid and cannot be used to verify an apt repository." unless key_is_valid?(key.upcase)
end
+ # @param [String] owner
+ # @param [String] repo
+ #
+ # @raise [RuntimeError] Could not access the Launchpad PPA API
+ #
+ # @return [void]
def install_ppa_key(owner, repo)
url = "https://launchpad.net/api/1.0/~#{owner}/+archive/#{repo}"
key_id = Chef::HTTP::Simple.new(url).get("signing_key_fingerprint").delete('"')
@@ -221,12 +270,33 @@ class Chef
raise "Could not access Launchpad ppa API: #{e.message}"
end
+ # determine if the repository URL is a PPA
+ # @param [String] url the url of the repository
+ #
+ # @return [Boolean] is the repo URL a PPA
def is_ppa_url?(url)
url.start_with?("ppa:")
end
+ # determine the repository's components:
+ # - "components" property if defined
+ # - "main" if "components" not defined and the repo is a PPA URL
+ # - otherwise nothing
+ #
+ # @return [String] the repository component
+ def repo_components
+ if is_ppa_url?(new_resource.uri) && new_resource.components.empty?
+ "main"
+ else
+ new_resource.components
+ end
+ end
+
+ # given a PPA return a PPA URL in http://ppa.launchpad.net format
+ # @param [String] ppa the ppa URL
+ #
+ # @return [String] full PPA URL
def make_ppa_url(ppa)
- return unless is_ppa_url?(ppa)
owner, repo = ppa[4..-1].split("/")
repo ||= "ppa"
@@ -234,6 +304,14 @@ class Chef
"http://ppa.launchpad.net/#{owner}/#{repo}/ubuntu"
end
+ # build complete repo text that will be written to the config
+ # @param [String] uri
+ # @param [Array] components
+ # @param [Boolean] trusted
+ # @param [String] arch
+ # @param [Boolean] add_src
+ #
+ # @return [String] complete repo config text
def build_repo(uri, distribution, components, trusted, arch, add_src = false)
uri = make_ppa_url(uri) if is_ppa_url?(uri)
@@ -250,8 +328,25 @@ class Chef
repo << "deb-src #{info}\n" if add_src
repo
end
+
+ # clean up a potentially legacy file from before we fixed the usage of
+ # new_resource.name vs. new_resource.repo_name. We might have the
+ # name.list file hanging around and need to clean it up.
+ #
+ # @return [void]
+ def cleanup_legacy_file!
+ legacy_path = "/etc/apt/sources.list.d/#{new_resource.name}.list"
+ if new_resource.name != new_resource.repo_name && ::File.exist?(legacy_path)
+ converge_by "Cleaning up legacy #{legacy_path} repo file" do
+ declare_resource(:file, legacy_path) do
+ action :delete
+ # Not triggering an update since it isn't super likely to be needed.
+ end
+ end
+ end
+ end
end
end
end
-Chef::Provider::Noop.provides :apt_resource
+Chef::Provider::Noop.provides :apt_repository
diff --git a/lib/chef/provider/apt_update.rb b/lib/chef/provider/apt_update.rb
index d2dd5cfb14..9d794abcf0 100644
--- a/lib/chef/provider/apt_update.rb
+++ b/lib/chef/provider/apt_update.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) 2016-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +16,18 @@
# limitations under the License.
#
-require "chef/resource"
+require "chef/provider"
+require "chef/provider/noop"
require "chef/dsl/declare_resource"
class Chef
class Provider
class AptUpdate < Chef::Provider
- use_inline_resources
-
- provides :apt_update, os: "linux"
+ provides :apt_update, platform_family: "debian"
APT_CONF_DIR = "/etc/apt/apt.conf.d"
STAMP_DIR = "/var/lib/apt/periodic"
- def whyrun_supported?
- true
- end
-
def load_current_resource
end
@@ -68,7 +63,7 @@ class Chef
end
declare_resource(:file, "#{APT_CONF_DIR}/15update-stamp") do
- content "APT::Update::Post-Invoke-Success {\"touch #{STAMP_DIR}/update-success-stamp 2>/dev/null || true\";};"
+ content "APT::Update::Post-Invoke-Success {\"touch #{STAMP_DIR}/update-success-stamp 2>/dev/null || true\";};\n"
action :create_if_missing
end
@@ -78,3 +73,5 @@ class Chef
end
end
end
+
+Chef::Provider::Noop.provides :apt_update
diff --git a/lib/chef/provider/batch.rb b/lib/chef/provider/batch.rb
index bb294afd3f..dae1513a51 100644
--- a/lib/chef/provider/batch.rb
+++ b/lib/chef/provider/batch.rb
@@ -22,14 +22,14 @@ class Chef
class Provider
class Batch < Chef::Provider::WindowsScript
- provides :batch, os: "windows"
+ provides :batch
def initialize(new_resource, run_context)
super(new_resource, run_context, ".bat")
end
def command
- basepath = is_forced_32bit ? wow64_directory : run_context.node.kernel.os_info.system_directory
+ basepath = is_forced_32bit ? wow64_directory : run_context.node["kernel"]["os_info"]["system_directory"]
interpreter_path = Chef::Util::PathHelper.join(basepath, interpreter)
@@ -37,7 +37,7 @@ class Chef
end
def flags
- @new_resource.flags.nil? ? "/c" : new_resource.flags + " /c"
+ new_resource.flags.nil? ? "/c" : new_resource.flags + " /c"
end
end
diff --git a/lib/chef/provider/cookbook_file.rb b/lib/chef/provider/cookbook_file.rb
index 3ca0bbd47a..92383fd5fa 100644
--- a/lib/chef/provider/cookbook_file.rb
+++ b/lib/chef/provider/cookbook_file.rb
@@ -17,8 +17,6 @@
#
require "chef/provider/file"
-require "chef/deprecation/provider/cookbook_file"
-require "chef/deprecation/warnings"
class Chef
class Provider
@@ -26,25 +24,21 @@ class Chef
provides :cookbook_file
- extend Chef::Deprecation::Warnings
- include Chef::Deprecation::Provider::CookbookFile
- add_deprecation_warnings_for(Chef::Deprecation::Provider::CookbookFile.instance_methods)
-
def initialize(new_resource, run_context)
@content_class = Chef::Provider::CookbookFile::Content
super
end
def load_current_resource
- @current_resource = Chef::Resource::CookbookFile.new(@new_resource.name)
+ @current_resource = Chef::Resource::CookbookFile.new(new_resource.name)
super
end
private
def managing_content?
- return true if @new_resource.checksum
- return true if !@new_resource.source.nil? && @action != :create_if_missing
+ return true if new_resource.checksum
+ return true if !new_resource.source.nil? && @action != :create_if_missing
false
end
diff --git a/lib/chef/provider/cookbook_file/content.rb b/lib/chef/provider/cookbook_file/content.rb
index 1d24dee3e7..82e2cf4390 100644
--- a/lib/chef/provider/cookbook_file/content.rb
+++ b/lib/chef/provider/cookbook_file/content.rb
@@ -34,7 +34,7 @@ class Chef
else
tempfile = Chef::FileContentManagement::Tempfile.new(@new_resource).tempfile
tempfile.close
- Chef::Log.debug("#{@new_resource} staging #{file_cache_location} to #{tempfile.path}")
+ logger.trace("#{@new_resource} staging #{file_cache_location} to #{tempfile.path}")
FileUtils.cp(file_cache_location, tempfile.path)
tempfile
end
diff --git a/lib/chef/provider/cron.rb b/lib/chef/provider/cron.rb
index 36b67ab6a5..70edd89636 100644
--- a/lib/chef/provider/cron.rb
+++ b/lib/chef/provider/cron.rb
@@ -17,13 +17,11 @@
#
require "chef/log"
-require "chef/mixin/command"
require "chef/provider"
class Chef
class Provider
class Cron < Chef::Provider
- include Chef::Mixin::Command
provides :cron, os: ["!aix", "!solaris2"]
@@ -42,21 +40,17 @@ class Chef
end
attr_accessor :cron_exists, :cron_empty
- def whyrun_supported?
- true
- end
-
def load_current_resource
crontab_lines = []
- @current_resource = Chef::Resource::Cron.new(@new_resource.name)
- @current_resource.user(@new_resource.user)
+ @current_resource = Chef::Resource::Cron.new(new_resource.name)
+ current_resource.user(new_resource.user)
@cron_exists = false
if crontab = read_crontab
cron_found = false
crontab.each_line do |line|
case line.chomp
- when "# Chef Name: #{@new_resource.name}"
- Chef::Log.debug("Found cron '#{@new_resource.name}'")
+ when "# Chef Name: #{new_resource.name}"
+ logger.trace("Found cron '#{new_resource.name}'")
cron_found = true
@cron_exists = true
next
@@ -65,18 +59,18 @@ class Chef
next
when SPECIAL_PATTERN
if cron_found
- @current_resource.time($2.to_sym)
- @current_resource.command($3)
+ current_resource.time($2.to_sym)
+ current_resource.command($3)
cron_found = false
end
when CRON_PATTERN
if cron_found
- @current_resource.minute($1)
- @current_resource.hour($2)
- @current_resource.day($3)
- @current_resource.month($4)
- @current_resource.weekday($5)
- @current_resource.command($6)
+ current_resource.minute($1)
+ current_resource.hour($2)
+ current_resource.day($3)
+ current_resource.month($4)
+ current_resource.weekday($5)
+ current_resource.command($6)
cron_found = false
end
next
@@ -85,36 +79,36 @@ class Chef
next
end
end
- Chef::Log.debug("Cron '#{@new_resource.name}' not found") unless @cron_exists
+ logger.trace("Cron '#{new_resource.name}' not found") unless @cron_exists
else
- Chef::Log.debug("Cron empty for '#{@new_resource.user}'")
+ logger.trace("Cron empty for '#{new_resource.user}'")
@cron_empty = true
end
- @current_resource
+ current_resource
end
def cron_different?
CRON_ATTRIBUTES.any? do |cron_var|
- @new_resource.send(cron_var) != @current_resource.send(cron_var)
+ new_resource.send(cron_var) != current_resource.send(cron_var)
end
end
def action_create
- crontab = String.new
- newcron = String.new
+ crontab = ""
+ newcron = ""
cron_found = false
newcron = get_crontab_entry
if @cron_exists
unless cron_different?
- Chef::Log.debug("Skipping existing cron entry '#{@new_resource.name}'")
+ logger.trace("Skipping existing cron entry '#{new_resource.name}'")
return
end
read_crontab.each_line do |line|
case line.chomp
- when "# Chef Name: #{@new_resource.name}"
+ when "# Chef Name: #{new_resource.name}"
cron_found = true
next
when ENV_PATTERN
@@ -144,29 +138,29 @@ class Chef
# Handle edge case where the Chef comment is the last line in the current crontab
crontab << newcron if cron_found
- converge_by("update crontab entry for #{@new_resource}") do
+ converge_by("update crontab entry for #{new_resource}") do
write_crontab crontab
- Chef::Log.info("#{@new_resource} updated crontab entry")
+ logger.info("#{new_resource} updated crontab entry")
end
else
crontab = read_crontab unless @cron_empty
crontab << newcron
- converge_by("add crontab entry for #{@new_resource}") do
+ converge_by("add crontab entry for #{new_resource}") do
write_crontab crontab
- Chef::Log.info("#{@new_resource} added crontab entry")
+ logger.info("#{new_resource} added crontab entry")
end
end
end
def action_delete
if @cron_exists
- crontab = String.new
+ crontab = ""
cron_found = false
read_crontab.each_line do |line|
case line.chomp
- when "# Chef Name: #{@new_resource.name}"
+ when "# Chef Name: #{new_resource.name}"
cron_found = true
next
when ENV_PATTERN
@@ -187,11 +181,10 @@ class Chef
end
crontab << line
end
- description = cron_found ? "remove #{@new_resource.name} from crontab" :
- "save unmodified crontab"
+ description = cron_found ? "remove #{new_resource.name} from crontab" : "save unmodified crontab"
converge_by(description) do
write_crontab crontab
- Chef::Log.info("#{@new_resource} deleted crontab entry")
+ logger.info("#{new_resource} deleted crontab entry")
end
end
end
@@ -200,60 +193,48 @@ class Chef
def set_environment_var(attr_name, attr_value)
if %w{MAILTO PATH SHELL HOME}.include?(attr_name)
- @current_resource.send(attr_name.downcase.to_sym, attr_value)
+ current_resource.send(attr_name.downcase.to_sym, attr_value.gsub(/^"|"$/, ""))
else
- @current_resource.environment(@current_resource.environment.merge(attr_name => attr_value))
+ current_resource.environment(current_resource.environment.merge(attr_name => attr_value))
end
end
def read_crontab
- crontab = nil
- status = popen4("crontab -l -u #{@new_resource.user}") do |pid, stdin, stdout, stderr|
- crontab = stdout.read
- end
- if status.exitstatus > 1
- raise Chef::Exceptions::Cron, "Error determining state of #{@new_resource.name}, exit: #{status.exitstatus}"
- end
- crontab
+ so = shell_out!("crontab -l -u #{new_resource.user}", returns: [0, 1])
+ return nil if so.exitstatus == 1
+ so.stdout
+ rescue => e
+ raise Chef::Exceptions::Cron, "Error determining state of #{new_resource.name}, error: #{e}"
end
def write_crontab(crontab)
write_exception = false
- status = popen4("crontab -u #{@new_resource.user} -", :waitlast => true) do |pid, stdin, stdout, stderr|
- begin
- stdin.write crontab
- rescue Errno::EPIPE => e
- # popen4 could yield while child has already died.
- write_exception = true
- Chef::Log.debug("#{e.message}")
- end
- end
- if status.exitstatus > 0 || write_exception
- raise Chef::Exceptions::Cron, "Error updating state of #{@new_resource.name}, exit: #{status.exitstatus}"
- end
+ so = shell_out!("crontab -u #{new_resource.user} -", input: crontab)
+ rescue => e
+ raise Chef::Exceptions::Cron, "Error updating state of #{new_resource.name}, error: #{e}"
end
def get_crontab_entry
newcron = ""
newcron << "# Chef Name: #{new_resource.name}\n"
[ :mailto, :path, :shell, :home ].each do |v|
- newcron << "#{v.to_s.upcase}=#{@new_resource.send(v)}\n" if @new_resource.send(v)
+ newcron << "#{v.to_s.upcase}=\"#{new_resource.send(v)}\"\n" if new_resource.send(v)
end
- @new_resource.environment.each do |name, value|
+ new_resource.environment.each do |name, value|
newcron << "#{name}=#{value}\n"
end
- if @new_resource.time
- newcron << "@#{@new_resource.time} #{@new_resource.command}\n"
+ if new_resource.time
+ newcron << "@#{new_resource.time} #{new_resource.command}\n"
else
- newcron << "#{@new_resource.minute} #{@new_resource.hour} #{@new_resource.day} #{@new_resource.month} #{@new_resource.weekday} #{@new_resource.command}\n"
+ newcron << "#{new_resource.minute} #{new_resource.hour} #{new_resource.day} #{new_resource.month} #{new_resource.weekday} #{new_resource.command}\n"
end
newcron
end
def weekday_in_crontab
- weekday_in_crontab = WEEKDAY_SYMBOLS.index(@new_resource.weekday)
+ weekday_in_crontab = WEEKDAY_SYMBOLS.index(new_resource.weekday)
if weekday_in_crontab.nil?
- @new_resource.weekday
+ new_resource.weekday
else
weekday_in_crontab.to_s
end
diff --git a/lib/chef/provider/cron/unix.rb b/lib/chef/provider/cron/unix.rb
index 108e73c9d3..15195dbb88 100644
--- a/lib/chef/provider/cron/unix.rb
+++ b/lib/chef/provider/cron/unix.rb
@@ -36,7 +36,7 @@ class Chef
crontab = shell_out("/usr/bin/crontab -l", :user => @new_resource.user)
status = crontab.status.exitstatus
- Chef::Log.debug crontab.format_for_exception if status > 0
+ logger.trace crontab.format_for_exception if status > 0
if status > 1
raise Chef::Exceptions::Cron, "Error determining state of #{@new_resource.name}, exit: #{status}"
@@ -62,12 +62,12 @@ class Chef
exit_status = 1
end
rescue Chef::Exceptions::Exec => e
- Chef::Log.debug(e.message)
+ logger.trace(e.message)
exit_status = 1
error_message = e.message
rescue ArgumentError => e
# usually raised on invalid user.
- Chef::Log.debug(e.message)
+ logger.trace(e.message)
exit_status = 1
error_message = e.message
end
diff --git a/lib/chef/provider/deploy.rb b/lib/chef/provider/deploy.rb
deleted file mode 100644
index 4c758033ac..0000000000
--- a/lib/chef/provider/deploy.rb
+++ /dev/null
@@ -1,476 +0,0 @@
-#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/mixin/command"
-require "chef/mixin/from_file"
-require "chef/provider/git"
-require "chef/provider/subversion"
-require "chef/dsl/recipe"
-require "chef/util/path_helper"
-
-class Chef
- class Provider
- class Deploy < Chef::Provider
-
- include Chef::DSL::Recipe
- include Chef::Mixin::FromFile
- include Chef::Mixin::Command
-
- attr_reader :scm_provider, :release_path, :shared_path, :previous_release_path
-
- def initialize(new_resource, run_context)
- super(new_resource, run_context)
-
- # will resolve to either git or svn based on resource attributes,
- # and will create a resource corresponding to that provider
- @scm_provider = new_resource.scm_provider.new(new_resource, run_context)
-
- # @configuration is not used by Deploy, it is only for backwards compat with
- # chef-deploy or capistrano hooks that might use it to get environment information
- @configuration = @new_resource.to_hash
- @configuration[:environment] = @configuration[:environment] && @configuration[:environment]["RAILS_ENV"]
- end
-
- def whyrun_supported?
- true
- end
-
- def load_current_resource
- @scm_provider.load_current_resource
- @release_path = @new_resource.deploy_to + "/releases/#{release_slug}"
- @shared_path = @new_resource.shared_path
- end
-
- def sudo(command, &block)
- execute(command, &block)
- end
-
- def run(command, &block)
- exec = execute(command, &block)
- exec.user(@new_resource.user) if @new_resource.user
- exec.group(@new_resource.group) if @new_resource.group
- exec.cwd(release_path) unless exec.cwd
- exec.environment(@new_resource.environment) unless exec.environment
- converge_by("execute #{command}") do
- exec
- end
- end
-
- def define_resource_requirements
- requirements.assert(:rollback) do |a|
- a.assertion { all_releases[-2] }
- a.failure_message(RuntimeError, "There is no release to rollback to!")
- #There is no reason to assume 2 deployments in a single chef run, hence fails in whyrun.
- end
-
- [ @new_resource.before_migrate, @new_resource.before_symlink,
- @new_resource.before_restart, @new_resource.after_restart ].each do |script|
- requirements.assert(:deploy, :force_deploy) do |a|
- callback_file = "#{release_path}/#{script}"
- a.assertion do
- if script && script.class == String
- ::File.exist?(callback_file)
- else
- true
- end
- end
- a.failure_message(RuntimeError, "Can't find your callback file #{callback_file}")
- a.whyrun("Would assume callback file #{callback_file} included in release")
- end
- end
- end
-
- def action_deploy
- save_release_state
- if deployed?(release_path )
- if current_release?(release_path )
- Chef::Log.debug("#{@new_resource} is the latest version")
- else
- rollback_to release_path
- end
- else
-
- with_rollback_on_error do
- deploy
- end
- end
- end
-
- def action_force_deploy
- if deployed?(release_path)
- converge_by("delete deployed app at #{release_path} prior to force-deploy") do
- Chef::Log.info("Already deployed app at #{release_path}, forcing.")
- FileUtils.rm_rf(release_path)
- Chef::Log.info("#{@new_resource} forcing deploy of already deployed app at #{release_path}")
- end
- end
-
- # Alternatives:
- # * Move release_path directory before deploy and move it back when error occurs
- # * Rollback to previous commit
- # * Do nothing - because deploy is force, it will be retried in short time
- # Because last is simplest, keep it
- deploy
- end
-
- def action_rollback
- rollback_to all_releases[-2]
- end
-
- def rollback_to(target_release_path)
- @release_path = target_release_path
-
- rp_index = all_releases.index(release_path)
- releases_to_nuke = all_releases[(rp_index + 1)..-1]
-
- rollback
-
- releases_to_nuke.each do |i|
- converge_by("roll back by removing release #{i}") do
- Chef::Log.info "#{@new_resource} removing release: #{i}"
- FileUtils.rm_rf i
- end
- release_deleted(i)
- end
- end
-
- def deploy
- verify_directories_exist
- update_cached_repo # no converge-by - scm provider will dothis
- enforce_ownership
- copy_cached_repo
- install_gems
- enforce_ownership
- callback(:before_migrate, @new_resource.before_migrate)
- migrate
- callback(:before_symlink, @new_resource.before_symlink)
- symlink
- callback(:before_restart, @new_resource.before_restart)
- restart
- callback(:after_restart, @new_resource.after_restart)
- cleanup!
- Chef::Log.info "#{@new_resource} deployed to #{@new_resource.deploy_to}"
- end
-
- def rollback
- Chef::Log.info "#{@new_resource} rolling back to previous release #{release_path}"
- symlink
- Chef::Log.info "#{@new_resource} restarting with previous release"
- restart
- end
-
- def callback(what, callback_code = nil)
- @collection = Chef::ResourceCollection.new
- case callback_code
- when Proc
- Chef::Log.info "#{@new_resource} running callback #{what}"
- recipe_eval(&callback_code)
- when String
- run_callback_from_file("#{release_path}/#{callback_code}")
- when nil
- run_callback_from_file("#{release_path}/deploy/#{what}.rb")
- end
- end
-
- def migrate
- run_symlinks_before_migrate
-
- if @new_resource.migrate
- enforce_ownership
-
- environment = @new_resource.environment
- env_info = environment && environment.map do |key_and_val|
- "#{key_and_val.first}='#{key_and_val.last}'"
- end.join(" ")
-
- converge_by("execute migration command #{@new_resource.migration_command}") do
- Chef::Log.info "#{@new_resource} migrating #{@new_resource.user} with environment #{env_info}"
- shell_out!(@new_resource.migration_command, run_options(:cwd => release_path, :log_level => :info))
- end
- end
- end
-
- def symlink
- purge_tempfiles_from_current_release
- link_tempfiles_to_current_release
- link_current_release_to_production
- Chef::Log.info "#{@new_resource} updated symlinks"
- end
-
- def restart
- if restart_cmd = @new_resource.restart_command
- if restart_cmd.kind_of?(Proc)
- Chef::Log.info("#{@new_resource} restarting app with embedded recipe")
- recipe_eval(&restart_cmd)
- else
- converge_by("restart app using command #{@new_resource.restart_command}") do
- Chef::Log.info("#{@new_resource} restarting app")
- shell_out!(@new_resource.restart_command, run_options(:cwd => @new_resource.current_path))
- end
- end
- end
- end
-
- def cleanup!
- converge_by("update release history data") do
- release_created(release_path)
- end
-
- chop = -1 - @new_resource.keep_releases
- all_releases[0..chop].each do |old_release|
- converge_by("remove old release #{old_release}") do
- Chef::Log.info "#{@new_resource} removing old release #{old_release}"
- FileUtils.rm_rf(old_release)
- end
- release_deleted(old_release)
- end
- end
-
- def all_releases
- Dir.glob(Chef::Util::PathHelper.escape_glob_dir(@new_resource.deploy_to) + "/releases/*").sort
- end
-
- def update_cached_repo
- if @new_resource.svn_force_export
- # TODO assertion, non-recoverable - @scm_provider must be svn if force_export?
- svn_force_export
- else
- run_scm_sync
- end
- end
-
- def run_scm_sync
- @scm_provider.run_action(:sync)
- end
-
- def svn_force_export
- Chef::Log.info "#{@new_resource} exporting source repository"
- @scm_provider.run_action(:force_export)
- end
-
- def copy_cached_repo
- target_dir_path = @new_resource.deploy_to + "/releases"
- converge_by("deploy from repo to #{target_dir_path} ") do
- FileUtils.rm_rf(release_path) if ::File.exist?(release_path)
- FileUtils.mkdir_p(target_dir_path)
- FileUtils.cp_r(::File.join(@new_resource.destination, "."), release_path, :preserve => true)
- Chef::Log.info "#{@new_resource} copied the cached checkout to #{release_path}"
- end
- end
-
- def enforce_ownership
- converge_by("force ownership of #{@new_resource.deploy_to} to #{@new_resource.group}:#{@new_resource.user}") do
- FileUtils.chown_R(@new_resource.user, @new_resource.group, @new_resource.deploy_to, :force => true)
- Chef::Log.info("#{@new_resource} set user to #{@new_resource.user}") if @new_resource.user
- Chef::Log.info("#{@new_resource} set group to #{@new_resource.group}") if @new_resource.group
- end
- end
-
- def verify_directories_exist
- create_dir_unless_exists(@new_resource.deploy_to)
- create_dir_unless_exists(@new_resource.shared_path)
- end
-
- def link_current_release_to_production
- converge_by(["remove existing link at #{@new_resource.current_path}",
- "link release #{release_path} into production at #{@new_resource.current_path}"]) do
- FileUtils.rm_f(@new_resource.current_path)
- begin
- FileUtils.ln_sf(release_path, @new_resource.current_path)
- rescue => e
- raise Chef::Exceptions::FileNotFound.new("Cannot symlink current release to production: #{e.message}")
- end
- Chef::Log.info "#{@new_resource} linked release #{release_path} into production at #{@new_resource.current_path}"
- end
- enforce_ownership
- end
-
- def run_symlinks_before_migrate
- links_info = @new_resource.symlink_before_migrate.map { |src, dst| "#{src} => #{dst}" }.join(", ")
- converge_by("make pre-migration symlinks: #{links_info}") do
- @new_resource.symlink_before_migrate.each do |src, dest|
- begin
- FileUtils.ln_sf(@new_resource.shared_path + "/#{src}", release_path + "/#{dest}")
- rescue => e
- raise Chef::Exceptions::FileNotFound.new("Cannot symlink #{@new_resource.shared_path}/#{src} to #{release_path}/#{dest} before migrate: #{e.message}")
- end
- end
- Chef::Log.info "#{@new_resource} made pre-migration symlinks"
- end
- end
-
- def link_tempfiles_to_current_release
- dirs_info = @new_resource.create_dirs_before_symlink.join(",")
- @new_resource.create_dirs_before_symlink.each do |dir|
- create_dir_unless_exists(release_path + "/#{dir}")
- end
- Chef::Log.info("#{@new_resource} created directories before symlinking: #{dirs_info}")
-
- links_info = @new_resource.symlinks.map { |src, dst| "#{src} => #{dst}" }.join(", ")
- converge_by("link shared paths into current release: #{links_info}") do
- @new_resource.symlinks.each do |src, dest|
- begin
- FileUtils.ln_sf(::File.join(@new_resource.shared_path, src), ::File.join(release_path, dest))
- rescue => e
- raise Chef::Exceptions::FileNotFound.new("Cannot symlink shared data #{::File.join(@new_resource.shared_path, src)} to #{::File.join(release_path, dest)}: #{e.message}")
- end
- end
- Chef::Log.info("#{@new_resource} linked shared paths into current release: #{links_info}")
- end
- run_symlinks_before_migrate
- enforce_ownership
- end
-
- def create_dirs_before_symlink
- end
-
- def purge_tempfiles_from_current_release
- log_info = @new_resource.purge_before_symlink.join(", ")
- converge_by("purge directories in checkout #{log_info}") do
- @new_resource.purge_before_symlink.each { |dir| FileUtils.rm_rf(release_path + "/#{dir}") }
- Chef::Log.info("#{@new_resource} purged directories in checkout #{log_info}")
- end
- end
-
- protected
-
- # Internal callback, called after copy_cached_repo.
- # Override if you need to keep state externally.
- # Note that YOU are responsible for implementing whyrun-friendly behavior
- # in any actions you take in this callback.
- def release_created(release_path)
- end
-
- # Note that YOU are responsible for using appropriate whyrun nomenclature
- # Override if you need to keep state externally.
- # Note that YOU are responsible for implementing whyrun-friendly behavior
- # in any actions you take in this callback.
- def release_deleted(release_path)
- end
-
- def release_slug
- raise Chef::Exceptions::Override, "You must override release_slug in #{self}"
- end
-
- def install_gems
- gem_resource_collection_runner.converge
- end
-
- def gem_resource_collection_runner
- child_context = run_context.create_child
- gem_packages.each { |rbgem| child_context.resource_collection.insert(rbgem) }
- Chef::Runner.new(child_context)
- end
-
- def gem_packages
- return [] unless ::File.exist?("#{release_path}/gems.yml")
- gems = YAML.load(IO.read("#{release_path}/gems.yml"))
-
- gems.map do |g|
- r = Chef::Resource::GemPackage.new(g[:name], run_context)
- r.version g[:version]
- r.action :install
- r.source "http://gems.github.com"
- r
- end
- end
-
- def run_options(run_opts = {})
- run_opts[:user] = @new_resource.user if @new_resource.user
- run_opts[:group] = @new_resource.group if @new_resource.group
- run_opts[:environment] = @new_resource.environment if @new_resource.environment
- run_opts[:log_tag] = @new_resource.to_s
- run_opts[:log_level] ||= :debug
- if run_opts[:log_level] == :info
- if STDOUT.tty? && !Chef::Config[:daemon] && Chef::Log.info?
- run_opts[:live_stream] = STDOUT
- end
- end
- run_opts
- end
-
- def run_callback_from_file(callback_file)
- Chef::Log.info "#{@new_resource} queueing checkdeploy hook #{callback_file}"
- recipe_eval do
- Dir.chdir(release_path) do
- from_file(callback_file) if ::File.exist?(callback_file)
- end
- end
- end
-
- def create_dir_unless_exists(dir)
- if ::File.directory?(dir)
- Chef::Log.debug "#{@new_resource} not creating #{dir} because it already exists"
- return false
- end
- converge_by("create new directory #{dir}") do
- begin
- FileUtils.mkdir_p(dir)
- Chef::Log.debug "#{@new_resource} created directory #{dir}"
- if @new_resource.user
- FileUtils.chown(@new_resource.user, nil, dir)
- Chef::Log.debug("#{@new_resource} set user to #{@new_resource.user} for #{dir}")
- end
- if @new_resource.group
- FileUtils.chown(nil, @new_resource.group, dir)
- Chef::Log.debug("#{@new_resource} set group to #{@new_resource.group} for #{dir}")
- end
- rescue => e
- raise Chef::Exceptions::FileNotFound.new("Cannot create directory #{dir}: #{e.message}")
- end
- end
- end
-
- def with_rollback_on_error
- yield
- rescue ::Exception => e
- if @new_resource.rollback_on_error
- Chef::Log.warn "Error on deploying #{release_path}: #{e.message}"
- failed_release = release_path
-
- if previous_release_path
- @release_path = previous_release_path
- rollback
- end
- converge_by("remove failed deploy #{failed_release}") do
- Chef::Log.info "Removing failed deploy #{failed_release}"
- FileUtils.rm_rf failed_release
- end
- release_deleted(failed_release)
- end
-
- raise
- end
-
- def save_release_state
- if ::File.exists?(@new_resource.current_path)
- release = ::File.readlink(@new_resource.current_path)
- @previous_release_path = release if ::File.exists?(release)
- end
- end
-
- def deployed?(release)
- all_releases.include?(release)
- end
-
- def current_release?(release)
- @previous_release_path == release
- end
- end
- end
-end
diff --git a/lib/chef/provider/deploy/revision.rb b/lib/chef/provider/deploy/revision.rb
deleted file mode 100644
index f61e439486..0000000000
--- a/lib/chef/provider/deploy/revision.rb
+++ /dev/null
@@ -1,109 +0,0 @@
-#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Author:: Tim Hinderliter (<tim@chef.io>)
-# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2009-2016, Daniel DeLeo
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/provider"
-require "chef/provider/deploy"
-require "chef/json_compat"
-
-class Chef
- class Provider
- class Deploy
- class Revision < Chef::Provider::Deploy
- provides :deploy_revision
- provides :deploy_branch
-
- def all_releases
- sorted_releases
- end
-
- def action_deploy
- validate_release_history!
- super
- end
-
- def cleanup!
- super
-
- known_releases = sorted_releases
-
- Dir["#{Chef::Util::PathHelper.escape_glob_dir(new_resource.deploy_to)}/releases/*"].each do |release_dir|
- unless known_releases.include?(release_dir)
- converge_by("Remove unknown release in #{release_dir}") do
- FileUtils.rm_rf(release_dir)
- end
- end
- end
- end
-
- protected
-
- def release_created(release)
- sorted_releases { |r| r.delete(release); r << release }
- end
-
- def release_deleted(release)
- sorted_releases { |r| r.delete(release) }
- end
-
- def release_slug
- scm_provider.revision_slug
- end
-
- private
-
- def sorted_releases
- cache = load_cache
- if block_given?
- yield cache
- save_cache(cache)
- end
- cache
- end
-
- def validate_release_history!
- sorted_releases do |release_list|
- release_list.each do |path|
- release_list.delete(path) unless ::File.exist?(path)
- end
- end
- end
-
- def sorted_releases_from_filesystem
- Dir.glob(Chef::Util::PathHelper.escape_glob_dir(new_resource.deploy_to) + "/releases/*").sort_by { |d| ::File.ctime(d) }
- end
-
- def load_cache
- begin
- Chef::JSONCompat.parse(Chef::FileCache.load("revision-deploys/#{new_resource.name}"))
- rescue Chef::Exceptions::FileNotFound
- sorted_releases_from_filesystem
- end
- end
-
- def save_cache(cache)
- Chef::FileCache.store("revision-deploys/#{new_resource.name}", Chef::JSONCompat.to_json(cache))
- cache
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/provider/directory.rb b/lib/chef/provider/directory.rb
index 7cc05259b6..3e816d5a06 100644
--- a/lib/chef/provider/directory.rb
+++ b/lib/chef/provider/directory.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,17 +29,13 @@ class Chef
provides :directory
- def whyrun_supported?
- true
- end
-
def load_current_resource
- @current_resource = Chef::Resource::Directory.new(@new_resource.name)
- @current_resource.path(@new_resource.path)
- if ::File.exists?(@current_resource.path) && @action != :create_if_missing
- load_resource_attributes_from_file(@current_resource)
+ @current_resource = Chef::Resource::Directory.new(new_resource.name)
+ current_resource.path(new_resource.path)
+ if ::File.exists?(current_resource.path) && @action != :create_if_missing
+ load_resource_attributes_from_file(current_resource)
end
- @current_resource
+ current_resource
end
def define_resource_requirements
@@ -49,9 +45,9 @@ class Chef
requirements.assert(:create) do |a|
# Make sure the parent dir exists, or else fail.
# for why run, print a message explaining the potential error.
- parent_directory = ::File.dirname(@new_resource.path)
+ parent_directory = ::File.dirname(new_resource.path)
a.assertion do
- if @new_resource.recursive
+ if new_resource.recursive
does_parent_exist = lambda do |base_dir|
base_dir = ::File.dirname(base_dir)
if ::File.exist?(base_dir)
@@ -60,20 +56,20 @@ class Chef
does_parent_exist.call(base_dir)
end
end
- does_parent_exist.call(@new_resource.path)
+ does_parent_exist.call(new_resource.path)
else
::File.directory?(parent_directory)
end
end
- a.failure_message(Chef::Exceptions::EnclosingDirectoryDoesNotExist, "Parent directory #{parent_directory} does not exist, cannot create #{@new_resource.path}")
+ a.failure_message(Chef::Exceptions::EnclosingDirectoryDoesNotExist, "Parent directory #{parent_directory} does not exist, cannot create #{new_resource.path}")
a.whyrun("Assuming directory #{parent_directory} would have been created")
end
requirements.assert(:create) do |a|
- parent_directory = ::File.dirname(@new_resource.path)
+ parent_directory = ::File.dirname(new_resource.path)
a.assertion do
- if @new_resource.recursive
- # find the lowest-level directory in @new_resource.path that already exists
+ if new_resource.recursive
+ # find the lowest-level directory in new_resource.path that already exists
# make sure we have write permissions to that directory
is_parent_writable = lambda do |base_dir|
base_dir = ::File.dirname(base_dir)
@@ -89,7 +85,7 @@ class Chef
is_parent_writable.call(base_dir)
end
end
- is_parent_writable.call(@new_resource.path)
+ is_parent_writable.call(new_resource.path)
else
# in why run mode & parent directory does not exist no permissions check is required
# If not in why run, permissions must be valid and we rely on prior assertion that dir exists
@@ -97,7 +93,7 @@ class Chef
if Chef::FileAccessControl.writable?(parent_directory)
true
elsif Chef::Util::PathHelper.is_sip_path?(parent_directory, node)
- Chef::Util::PathHelper.writable_sip_path?(@new_resource.path)
+ Chef::Util::PathHelper.writable_sip_path?(new_resource.path)
else
false
end
@@ -107,18 +103,18 @@ class Chef
end
end
a.failure_message(Chef::Exceptions::InsufficientPermissions,
- "Cannot create #{@new_resource} at #{@new_resource.path} due to insufficient permissions")
+ "Cannot create #{new_resource} at #{new_resource.path} due to insufficient permissions")
end
requirements.assert(:delete) do |a|
a.assertion do
- if ::File.exists?(@new_resource.path)
- ::File.directory?(@new_resource.path) && Chef::FileAccessControl.writable?(@new_resource.path)
+ if ::File.exists?(new_resource.path)
+ ::File.directory?(new_resource.path) && Chef::FileAccessControl.writable?(new_resource.path)
else
true
end
end
- a.failure_message(RuntimeError, "Cannot delete #{@new_resource} at #{@new_resource.path}!")
+ a.failure_message(RuntimeError, "Cannot delete #{new_resource} at #{new_resource.path}!")
# No why-run handling here:
# * if we don't have permissions, this is unlikely to be changed earlier in the run
# * if the target is a file (not a dir), there's no reasonable path by which this would have been changed
@@ -126,30 +122,32 @@ class Chef
end
def action_create
- unless ::File.exists?(@new_resource.path)
- converge_by("create new directory #{@new_resource.path}") do
- if @new_resource.recursive == true
- ::FileUtils.mkdir_p(@new_resource.path)
+ unless ::File.exists?(new_resource.path)
+ converge_by("create new directory #{new_resource.path}") do
+ if new_resource.recursive == true
+ ::FileUtils.mkdir_p(new_resource.path)
else
- ::Dir.mkdir(@new_resource.path)
+ ::Dir.mkdir(new_resource.path)
end
- Chef::Log.info("#{@new_resource} created directory #{@new_resource.path}")
+ logger.info("#{new_resource} created directory #{new_resource.path}")
end
end
do_acl_changes
do_selinux(true)
- load_resource_attributes_from_file(@new_resource)
+ load_resource_attributes_from_file(new_resource) unless Chef::Config[:why_run]
end
def action_delete
- if ::File.exists?(@new_resource.path)
- converge_by("delete existing directory #{@new_resource.path}") do
- if @new_resource.recursive == true
- FileUtils.rm_rf(@new_resource.path)
- Chef::Log.info("#{@new_resource} deleted #{@new_resource.path} recursively")
+ if ::File.exists?(new_resource.path)
+ converge_by("delete existing directory #{new_resource.path}") do
+ if new_resource.recursive == true
+ # we don't use rm_rf here because it masks all errors, including
+ # IO errors or permission errors that would prvent the deletion
+ FileUtils.rm_r(new_resource.path)
+ logger.info("#{new_resource} deleted #{new_resource.path} recursively")
else
- ::Dir.delete(@new_resource.path)
- Chef::Log.info("#{@new_resource} deleted #{@new_resource.path}")
+ ::Dir.delete(new_resource.path)
+ logger.info("#{new_resource} deleted #{new_resource.path}")
end
end
end
diff --git a/lib/chef/provider/dsc_resource.rb b/lib/chef/provider/dsc_resource.rb
index 0f25065925..9c147cb634 100644
--- a/lib/chef/provider/dsc_resource.rb
+++ b/lib/chef/provider/dsc_resource.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2017, Chef Software Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -24,11 +24,12 @@ class Chef
class Provider
class DscResource < Chef::Provider
include Chef::Mixin::PowershellTypeCoercions
- provides :dsc_resource, os: "windows"
+ provides :dsc_resource
def initialize(new_resource, run_context)
super
@new_resource = new_resource
@module_name = new_resource.module_name
+ @module_version = new_resource.module_version
@reboot_resource = nil
end
@@ -44,10 +45,6 @@ class Chef
def load_current_resource
end
- def whyrun_supported?
- true
- end
-
def define_resource_requirements
requirements.assert(:run) do |a|
a.assertion { supports_dsc_invoke_resource? }
@@ -65,6 +62,13 @@ class Chef
a.whyrun err + ["Assuming a previous resource sets the RefreshMode."]
a.block_action!
end
+ requirements.assert(:run) do |a|
+ a.assertion { module_usage_valid? }
+ err = ["module_name must be supplied along with module_version."]
+ a.failure_message Chef::Exceptions::DSCModuleNameMissing,
+ err
+ a.block_action!
+ end
end
protected
@@ -92,6 +96,10 @@ class Chef
Chef::Platform.supports_refresh_mode_enabled?(node)
end
+ def module_usage_valid?
+ !(!@module_name && @module_version)
+ end
+
def generate_description
@converge_description
end
@@ -148,10 +156,14 @@ class Chef
end
end
+ def module_info_object
+ @module_version.nil? ? module_name : "@{ModuleName='#{module_name}';ModuleVersion='#{@module_version}'}"
+ end
+
def invoke_resource(method, output_format = :object)
- properties = translate_type(@new_resource.properties)
- switches = "-Method #{method} -Name #{@new_resource.resource}"\
- " -Property #{properties} -Module #{module_name} -Verbose"
+ properties = translate_type(new_resource.properties)
+ switches = "-Method #{method} -Name #{new_resource.resource}"\
+ " -Property #{properties} -Module #{module_info_object} -Verbose"
cmdlet = Chef::Util::Powershell::Cmdlet.new(
node,
"Invoke-DscResource #{switches}",
@@ -172,22 +184,22 @@ class Chef
def create_reboot_resource
@reboot_resource = Chef::Resource::Reboot.new(
- "Reboot for #{@new_resource.name}",
+ "Reboot for #{new_resource.name}",
run_context
).tap do |r|
- r.reason("Reboot for #{@new_resource.resource}.")
+ r.reason("Reboot for #{new_resource.resource}.")
end
end
def reboot_if_required
- reboot_action = @new_resource.reboot_action
+ reboot_action = new_resource.reboot_action
unless @reboot_resource.nil?
case reboot_action
when :nothing
- Chef::Log.debug("A reboot was requested by the DSC resource, but reboot_action is :nothing.")
- Chef::Log.debug("This dsc_resource will not reboot the node.")
+ logger.trace("A reboot was requested by the DSC resource, but reboot_action is :nothing.")
+ logger.trace("This dsc_resource will not reboot the node.")
else
- Chef::Log.debug("Requesting node reboot with #{reboot_action}.")
+ logger.trace("Requesting node reboot with #{reboot_action}.")
@reboot_resource.run_action(reboot_action)
end
end
diff --git a/lib/chef/provider/dsc_script.rb b/lib/chef/provider/dsc_script.rb
index 79769d9773..7a101fa68b 100644
--- a/lib/chef/provider/dsc_script.rb
+++ b/lib/chef/provider/dsc_script.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2017, Chef Software Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -25,26 +25,26 @@ class Chef
class Provider
class DscScript < Chef::Provider
- provides :dsc_script, os: "windows"
+ provides :dsc_script
def initialize(dsc_resource, run_context)
super(dsc_resource, run_context)
@dsc_resource = dsc_resource
@resource_converged = false
@operations = {
- :set => Proc.new { |config_manager, document, shellout_flags|
+ :set => Proc.new do |config_manager, document, shellout_flags|
config_manager.set_configuration(document, shellout_flags)
- },
- :test => Proc.new { |config_manager, document, shellout_flags|
+ end,
+ :test => Proc.new do |config_manager, document, shellout_flags|
config_manager.test_configuration(document, shellout_flags)
- } }
+ end }
end
def action_run
if ! @resource_converged
converge_by(generate_description) do
run_configuration(:set)
- Chef::Log.info("DSC resource configuration completed successfully")
+ logger.info("DSC resource configuration completed successfully")
end
end
end
@@ -58,10 +58,6 @@ class Chef
end
end
- def whyrun_supported?
- true
- end
-
def define_resource_requirements
requirements.assert(:run) do |a|
err = [
@@ -99,7 +95,7 @@ class Chef
configuration_document = generate_configuration_document(config_directory, configuration_flags)
@operations[operation].call(config_manager, configuration_document, shellout_flags)
rescue Exception => e
- Chef::Log.error("DSC operation failed: #{e.message}")
+ logger.error("DSC operation failed: #{e.message}")
raise e
ensure
::FileUtils.rm_rf(config_directory)
@@ -128,7 +124,7 @@ class Chef
generator.configuration_document_from_script_path(@dsc_resource.command, configuration_name, configuration_flags, shellout_flags)
else
# If code is also not provided, we mimic what the other script resources do (execute nothing)
- Chef::Log.warn("Neither code or command were provided for dsc_resource[#{@dsc_resource.name}].") unless @dsc_resource.code
+ logger.warn("Neither code or command were provided for dsc_resource[#{@dsc_resource.name}].") unless @dsc_resource.code
generator.configuration_document_from_script_code(@dsc_resource.code || "", configuration_flags, @dsc_resource.imports, shellout_flags)
end
end
@@ -165,7 +161,11 @@ class Chef
if resource.changes_state?
# We ignore the last log message because it only contains the time it took, which looks weird
cleaned_messages = resource.change_log[0..-2].map { |c| c.sub(/^#{Regexp.escape(resource.name)}/, "").strip }
- "converge DSC resource #{resource.name} by #{cleaned_messages.find_all { |c| c != '' }.join("\n")}"
+ unless cleaned_messages.empty?
+ "converge DSC resource #{resource.name} by #{cleaned_messages.find_all { |c| c != '' }.join("\n")}"
+ else
+ "converge DSC resource #{resource.name}"
+ end
else
# This is needed because a dsc script can have resources that are both converged and not
"converge DSC resource #{resource.name} by doing nothing because it is already converged"
diff --git a/lib/chef/provider/env.rb b/lib/chef/provider/env.rb
deleted file mode 100644
index 5b252dd344..0000000000
--- a/lib/chef/provider/env.rb
+++ /dev/null
@@ -1,169 +0,0 @@
-#
-# Author:: Doug MacEachern (<dougm@vmware.com>)
-# Copyright:: Copyright 2010-2016, VMware, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/provider"
-require "chef/mixin/command"
-require "chef/resource/env"
-
-class Chef
- class Provider
- class Env < Chef::Provider
- include Chef::Mixin::Command
- attr_accessor :key_exists
-
- provides :env, os: "!windows"
-
- def initialize(new_resource, run_context)
- super
- @key_exists = true
- end
-
- def load_current_resource
- @current_resource = Chef::Resource::Env.new(@new_resource.name)
- @current_resource.key_name(@new_resource.key_name)
-
- if env_key_exists(@new_resource.key_name)
- @current_resource.value(env_value(@new_resource.key_name))
- else
- @key_exists = false
- Chef::Log.debug("#{@new_resource} key does not exist")
- end
-
- @current_resource
- end
-
- def env_value(key_name)
- raise Chef::Exceptions::Env, "#{self} provider does not implement env_value!"
- end
-
- def env_key_exists(key_name)
- env_value(key_name) ? true : false
- end
-
- # Check to see if value needs any changes
- #
- # ==== Returns
- # <true>:: If a change is required
- # <false>:: If a change is not required
- def requires_modify_or_create?
- if @new_resource.delim
- #e.g. check for existing value within PATH
- new_values.inject(0) do |index, val|
- next_index = current_values.find_index val
- return true if next_index.nil? || next_index < index
- next_index
- end
- false
- else
- @new_resource.value != @current_resource.value
- end
- end
-
- alias_method :compare_value, :requires_modify_or_create?
-
- def action_create
- if @key_exists
- if requires_modify_or_create?
- modify_env
- Chef::Log.info("#{@new_resource} altered")
- @new_resource.updated_by_last_action(true)
- end
- else
- create_env
- Chef::Log.info("#{@new_resource} created")
- @new_resource.updated_by_last_action(true)
- end
- end
-
- #e.g. delete a PATH element
- #
- # ==== Returns
- # <true>:: If we handled the element case and caller should not delete the key
- # <false>:: Caller should delete the key, either no :delim was specific or value was empty
- # after we removed the element.
- def delete_element
- return false unless @new_resource.delim #no delim: delete the key
- needs_delete = new_values.any? { |v| current_values.include?(v) }
- if !needs_delete
- Chef::Log.debug("#{@new_resource} element '#{@new_resource.value}' does not exist")
- return true #do not delete the key
- else
- new_value =
- current_values.select do |item|
- not new_values.include?(item)
- end.join(@new_resource.delim)
-
- if new_value.empty?
- return false #nothing left here, delete the key
- else
- old_value = @new_resource.value(new_value)
- create_env
- Chef::Log.debug("#{@new_resource} deleted #{old_value} element")
- @new_resource.updated_by_last_action(true)
- return true #we removed the element and updated; do not delete the key
- end
- end
- end
-
- def action_delete
- if @key_exists && !delete_element
- delete_env
- Chef::Log.info("#{@new_resource} deleted")
- @new_resource.updated_by_last_action(true)
- end
- end
-
- def action_modify
- if @key_exists
- if requires_modify_or_create?
- modify_env
- Chef::Log.info("#{@new_resource} modified")
- @new_resource.updated_by_last_action(true)
- end
- else
- raise Chef::Exceptions::Env, "Cannot modify #{@new_resource} - key does not exist!"
- end
- end
-
- def create_env
- raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :#{@new_resource.action}"
- end
-
- def delete_env
- raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :delete"
- end
-
- def modify_env
- if @new_resource.delim
- @new_resource.value((new_values + current_values).uniq.join(@new_resource.delim))
- end
- create_env
- end
-
- # Returns the current values to split by delimiter
- def current_values
- @current_values ||= @current_resource.value.split(@new_resource.delim)
- end
-
- # Returns the new values to split by delimiter
- def new_values
- @new_values ||= @new_resource.value.split(@new_resource.delim)
- end
- end
- end
-end
diff --git a/lib/chef/provider/env/windows.rb b/lib/chef/provider/env/windows.rb
deleted file mode 100644
index a68c8276e0..0000000000
--- a/lib/chef/provider/env/windows.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-#
-# Author:: Doug MacEachern (<dougm@vmware.com>)
-# Copyright:: Copyright 2010-2016, VMware, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/mixin/windows_env_helper"
-
-class Chef
- class Provider
- class Env
- class Windows < Chef::Provider::Env
- include Chef::Mixin::WindowsEnvHelper
-
- provides :env, os: "windows"
-
- def create_env
- obj = env_obj(@new_resource.key_name)
- unless obj
- obj = WIN32OLE.connect("winmgmts://").get("Win32_Environment").spawninstance_
- obj.name = @new_resource.key_name
- obj.username = "<System>"
- end
- obj.variablevalue = @new_resource.value
- obj.put_
- value = @new_resource.value
- value = expand_path(value) if @new_resource.key_name.casecmp("PATH").zero?
- ENV[@new_resource.key_name] = value
- broadcast_env_change
- end
-
- def delete_env
- obj = env_obj(@new_resource.key_name)
- if obj
- obj.delete_
- broadcast_env_change
- end
- if ENV[@new_resource.key_name]
- ENV.delete(@new_resource.key_name)
- end
- end
-
- def env_value(key_name)
- obj = env_obj(key_name)
- return obj ? obj.variablevalue : ENV[key_name]
- end
-
- def env_obj(key_name)
- wmi = WmiLite::Wmi.new
- # Note that by design this query is case insensitive with regard to key_name
- environment_variables = wmi.query("select * from Win32_Environment where name = '#{key_name}'")
- if environment_variables && environment_variables.length > 0
- environment_variables[0].wmi_ole_object
- end
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/provider/erl_call.rb b/lib/chef/provider/erl_call.rb
deleted file mode 100644
index 7167f3b8a5..0000000000
--- a/lib/chef/provider/erl_call.rb
+++ /dev/null
@@ -1,108 +0,0 @@
-#
-# Author:: Joe Williams (<joe@joetify.com>)
-# Copyright:: Copyright 2009-2016, Joe Williams
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/log"
-require "chef/mixin/command"
-require "chef/provider"
-
-class Chef
- class Provider
- class ErlCall < Chef::Provider
- include Chef::Mixin::Command
-
- provides :erl_call
-
- def initialize(node, new_resource)
- super(node, new_resource)
- end
-
- def whyrun_supported?
- true
- end
-
- def load_current_resource
- true
- end
-
- def action_run
- case @new_resource.name_type
- when "sname"
- node = "-sname #{@new_resource.node_name}"
- when "name"
- node = "-name #{@new_resource.node_name}"
- end
-
- if @new_resource.cookie
- cookie = "-c #{@new_resource.cookie}"
- else
- cookie = ""
- end
-
- if @new_resource.distributed
- distributed = "-s"
- else
- distributed = ""
- end
-
- command = "erl_call -e #{distributed} #{node} #{cookie}"
-
- converge_by("run erlang block") do
- begin
- pid, stdin, stdout, stderr = popen4(command, :waitlast => true)
-
- Chef::Log.debug("#{@new_resource} running")
- Chef::Log.debug("#{@new_resource} command: #{command}")
- Chef::Log.debug("#{@new_resource} code: #{@new_resource.code}")
-
- @new_resource.code.each_line { |line| stdin.puts(line.chomp) }
-
- stdin.close
-
- Chef::Log.debug("#{@new_resource} output: ")
-
- stdout_output = ""
- stdout.each_line { |line| stdout_output << line }
- stdout.close
-
- stderr_output = ""
- stderr.each_line { |line| stderr_output << line }
- stderr.close
-
- # fail if stderr contains anything
- if stderr_output.length > 0
- raise Chef::Exceptions::ErlCall, stderr_output
- end
-
- # fail if the first 4 characters aren't "{ok,"
- unless stdout_output[0..3].include?("{ok,")
- raise Chef::Exceptions::ErlCall, stdout_output
- end
-
- @new_resource.updated_by_last_action(true)
-
- Chef::Log.debug("#{@new_resource} #{stdout_output}")
- Chef::Log.info("#{@new_resouce} ran successfully")
- ensure
- Process.wait(pid) if pid
- end
- end
- end
-
- end
- end
-end
diff --git a/lib/chef/provider/execute.rb b/lib/chef/provider/execute.rb
index 45f0ad5488..6872e2d67d 100644
--- a/lib/chef/provider/execute.rb
+++ b/lib/chef/provider/execute.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,21 +27,17 @@ class Chef
provides :execute
- def_delegators :@new_resource, :command, :returns, :environment, :user, :group, :cwd, :umask, :creates
+ def_delegators :new_resource, :command, :returns, :environment, :user, :domain, :password, :group, :cwd, :umask, :creates, :elevated
def load_current_resource
current_resource = Chef::Resource::Execute.new(new_resource.name)
current_resource
end
- def whyrun_supported?
- true
- end
-
def define_resource_requirements
- # @todo: this should change to raise in some appropriate major version bump.
if creates && creates_relative? && !cwd
- Chef::Log.warn "Providing a relative path for the creates attribute without the cwd is deprecated and will be changed to fail in the future (CHEF-3819)"
+ # FIXME? move this onto the resource?
+ raise Chef::Exceptions::Execute, "Please either specify a full path for the creates attribute, or specify a cwd property to the #{new_resource} resource"
end
end
@@ -53,22 +49,26 @@ class Chef
def action_run
if creates && sentinel_file.exist?
- Chef::Log.debug("#{new_resource} sentinel file #{sentinel_file} exists - nothing to do")
+ logger.debug("#{new_resource} sentinel file #{sentinel_file} exists - nothing to do")
return false
end
converge_by("execute #{description}") do
begin
- shell_out!(command, opts)
+ shell_out_with_systems_locale!(command, opts)
rescue Mixlib::ShellOut::ShellCommandFailed
if sensitive?
- raise Mixlib::ShellOut::ShellCommandFailed,
- "Command execution failed. STDOUT/STDERR suppressed for sensitive resource"
+ ex = Mixlib::ShellOut::ShellCommandFailed.new("Command execution failed. STDOUT/STDERR suppressed for sensitive resource")
+ # Forcibly hide the exception cause chain here so we don't log the unredacted version
+ def ex.cause
+ nil
+ end
+ raise ex
else
raise
end
end
- Chef::Log.info("#{new_resource} ran successfully")
+ logger.info("#{new_resource} ran successfully")
end
end
@@ -92,18 +92,21 @@ class Chef
opts[:returns] = returns if returns
opts[:environment] = environment if environment
opts[:user] = user if user
+ opts[:domain] = domain if domain
+ opts[:password] = password if password
opts[:group] = group if group
opts[:cwd] = cwd if cwd
opts[:umask] = umask if umask
opts[:log_level] = :info
opts[:log_tag] = new_resource.to_s
- if (Chef::Log.info? || live_stream?) && !sensitive?
+ if (logger.info? || live_stream?) && !sensitive?
if run_context.events.formatter?
opts[:live_stream] = Chef::EventDispatch::EventsOutputStream.new(run_context.events, :name => :execute)
elsif stream_to_stdout?
opts[:live_stream] = STDOUT
end
end
+ opts[:elevated] = elevated if elevated
opts
end
@@ -120,6 +123,7 @@ class Chef
( cwd && creates_relative? ) ? ::File.join(cwd, creates) : creates
))
end
+
end
end
end
diff --git a/lib/chef/provider/file.rb b/lib/chef/provider/file.rb
index 7f85085eeb..05522f1eb8 100644
--- a/lib/chef/provider/file.rb
+++ b/lib/chef/provider/file.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,8 +30,6 @@ require "chef/mixin/enforce_ownership_and_permissions"
require "chef/util/backup"
require "chef/util/diff"
require "chef/util/selinux"
-require "chef/deprecation/provider/file"
-require "chef/deprecation/warnings"
require "chef/file_content_management/deploy"
# The Tao of File Providers:
@@ -52,10 +50,6 @@ class Chef
include Chef::Util::Selinux
include Chef::Mixin::FileClass
- extend Chef::Deprecation::Warnings
- include Chef::Deprecation::Provider::File
- add_deprecation_warnings_for(Chef::Deprecation::Provider::File.instance_methods)
-
provides :file
attr_reader :deployment_strategy
@@ -72,10 +66,6 @@ class Chef
super
end
- def whyrun_supported?
- true
- end
-
def load_current_resource
# true if there is a symlink and we need to manage what it points at
@managing_symlink = file_class.symlink?(new_resource.path) && ( new_resource.manage_symlink_source || new_resource.manage_symlink_source.nil? )
@@ -95,14 +85,14 @@ class Chef
# true if we are going to be creating a new file
@needs_creating = !::File.exist?(new_resource.path) || needs_unlinking?
- # Let children resources override constructing the @current_resource
+ # Let children resources override constructing the current_resource
@current_resource ||= Chef::Resource::File.new(new_resource.name)
current_resource.path(new_resource.path)
if !needs_creating?
# we are updating an existing file
if managing_content?
- Chef::Log.debug("#{new_resource} checksumming file at #{new_resource.path}.")
+ logger.trace("#{new_resource} checksumming file at #{new_resource.path}.")
current_resource.checksum(checksum(current_resource.path))
else
# if the file does not exist or is not a file, then the checksum is invalid/pointless
@@ -120,17 +110,17 @@ class Chef
# Make sure the parent directory exists, otherwise fail. For why-run assume it would have been created.
requirements.assert(:create, :create_if_missing, :touch) do |a|
- parent_directory = ::File.dirname(@new_resource.path)
+ parent_directory = ::File.dirname(new_resource.path)
a.assertion { ::File.directory?(parent_directory) }
a.failure_message(Chef::Exceptions::EnclosingDirectoryDoesNotExist, "Parent directory #{parent_directory} does not exist.")
a.whyrun("Assuming directory #{parent_directory} would have been created")
end
# Make sure the file is deletable if it exists, otherwise fail.
- if ::File.exist?(@new_resource.path)
+ if ::File.exist?(new_resource.path)
requirements.assert(:delete) do |a|
- a.assertion { ::File.writable?(@new_resource.path) }
- a.failure_message(Chef::Exceptions::InsufficientPermissions, "File #{@new_resource.path} exists but is not writable so it cannot be deleted")
+ a.assertion { ::File.writable?(new_resource.path) }
+ a.failure_message(Chef::Exceptions::InsufficientPermissions, "File #{new_resource.path} exists but is not writable so it cannot be deleted")
end
end
@@ -154,33 +144,33 @@ class Chef
do_contents_changes
do_acl_changes
do_selinux
- load_resource_attributes_from_file(@new_resource)
+ load_resource_attributes_from_file(new_resource) unless Chef::Config[:why_run]
end
def action_create_if_missing
- unless ::File.exist?(@new_resource.path)
+ unless ::File.exist?(new_resource.path)
action_create
else
- Chef::Log.debug("#{@new_resource} exists at #{@new_resource.path} taking no action.")
+ logger.trace("#{new_resource} exists at #{new_resource.path} taking no action.")
end
end
def action_delete
- if ::File.exists?(@new_resource.path)
- converge_by("delete file #{@new_resource.path}") do
- do_backup unless file_class.symlink?(@new_resource.path)
- ::File.delete(@new_resource.path)
- Chef::Log.info("#{@new_resource} deleted file at #{@new_resource.path}")
+ if ::File.exists?(new_resource.path)
+ converge_by("delete file #{new_resource.path}") do
+ do_backup unless file_class.symlink?(new_resource.path)
+ ::File.delete(new_resource.path)
+ logger.info("#{new_resource} deleted file at #{new_resource.path}")
end
end
end
def action_touch
action_create
- converge_by("update utime on file #{@new_resource.path}") do
+ converge_by("update utime on file #{new_resource.path}") do
time = Time.now
- ::File.utime(time, time, @new_resource.path)
- Chef::Log.info("#{@new_resource} updated atime and mtime to #{time}")
+ ::File.utime(time, time, new_resource.path)
+ logger.info("#{new_resource} updated atime and mtime to #{time}")
end
end
@@ -197,8 +187,8 @@ class Chef
# content (for things like doing checksums in load_current_resource). Expected to
# be overridden in subclasses.
def managing_content?
- return true if @new_resource.checksum
- return true if !@new_resource.content.nil? && @action != :create_if_missing
+ return true if new_resource.checksum
+ return true if !new_resource.content.nil? && @action != :create_if_missing
false
end
@@ -228,25 +218,25 @@ class Chef
# assertions, which then decide whether or not to raise or issue a
# warning for whyrun mode.
def inspect_existing_fs_entry
- path = @new_resource.path
+ path = new_resource.path
if !l_exist?(path)
[nil, nil, nil]
elsif real_file?(path)
[nil, nil, nil]
- elsif file_class.symlink?(path) && @new_resource.manage_symlink_source
+ elsif file_class.symlink?(path) && new_resource.manage_symlink_source
verify_symlink_sanity(path)
- elsif file_class.symlink?(@new_resource.path) && @new_resource.manage_symlink_source.nil?
- Chef::Log.warn("File #{path} managed by #{@new_resource} is really a symlink. Managing the source file instead.")
- Chef::Log.warn("Disable this warning by setting `manage_symlink_source true` on the resource")
- Chef::Log.warn("In a future Chef release, 'manage_symlink_source' will not be enabled by default")
+ elsif file_class.symlink?(new_resource.path) && new_resource.manage_symlink_source.nil?
+ logger.warn("File #{path} managed by #{new_resource} is really a symlink. Managing the source file instead.")
+ logger.warn("Disable this warning by setting `manage_symlink_source true` on the resource")
+ logger.warn("In a future Chef release, 'manage_symlink_source' will not be enabled by default")
verify_symlink_sanity(path)
- elsif @new_resource.force_unlink
+ elsif new_resource.force_unlink
[nil, nil, nil]
else
[ Chef::Exceptions::FileTypeMismatch,
- "File #{path} exists, but is a #{file_type_string(@new_resource.path)}, set force_unlink to true to remove",
- "Assuming #{file_type_string(@new_resource.path)} at #{@new_resource.path} would have been removed by a previous resource",
+ "File #{path} exists, but is a #{file_type_string(new_resource.path)}, set force_unlink to true to remove",
+ "Assuming #{file_type_string(new_resource.path)} at #{new_resource.path} would have been removed by a previous resource",
]
end
end
@@ -282,8 +272,8 @@ class Chef
def content
@content ||= begin
- load_current_resource if @current_resource.nil?
- @content_class.new(@new_resource, @current_resource, @run_context)
+ load_current_resource if current_resource.nil?
+ @content_class.new(new_resource, current_resource, @run_context, logger)
end
end
@@ -312,11 +302,9 @@ class Chef
# like real_file? that follows (sane) symlinks
def symlink_to_real_file?(path)
- begin
- real_file?(::File.realpath(path))
- rescue Errno::ELOOP, Errno::ENOENT
- false
- end
+ real_file?(::File.realpath(path))
+ rescue Errno::ELOOP, Errno::ENOENT
+ false
end
# Similar to File.exist?, but also returns true in the case that the
@@ -344,26 +332,26 @@ class Chef
end
def do_validate_content
- if new_resource.checksum && tempfile && ( new_resource.checksum != tempfile_checksum )
+ if new_resource.checksum && tempfile && ( new_resource.checksum.downcase != tempfile_checksum )
raise Chef::Exceptions::ChecksumMismatch.new(short_cksum(new_resource.checksum), short_cksum(tempfile_checksum))
end
if tempfile
new_resource.verify.each do |v|
if ! v.verify(tempfile.path)
- raise Chef::Exceptions::ValidationFailed.new "Proposed content for #{new_resource.path} failed verification #{v}"
+ raise Chef::Exceptions::ValidationFailed.new "Proposed content for #{new_resource.path} failed verification #{new_resource.sensitive ? '[sensitive]' : v}"
end
end
end
end
def do_unlink
- if @new_resource.force_unlink
+ if new_resource.force_unlink
if needs_unlinking?
# unlink things that aren't normal files
- description = "unlink #{file_type_string(@new_resource.path)} at #{@new_resource.path}"
+ description = "unlink #{file_type_string(new_resource.path)} at #{new_resource.path}"
converge_by(description) do
- unlink(@new_resource.path)
+ unlink(new_resource.path)
end
end
end
@@ -371,15 +359,15 @@ class Chef
def do_create_file
if needs_creating?
- converge_by("create new file #{@new_resource.path}") do
- deployment_strategy.create(@new_resource.path)
- Chef::Log.info("#{@new_resource} created file #{@new_resource.path}")
+ converge_by("create new file #{new_resource.path}") do
+ deployment_strategy.create(new_resource.path)
+ logger.info("#{new_resource} created file #{new_resource.path}")
end
end
end
def do_backup(file = nil)
- Chef::Util::Backup.new(@new_resource, file).backup!
+ Chef::Util::Backup.new(new_resource, file).backup!
end
def diff
@@ -389,7 +377,7 @@ class Chef
def update_file_contents
do_backup unless needs_creating?
deployment_strategy.deploy(tempfile.path, ::File.realpath(new_resource.path))
- Chef::Log.info("#{new_resource} updated file contents #{new_resource.path}")
+ logger.info("#{new_resource} updated file contents #{new_resource.path}")
if managing_content?
# save final checksum for reporting.
new_resource.final_checksum = checksum(new_resource.path)
@@ -405,17 +393,17 @@ class Chef
end
# the file? on the next line suppresses the case in why-run when we have a not-file here that would have otherwise been removed
- if ::File.file?(@new_resource.path) && contents_changed?
- description = [ "update content in file #{@new_resource.path} from \
-#{short_cksum(@current_resource.checksum)} to #{short_cksum(tempfile_checksum)}" ]
+ if ::File.file?(new_resource.path) && contents_changed?
+ description = [ "update content in file #{new_resource.path} from \
+#{short_cksum(current_resource.checksum)} to #{short_cksum(tempfile_checksum)}" ]
# Hide the diff output if the resource is marked as a sensitive resource
- if @new_resource.sensitive
- @new_resource.diff("suppressed sensitive resource")
+ if new_resource.sensitive
+ new_resource.diff("suppressed sensitive resource")
description << "suppressed sensitive resource"
else
- diff.diff(@current_resource.path, tempfile.path)
- @new_resource.diff( diff.for_reporting ) unless needs_creating?
+ diff.diff(current_resource.path, tempfile.path)
+ new_resource.diff( diff.for_reporting ) unless needs_creating?
description << diff.for_output
end
@@ -437,10 +425,10 @@ class Chef
if resource_updated? && Chef::Config[:enable_selinux_file_permission_fixup]
if selinux_enabled?
converge_by("restore selinux security context") do
- restore_security_context(::File.realpath(@new_resource.path), recursive)
+ restore_security_context(::File.realpath(new_resource.path), recursive)
end
else
- Chef::Log.debug "selinux utilities can not be found. Skipping selinux permission fixup."
+ logger.trace "selinux utilities can not be found. Skipping selinux permission fixup."
end
end
end
@@ -454,19 +442,14 @@ class Chef
end
def contents_changed?
- Chef::Log.debug "calculating checksum of #{tempfile.path} to compare with #{@current_resource.checksum}"
- tempfile_checksum != @current_resource.checksum
+ logger.trace "calculating checksum of #{tempfile.path} to compare with #{current_resource.checksum}"
+ tempfile_checksum != current_resource.checksum
end
def tempfile
@tempfile ||= content.tempfile
end
- def short_cksum(checksum)
- return "none" if checksum.nil?
- checksum.slice(0, 6)
- end
-
def load_resource_attributes_from_file(resource)
if Chef::Platform.windows?
# This is a work around for CHEF-3554.
@@ -475,7 +458,7 @@ class Chef
# reporting won't work for Windows.
return
end
- acl_scanner = ScanAccessControl.new(@new_resource, resource)
+ acl_scanner = ScanAccessControl.new(new_resource, resource)
acl_scanner.set_all!
end
diff --git a/lib/chef/provider/git.rb b/lib/chef/provider/git.rb
index 82f5ca2ba5..239dfa09e6 100644
--- a/lib/chef/provider/git.rb
+++ b/lib/chef/provider/git.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,78 +25,79 @@ class Chef
class Provider
class Git < Chef::Provider
+ extend Forwardable
provides :git
- def whyrun_supported?
- true
- end
+ GIT_VERSION_PATTERN = Regexp.compile('git version (\d+\.\d+.\d+)')
+
+ def_delegator :new_resource, :destination, :cwd
def load_current_resource
@resolved_reference = nil
- @current_resource = Chef::Resource::Git.new(@new_resource.name)
+ @current_resource = Chef::Resource::Git.new(new_resource.name)
if current_revision = find_current_revision
- @current_resource.revision current_revision
+ current_resource.revision current_revision
end
end
def define_resource_requirements
# Parent directory of the target must exist.
requirements.assert(:checkout, :sync) do |a|
- dirname = ::File.dirname(@new_resource.destination)
+ dirname = ::File.dirname(cwd)
a.assertion { ::File.directory?(dirname) }
a.whyrun("Directory #{dirname} does not exist, this run will fail unless it has been previously created. Assuming it would have been created.")
a.failure_message(Chef::Exceptions::MissingParentDirectory,
- "Cannot clone #{@new_resource} to #{@new_resource.destination}, the enclosing directory #{dirname} does not exist")
+ "Cannot clone #{new_resource} to #{cwd}, the enclosing directory #{dirname} does not exist")
end
requirements.assert(:all_actions) do |a|
- a.assertion { !(@new_resource.revision =~ /^origin\//) }
+ a.assertion { !(new_resource.revision =~ /^origin\//) }
a.failure_message Chef::Exceptions::InvalidRemoteGitReference,
"Deploying remote branches is not supported. " +
"Specify the remote branch as a local branch for " +
"the git repository you're deploying from " +
- "(ie: '#{@new_resource.revision.gsub('origin/', '')}' rather than '#{@new_resource.revision}')."
+ "(ie: '#{new_resource.revision.gsub('origin/', '')}' rather than '#{new_resource.revision}')."
end
requirements.assert(:all_actions) do |a|
# this can't be recovered from in why-run mode, because nothing that
# we do in the course of a run is likely to create a valid target_revision
# if we can't resolve it up front.
- a.assertion { target_revision != nil }
+ a.assertion { !target_revision.nil? }
a.failure_message Chef::Exceptions::UnresolvableGitReference,
- "Unable to parse SHA reference for '#{@new_resource.revision}' in repository '#{@new_resource.repository}'. " +
+ "Unable to parse SHA reference for '#{new_resource.revision}' in repository '#{new_resource.repository}'. " +
"Verify your (case-sensitive) repository URL and revision.\n" +
- "`git ls-remote '#{@new_resource.repository}' '#{rev_search_pattern}'` output: #{@resolved_reference}"
+ "`git ls-remote '#{new_resource.repository}' '#{rev_search_pattern}'` output: #{@resolved_reference}"
end
end
def action_checkout
if target_dir_non_existent_or_empty?
clone
- if @new_resource.enable_checkout
+ if new_resource.enable_checkout
checkout
end
enable_submodules
add_remotes
else
- Chef::Log.debug "#{@new_resource} checkout destination #{@new_resource.destination} already exists or is a non-empty directory"
+ logger.trace "#{new_resource} checkout destination #{cwd} already exists or is a non-empty directory"
end
end
def action_export
action_checkout
- converge_by("complete the export by removing #{@new_resource.destination}.git after checkout") do
- FileUtils.rm_rf(::File.join(@new_resource.destination, ".git"))
+ converge_by("complete the export by removing #{cwd}.git after checkout") do
+ FileUtils.rm_rf(::File.join(cwd, ".git"))
end
end
def action_sync
if existing_git_clone?
- Chef::Log.debug "#{@new_resource} current revision: #{@current_resource.revision} target revision: #{target_revision}"
+ logger.trace "#{new_resource} current revision: #{current_resource.revision} target revision: #{target_revision}"
unless current_revision_matches_target_revision?
fetch_updates
enable_submodules
- Chef::Log.info "#{@new_resource} updated to revision #{target_revision}"
+ logger.info "#{new_resource} updated to revision #{target_revision}"
end
add_remotes
else
@@ -104,32 +105,45 @@ class Chef
end
end
- def git_minor_version
- @git_minor_version ||= Gem::Version.new(shell_out!("git --version", run_options).stdout.split.last)
+ def git_has_single_branch_option?
+ @git_has_single_branch_option ||= !git_gem_version.nil? && git_gem_version >= Gem::Version.new("1.7.10")
+ end
+
+ def git_gem_version
+ return @git_gem_version if defined?(@git_gem_version)
+ output = git("--version").stdout
+ match = GIT_VERSION_PATTERN.match(output)
+ if match
+ @git_gem_version = Gem::Version.new(match[1])
+ else
+ logger.warn "Unable to parse git version from '#{output}'"
+ @git_gem_version = nil
+ end
+ @git_gem_version
end
def existing_git_clone?
- ::File.exist?(::File.join(@new_resource.destination, ".git"))
+ ::File.exist?(::File.join(cwd, ".git"))
end
def target_dir_non_existent_or_empty?
- !::File.exist?(@new_resource.destination) || Dir.entries(@new_resource.destination).sort == [".", ".."]
+ !::File.exist?(cwd) || Dir.entries(cwd).sort == [".", ".."]
end
def find_current_revision
- Chef::Log.debug("#{@new_resource} finding current git revision")
+ logger.trace("#{new_resource} finding current git revision")
if ::File.exist?(::File.join(cwd, ".git"))
# 128 is returned when we're not in a git repo. this is fine
- result = shell_out!("git rev-parse HEAD", :cwd => cwd, :returns => [0, 128]).stdout.strip
+ result = git("rev-parse", "HEAD", cwd: cwd, returns: [0, 128]).stdout.strip
end
sha_hash?(result) ? result : nil
end
def add_remotes
- if @new_resource.additional_remotes.length > 0
- @new_resource.additional_remotes.each_pair do |remote_name, remote_url|
+ if new_resource.additional_remotes.length > 0
+ new_resource.additional_remotes.each_pair do |remote_name, remote_url|
converge_by("add remote #{remote_name} from #{remote_url}") do
- Chef::Log.info "#{@new_resource} adding git remote #{remote_name} = #{remote_url}"
+ logger.info "#{new_resource} adding git remote #{remote_name} = #{remote_url}"
setup_remote_tracking_branches(remote_name, remote_url)
end
end
@@ -137,61 +151,60 @@ class Chef
end
def clone
- converge_by("clone from #{@new_resource.repository} into #{@new_resource.destination}") do
- remote = @new_resource.remote
-
- args = []
- args << "-o #{remote}" unless remote == "origin"
- args << "--depth #{@new_resource.depth}" if @new_resource.depth
- args << "--no-single-branch" if @new_resource.depth && git_minor_version >= Gem::Version.new("1.7.10")
-
- Chef::Log.info "#{@new_resource} cloning repo #{@new_resource.repository} to #{@new_resource.destination}"
-
- clone_cmd = "git clone #{args.join(' ')} \"#{@new_resource.repository}\" \"#{@new_resource.destination}\""
- shell_out!(clone_cmd, run_options)
+ converge_by("clone from #{new_resource.repository} into #{cwd}") do
+ remote = new_resource.remote
+
+ clone_cmd = ["clone"]
+ clone_cmd << "-o #{remote}" unless remote == "origin"
+ clone_cmd << "--depth #{new_resource.depth}" if new_resource.depth
+ clone_cmd << "--no-single-branch" if new_resource.depth && git_has_single_branch_option?
+ clone_cmd << "\"#{new_resource.repository}\""
+ clone_cmd << "\"#{cwd}\""
+
+ logger.info "#{new_resource} cloning repo #{new_resource.repository} to #{cwd}"
+ git clone_cmd
end
end
def checkout
sha_ref = target_revision
- converge_by("checkout ref #{sha_ref} branch #{@new_resource.revision}") do
+ converge_by("checkout ref #{sha_ref} branch #{new_resource.revision}") do
# checkout into a local branch rather than a detached HEAD
- shell_out!("git branch -f #{@new_resource.checkout_branch} #{sha_ref}", run_options(:cwd => @new_resource.destination))
- shell_out!("git checkout #{@new_resource.checkout_branch}", run_options(:cwd => @new_resource.destination))
- Chef::Log.info "#{@new_resource} checked out branch: #{@new_resource.revision} onto: #{@new_resource.checkout_branch} reference: #{sha_ref}"
+ git("branch", "-f", new_resource.checkout_branch, sha_ref, cwd: cwd)
+ git("checkout", new_resource.checkout_branch, cwd: cwd)
+ logger.info "#{new_resource} checked out branch: #{new_resource.revision} onto: #{new_resource.checkout_branch} reference: #{sha_ref}"
end
end
def enable_submodules
- if @new_resource.enable_submodules
- converge_by("enable git submodules for #{@new_resource}") do
- Chef::Log.info "#{@new_resource} synchronizing git submodules"
- command = "git submodule sync"
- shell_out!(command, run_options(:cwd => @new_resource.destination))
- Chef::Log.info "#{@new_resource} enabling git submodules"
+ if new_resource.enable_submodules
+ converge_by("enable git submodules for #{new_resource}") do
+ logger.info "#{new_resource} synchronizing git submodules"
+ git("submodule", "sync", cwd: cwd)
+ logger.info "#{new_resource} enabling git submodules"
# the --recursive flag means we require git 1.6.5+ now, see CHEF-1827
- command = "git submodule update --init --recursive"
- shell_out!(command, run_options(:cwd => @new_resource.destination))
+ git("submodule", "update", "--init", "--recursive", cwd: cwd)
end
end
end
def fetch_updates
- setup_remote_tracking_branches(@new_resource.remote, @new_resource.repository)
- converge_by("fetch updates for #{@new_resource.remote}") do
+ setup_remote_tracking_branches(new_resource.remote, new_resource.repository)
+ converge_by("fetch updates for #{new_resource.remote}") do
# since we're in a local branch already, just reset to specified revision rather than merge
- fetch_command = "git fetch #{@new_resource.remote} && git fetch #{@new_resource.remote} --tags && git reset --hard #{target_revision}"
- Chef::Log.debug "Fetching updates from #{new_resource.remote} and resetting to revision #{target_revision}"
- shell_out!(fetch_command, run_options(:cwd => @new_resource.destination))
+ logger.trace "Fetching updates from #{new_resource.remote} and resetting to revision #{target_revision}"
+ git("fetch", "--prune", new_resource.remote, cwd: cwd)
+ git("fetch", new_resource.remote, "--tags", cwd: cwd)
+ git("reset", "--hard", target_revision, cwd: cwd)
end
end
def setup_remote_tracking_branches(remote_name, remote_url)
converge_by("set up remote tracking branches for #{remote_url} at #{remote_name}") do
- Chef::Log.debug "#{@new_resource} configuring remote tracking branches for repository #{remote_url} " + "at remote #{remote_name}"
- check_remote_command = "git config --get remote.#{remote_name}.url"
- remote_status = shell_out!(check_remote_command, run_options(:cwd => @new_resource.destination, :returns => [0, 1, 2]))
+ logger.trace "#{new_resource} configuring remote tracking branches for repository #{remote_url} " + "at remote #{remote_name}"
+ check_remote_command = ["config", "--get", "remote.#{remote_name}.url"]
+ remote_status = git(check_remote_command, cwd: cwd, returns: [0, 1, 2])
case remote_status.exitstatus
when 0, 2
# * Status 0 means that we already have a remote with this name, so we should update the url
@@ -200,12 +213,10 @@ class Chef
# which we can fix by replacing them all with our target url (hence the --replace-all option)
if multiple_remotes?(remote_status) || !remote_matches?(remote_url, remote_status)
- update_remote_url_command = "git config --replace-all remote.#{remote_name}.url #{remote_url}"
- shell_out!(update_remote_url_command, run_options(:cwd => @new_resource.destination))
+ git("config", "--replace-all", "remote.#{remote_name}.url", remote_url, cwd: cwd)
end
when 1
- add_remote_command = "git remote add #{remote_name} #{remote_url}"
- shell_out!(add_remote_command, run_options(:cwd => @new_resource.destination))
+ git("remote", "add", remote_name, remote_url, cwd: cwd)
end
end
end
@@ -219,13 +230,13 @@ class Chef
end
def current_revision_matches_target_revision?
- (!@current_resource.revision.nil?) && (target_revision.strip.to_i(16) == @current_resource.revision.strip.to_i(16))
+ (!current_resource.revision.nil?) && (target_revision.strip.to_i(16) == current_resource.revision.strip.to_i(16))
end
def target_revision
@target_revision ||= begin
- if sha_hash?(@new_resource.revision)
- @target_revision = @new_resource.revision
+ if sha_hash?(new_resource.revision)
+ @target_revision = new_resource.revision
else
@target_revision = remote_resolve_reference
end
@@ -235,7 +246,7 @@ class Chef
alias :revision_slug :target_revision
def remote_resolve_reference
- Chef::Log.debug("#{@new_resource} resolving remote reference")
+ logger.trace("#{new_resource} resolving remote reference")
# The sha pointed to by an annotated tag is identified by the
# '^{}' suffix appended to the tag. In order to resolve
# annotated tags, we have to search for "revision*" and
@@ -250,11 +261,11 @@ class Chef
# confusing. We avoid the issue by disallowing the use of
# annotated tags named 'HEAD'.
if rev_search_pattern != "HEAD"
- found = find_revision(refs, @new_resource.revision, "^{}")
+ found = find_revision(refs, new_resource.revision, "^{}")
else
found = refs_search(refs, "HEAD")
end
- found = find_revision(refs, @new_resource.revision) if found.empty?
+ found = find_revision(refs, new_resource.revision) if found.empty?
found.size == 1 ? found.first[0] : nil
end
@@ -274,53 +285,57 @@ class Chef
end
def rev_search_pattern
- if ["", "HEAD"].include? @new_resource.revision
+ if ["", "HEAD"].include? new_resource.revision
"HEAD"
else
- @new_resource.revision + "*"
+ new_resource.revision + "*"
end
end
def git_ls_remote(rev_pattern)
- command = git(%Q{ls-remote "#{@new_resource.repository}" "#{rev_pattern}"})
- shell_out!(command, run_options).stdout
+ git("ls-remote", "\"#{new_resource.repository}\"", "\"#{rev_pattern}\"").stdout
end
def refs_search(refs, pattern)
refs.find_all { |m| m[1] == pattern }
end
+ alias git_minor_version git_gem_version
+
private
def run_options(run_opts = {})
env = {}
- if @new_resource.user
- run_opts[:user] = @new_resource.user
+ if new_resource.user
+ run_opts[:user] = new_resource.user
# Certain versions of `git` misbehave if git configuration is
# inaccessible in $HOME. We need to ensure $HOME matches the
# user who is executing `git` not the user running Chef.
env["HOME"] = begin
require "etc"
- Etc.getpwnam(@new_resource.user).dir
+ case new_resource.user
+ when Integer
+ Etc.getpwuid(new_resource.user).dir
+ else
+ Etc.getpwnam(new_resource.user.to_s).dir
+ end
rescue ArgumentError # user not found
- raise Chef::Exceptions::User, "Could not determine HOME for specified user '#{@new_resource.user}' for resource '#{@new_resource.name}'"
+ raise Chef::Exceptions::User, "Could not determine HOME for specified user '#{new_resource.user}' for resource '#{new_resource.name}'"
end
end
- run_opts[:group] = @new_resource.group if @new_resource.group
- env["GIT_SSH"] = @new_resource.ssh_wrapper if @new_resource.ssh_wrapper
- run_opts[:log_tag] = @new_resource.to_s
- run_opts[:timeout] = @new_resource.timeout if @new_resource.timeout
- env.merge!(@new_resource.environment) if @new_resource.environment
+ run_opts[:group] = new_resource.group if new_resource.group
+ env["GIT_SSH"] = new_resource.ssh_wrapper if new_resource.ssh_wrapper
+ run_opts[:log_tag] = new_resource.to_s
+ run_opts[:timeout] = new_resource.timeout if new_resource.timeout
+ env.merge!(new_resource.environment) if new_resource.environment
run_opts[:environment] = env unless env.empty?
run_opts
end
- def cwd
- @new_resource.destination
- end
-
- def git(*args)
- ["git", *args].compact.join(" ")
+ def git(*args, **run_opts)
+ git_command = ["git", args].compact.join(" ")
+ logger.trace "running #{git_command}"
+ shell_out!(git_command, run_options(run_opts))
end
def sha_hash?(string)
diff --git a/lib/chef/provider/group.rb b/lib/chef/provider/group.rb
index 8936bd2031..51e4f280fe 100644
--- a/lib/chef/provider/group.rb
+++ b/lib/chef/provider/group.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,59 +18,53 @@
require "chef/provider"
require "chef/mixin/shell_out"
-require "chef/mixin/command"
require "etc"
class Chef
class Provider
class Group < Chef::Provider
include Chef::Mixin::ShellOut
- include Chef::Mixin::Command
attr_accessor :group_exists
attr_accessor :change_desc
- def whyrun_supported?
- true
- end
-
def initialize(new_resource, run_context)
super
@group_exists = true
end
def load_current_resource
- @current_resource = Chef::Resource::Group.new(@new_resource.name)
- @current_resource.group_name(@new_resource.group_name)
+ @current_resource = Chef::Resource::Group.new(new_resource.name)
+ current_resource.group_name(new_resource.group_name)
group_info = nil
begin
- group_info = Etc.getgrnam(@new_resource.group_name)
- rescue ArgumentError => e
+ group_info = Etc.getgrnam(new_resource.group_name)
+ rescue ArgumentError
@group_exists = false
- Chef::Log.debug("#{@new_resource} group does not exist")
+ logger.trace("#{new_resource} group does not exist")
end
if group_info
- @new_resource.gid(group_info.gid) unless @new_resource.gid
- @current_resource.gid(group_info.gid)
- @current_resource.members(group_info.mem)
+ new_resource.gid(group_info.gid) unless new_resource.gid
+ current_resource.gid(group_info.gid)
+ current_resource.members(group_info.mem)
end
- @current_resource
+ current_resource
end
def define_resource_requirements
requirements.assert(:modify) do |a|
a.assertion { @group_exists }
- a.failure_message(Chef::Exceptions::Group, "Cannot modify #{@new_resource} - group does not exist!")
- a.whyrun("Group #{@new_resource} does not exist. Unless it would have been created earlier in this run, this attempt to modify it would fail.")
+ a.failure_message(Chef::Exceptions::Group, "Cannot modify #{new_resource} - group does not exist!")
+ a.whyrun("Group #{new_resource} does not exist. Unless it would have been created earlier in this run, this attempt to modify it would fail.")
end
requirements.assert(:all_actions) do |a|
# Make sure that the resource doesn't contain any common
# user names in the members and exclude_members properties.
- if !@new_resource.members.nil? && !@new_resource.excluded_members.nil?
- common_members = @new_resource.members & @new_resource.excluded_members
+ if !new_resource.members.nil? && !new_resource.excluded_members.nil?
+ common_members = new_resource.members & new_resource.excluded_members
a.assertion { common_members.empty? }
a.failure_message(Chef::Exceptions::ConflictingMembersInGroup, "Attempting to both add and remove users from a group: '#{common_members.join(', ')}'")
# No why-run alternative
@@ -86,41 +80,39 @@ class Chef
# <false>:: If a change is not required
def compare_group
@change_desc = [ ]
- if @new_resource.gid.to_s != @current_resource.gid.to_s
- @change_desc << "change gid #{@current_resource.gid} to #{@new_resource.gid}"
+ if new_resource.gid.to_s != current_resource.gid.to_s
+ @change_desc << "change gid #{current_resource.gid} to #{new_resource.gid}"
end
- if @new_resource.append
+ if new_resource.append
missing_members = []
- @new_resource.members.each do |member|
+ new_resource.members.each do |member|
next if has_current_group_member?(member)
validate_member!(member)
missing_members << member
end
- if missing_members.length > 0
- @change_desc << "add missing member(s): #{missing_members.join(", ")}"
+ unless missing_members.empty?
+ @change_desc << "add missing member(s): #{missing_members.join(', ')}"
end
members_to_be_removed = []
- @new_resource.excluded_members.each do |member|
+ new_resource.excluded_members.each do |member|
if has_current_group_member?(member)
members_to_be_removed << member
end
end
- if members_to_be_removed.length > 0
- @change_desc << "remove existing member(s): #{members_to_be_removed.join(", ")}"
- end
- else
- if @new_resource.members != @current_resource.members
- @change_desc << "replace group members with new list of members"
+ unless members_to_be_removed.empty?
+ @change_desc << "remove existing member(s): #{members_to_be_removed.join(', ')}"
end
+ elsif new_resource.members != current_resource.members
+ @change_desc << "replace group members with new list of members"
end
!@change_desc.empty?
end
def has_current_group_member?(member)
- @current_resource.members.include?(member)
+ current_resource.members.include?(member)
end
def validate_member!(member)
@@ -132,44 +124,41 @@ class Chef
def action_create
case @group_exists
when false
- converge_by("create group #{@new_resource.group_name}") do
+ converge_by("create group #{new_resource.group_name}") do
create_group
- Chef::Log.info("#{@new_resource} created")
+ logger.info("#{new_resource} created")
end
else
if compare_group
- converge_by(["alter group #{@new_resource.group_name}"] + change_desc) do
+ converge_by(["alter group #{new_resource.group_name}"] + change_desc) do
manage_group
- Chef::Log.info("#{@new_resource} altered")
+ logger.info("#{new_resource} altered")
end
end
end
end
def action_remove
- if @group_exists
- converge_by("remove group #{@new_resource.group_name}") do
- remove_group
- Chef::Log.info("#{@new_resource} removed")
- end
+ return unless @group_exists
+ converge_by("remove group #{new_resource.group_name}") do
+ remove_group
+ logger.info("#{new_resource} removed")
end
end
def action_manage
- if @group_exists && compare_group
- converge_by(["manage group #{@new_resource.group_name}"] + change_desc) do
- manage_group
- Chef::Log.info("#{@new_resource} managed")
- end
+ return unless @group_exists && compare_group
+ converge_by(["manage group #{new_resource.group_name}"] + change_desc) do
+ manage_group
+ logger.info("#{new_resource} managed")
end
end
def action_modify
- if compare_group
- converge_by(["modify group #{@new_resource.group_name}"] + change_desc) do
- manage_group
- Chef::Log.info("#{@new_resource} modified")
- end
+ return unless compare_group
+ converge_by(["modify group #{new_resource.group_name}"] + change_desc) do
+ manage_group
+ logger.info("#{new_resource} modified")
end
end
diff --git a/lib/chef/provider/group/aix.rb b/lib/chef/provider/group/aix.rb
index 4a02d5ef8c..5c500e2753 100644
--- a/lib/chef/provider/group/aix.rb
+++ b/lib/chef/provider/group/aix.rb
@@ -17,7 +17,6 @@
#
require "chef/provider/group/groupadd"
-require "chef/mixin/shell_out"
class Chef
class Provider
@@ -33,48 +32,42 @@ class Chef
end
def create_group
- command = "mkgroup"
- command << set_options << " #{@new_resource.group_name}"
- run_command(:command => command)
+ shell_out_compact!("mkgroup", set_options, new_resource.group_name)
modify_group_members
end
def manage_group
- command = "chgroup"
options = set_options
- #Usage: chgroup [-R load_module] "attr=value" ... group
if options.size > 0
- command << options << " #{@new_resource.group_name}"
- run_command(:command => command)
+ shell_out_compact!("chgroup", options, new_resource.group_name)
end
modify_group_members
end
def remove_group
- run_command(:command => "rmgroup #{@new_resource.group_name}")
+ shell_out_compact!("rmgroup", new_resource.group_name)
end
def add_member(member)
- shell_out!("chgrpmem -m + #{member} #{@new_resource.group_name}")
+ shell_out_compact!("chgrpmem", "-m", "+", member, new_resource.group_name)
end
def set_members(members)
return if members.empty?
- shell_out!("chgrpmem -m = #{members.join(',')} #{@new_resource.group_name}")
+ shell_out_compact!("chgrpmem", "-m", "=", members.join(","), new_resource.group_name)
end
def remove_member(member)
- shell_out!("chgrpmem -m - #{member} #{@new_resource.group_name}")
+ shell_out_compact!("chgrpmem", "-m", "-", member, new_resource.group_name)
end
def set_options
- opts = ""
- { :gid => "id" }.sort { |a, b| a[0] <=> b[0] }.each do |field, option|
- if @current_resource.send(field) != @new_resource.send(field)
- if @new_resource.send(field)
- Chef::Log.debug("#{@new_resource} setting #{field} to #{@new_resource.send(field)}")
- opts << " '#{option}=#{@new_resource.send(field)}'"
- end
+ opts = []
+ { gid: "id" }.sort_by { |a| a[0] }.each do |field, option|
+ next unless current_resource.send(field) != new_resource.send(field)
+ if new_resource.send(field)
+ logger.trace("#{new_resource} setting #{field} to #{new_resource.send(field)}")
+ opts << "#{option}=#{new_resource.send(field)}"
end
end
opts
diff --git a/lib/chef/provider/group/dscl.rb b/lib/chef/provider/group/dscl.rb
index 00b4ce2b93..81c7d66aa8 100644
--- a/lib/chef/provider/group/dscl.rb
+++ b/lib/chef/provider/group/dscl.rb
@@ -24,12 +24,15 @@ class Chef
provides :group, os: "darwin"
def dscl(*args)
- host = "."
- stdout_result = ""; stderr_result = ""; cmd = "dscl #{host} -#{args.join(' ')}"
- status = shell_out(cmd)
+ argdup = args.dup
+ cmd = argdup.shift
+ shellcmd = [ "dscl", ".", "-#{cmd}", argdup ]
+ status = shell_out_compact(shellcmd)
+ stdout_result = ""
+ stderr_result = ""
status.stdout.each_line { |line| stdout_result << line }
status.stderr.each_line { |line| stderr_result << line }
- return [cmd, status, stdout_result, stderr_result]
+ [shellcmd.flatten.compact.join(" "), status, stdout_result, stderr_result]
end
def safe_dscl(*args)
@@ -37,18 +40,18 @@ class Chef
return "" if ( args.first =~ /^delete/ ) && ( result[1].exitstatus != 0 )
raise(Chef::Exceptions::Group, "dscl error: #{result.inspect}") unless result[1].exitstatus == 0
raise(Chef::Exceptions::Group, "dscl error: #{result.inspect}") if result[2] =~ /No such key: /
- return result[2]
+ result[2]
end
def load_current_resource
- @current_resource = Chef::Resource::Group.new(@new_resource.name)
- @current_resource.group_name(@new_resource.group_name)
+ @current_resource = Chef::Resource::Group.new(new_resource.name)
+ current_resource.group_name(new_resource.group_name)
group_info = nil
begin
- group_info = safe_dscl("read /Groups/#{@new_resource.group_name}")
+ group_info = safe_dscl("read", "/Groups/#{new_resource.group_name}")
rescue Chef::Exceptions::Group
@group_exists = false
- Chef::Log.debug("#{@new_resource} group does not exist")
+ logger.trace("#{new_resource} group does not exist")
end
if group_info
@@ -57,21 +60,21 @@ class Chef
val.strip! if val
case key.downcase
when "primarygroupid"
- @new_resource.gid(val) unless @new_resource.gid
- @current_resource.gid(val)
+ new_resource.gid(val) unless new_resource.gid
+ current_resource.gid(val)
when "groupmembership"
- @current_resource.members(val.split(" "))
+ current_resource.members(val.split(" "))
end
end
end
- @current_resource
+ current_resource
end
# get a free GID greater than 200
def get_free_gid(search_limit = 1000)
gid = nil; next_gid_guess = 200
- groups_gids = safe_dscl("list /Groups gid")
+ groups_gids = safe_dscl("list", "/Groups", "gid")
while next_gid_guess < search_limit + 200
if groups_gids =~ Regexp.new("#{Regexp.escape(next_gid_guess.to_s)}\n")
next_gid_guess += 1
@@ -80,51 +83,55 @@ class Chef
break
end
end
- return gid || raise("gid not found. Exhausted. Searched #{search_limit} times")
+ gid || raise("gid not found. Exhausted. Searched #{search_limit} times")
end
def gid_used?(gid)
return false unless gid
- groups_gids = safe_dscl("list /Groups gid")
- !! ( groups_gids =~ Regexp.new("#{Regexp.escape(gid.to_s)}\n") )
+ search_gids = safe_dscl("search", "/Groups", "PrimaryGroupID", gid.to_s)
+
+ # dscl -search should not return anything if the gid doesn't exist,
+ # but on the off-chance that it does, check whether the given gid is
+ # in the output.
+ !!(search_gids =~ /\b#{gid}\b/)
end
def set_gid
- @new_resource.gid(get_free_gid) if [nil, ""].include? @new_resource.gid
- raise(Chef::Exceptions::Group, "gid is already in use") if gid_used?(@new_resource.gid)
- safe_dscl("create /Groups/#{@new_resource.group_name} PrimaryGroupID #{@new_resource.gid}")
+ new_resource.gid(get_free_gid) if [nil, ""].include? new_resource.gid
+ raise(Chef::Exceptions::Group, "gid is already in use") if gid_used?(new_resource.gid)
+ safe_dscl("create", "/Groups/#{new_resource.group_name}", "PrimaryGroupID", new_resource.gid)
end
def set_members
# First reset the memberships if the append is not set
- unless @new_resource.append
- Chef::Log.debug("#{@new_resource} removing group members #{@current_resource.members.join(' ')}") unless @current_resource.members.empty?
- safe_dscl("create /Groups/#{@new_resource.group_name} GroupMembers ''") # clear guid list
- safe_dscl("create /Groups/#{@new_resource.group_name} GroupMembership ''") # clear user list
- @current_resource.members([ ])
+ unless new_resource.append
+ logger.trace("#{new_resource} removing group members #{current_resource.members.join(' ')}") unless current_resource.members.empty?
+ safe_dscl("create", "/Groups/#{new_resource.group_name}", "GroupMembers", "") # clear guid list
+ safe_dscl("create", "/Groups/#{new_resource.group_name}", "GroupMembership", "") # clear user list
+ current_resource.members([ ])
end
# Add any members that need to be added
- if @new_resource.members && !@new_resource.members.empty?
+ if new_resource.members && !new_resource.members.empty?
members_to_be_added = [ ]
- @new_resource.members.each do |member|
- members_to_be_added << member if !@current_resource.members.include?(member)
+ new_resource.members.each do |member|
+ members_to_be_added << member unless current_resource.members.include?(member)
end
unless members_to_be_added.empty?
- Chef::Log.debug("#{@new_resource} setting group members #{members_to_be_added.join(', ')}")
- safe_dscl("append /Groups/#{@new_resource.group_name} GroupMembership #{members_to_be_added.join(' ')}")
+ logger.trace("#{new_resource} setting group members #{members_to_be_added.join(', ')}")
+ safe_dscl("append", "/Groups/#{new_resource.group_name}", "GroupMembership", *members_to_be_added)
end
end
# Remove any members that need to be removed
- if @new_resource.excluded_members && !@new_resource.excluded_members.empty?
+ if new_resource.excluded_members && !new_resource.excluded_members.empty?
members_to_be_removed = [ ]
- @new_resource.excluded_members.each do |member|
- members_to_be_removed << member if @current_resource.members.include?(member)
+ new_resource.excluded_members.each do |member|
+ members_to_be_removed << member if current_resource.members.include?(member)
end
unless members_to_be_removed.empty?
- Chef::Log.debug("#{@new_resource} removing group members #{members_to_be_removed.join(', ')}")
- safe_dscl("delete /Groups/#{@new_resource.group_name} GroupMembership #{members_to_be_removed.join(' ')}")
+ logger.trace("#{new_resource} removing group members #{members_to_be_removed.join(', ')}")
+ safe_dscl("delete", "/Groups/#{new_resource.group_name}", "GroupMembership", *members_to_be_removed)
end
end
end
@@ -132,8 +139,8 @@ class Chef
def define_resource_requirements
super
requirements.assert(:all_actions) do |a|
- a.assertion { ::File.exists?("/usr/bin/dscl") }
- a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/bin/dscl for #{@new_resource.name}"
+ a.assertion { ::File.exist?("/usr/bin/dscl") }
+ a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/bin/dscl for #{new_resource.name}"
# No whyrun alternative: this component should be available in the base install of any given system that uses it
end
end
@@ -145,24 +152,24 @@ class Chef
end
def manage_group
- if @new_resource.group_name && (@current_resource.group_name != @new_resource.group_name)
+ if new_resource.group_name && (current_resource.group_name != new_resource.group_name)
dscl_create_group
end
- if @new_resource.gid && (@current_resource.gid != @new_resource.gid)
+ if new_resource.gid && (current_resource.gid != new_resource.gid)
set_gid
end
- if @new_resource.members || @new_resource.excluded_members
+ if new_resource.members || new_resource.excluded_members
set_members
end
end
def dscl_create_group
- safe_dscl("create /Groups/#{@new_resource.group_name}")
- safe_dscl("create /Groups/#{@new_resource.group_name} Password '*'")
+ safe_dscl("create", "/Groups/#{new_resource.group_name}")
+ safe_dscl("create", "/Groups/#{new_resource.group_name}", "Password", "*")
end
def remove_group
- safe_dscl("delete /Groups/#{@new_resource.group_name}")
+ safe_dscl("delete", "/Groups/#{new_resource.group_name}")
end
end
end
diff --git a/lib/chef/provider/group/gpasswd.rb b/lib/chef/provider/group/gpasswd.rb
index dcf526b211..d8aff10d5b 100644
--- a/lib/chef/provider/group/gpasswd.rb
+++ b/lib/chef/provider/group/gpasswd.rb
@@ -31,26 +31,26 @@ class Chef
def define_resource_requirements
super
requirements.assert(:all_actions) do |a|
- a.assertion { ::File.exists?("/usr/bin/gpasswd") }
- a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/bin/gpasswd for #{@new_resource}"
+ a.assertion { ::File.exist?("/usr/bin/gpasswd") }
+ a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/bin/gpasswd for #{new_resource}"
# No whyrun alternative: this component should be available in the base install of any given system that uses it
end
end
def set_members(members)
- unless members.empty?
- shell_out!("gpasswd -M #{members.join(',')} #{@new_resource.group_name}")
+ if members.empty?
+ shell_out_compact!("gpasswd", "-M", "", new_resource.group_name)
else
- shell_out!("gpasswd -M \"\" #{@new_resource.group_name}")
+ shell_out_compact!("gpasswd", "-M", members.join(","), new_resource.group_name)
end
end
def add_member(member)
- shell_out!("gpasswd -a #{member} #{@new_resource.group_name}")
+ shell_out_compact!("gpasswd", "-a", member, new_resource.group_name)
end
def remove_member(member)
- shell_out!("gpasswd -d #{member} #{@new_resource.group_name}")
+ shell_out_compact!("gpasswd", "-d", member, new_resource.group_name)
end
end
end
diff --git a/lib/chef/provider/group/groupadd.rb b/lib/chef/provider/group/groupadd.rb
index bc6b5d0208..7d7fac146c 100644
--- a/lib/chef/provider/group/groupadd.rb
+++ b/lib/chef/provider/group/groupadd.rb
@@ -35,8 +35,8 @@ class Chef
super
required_binaries.each do |required_binary|
requirements.assert(:all_actions) do |a|
- a.assertion { ::File.exists?(required_binary) }
- a.failure_message Chef::Exceptions::Group, "Could not find binary #{required_binary} for #{@new_resource}"
+ a.assertion { ::File.exist?(required_binary) }
+ a.failure_message Chef::Exceptions::Group, "Could not find binary #{required_binary} for #{new_resource}"
# No whyrun alternative: this component should be available in the base install of any given system that uses it
end
end
@@ -44,54 +44,49 @@ class Chef
# Create the group
def create_group
- command = "groupadd"
- command << set_options
- command << groupadd_options
- run_command(:command => command)
+ shell_out_compact!("groupadd", set_options, groupadd_options)
modify_group_members
end
# Manage the group when it already exists
def manage_group
- command = "groupmod"
- command << set_options
- run_command(:command => command)
+ shell_out_compact!("groupmod", set_options)
modify_group_members
end
# Remove the group
def remove_group
- run_command(:command => "groupdel #{@new_resource.group_name}")
+ shell_out_compact!("groupdel", new_resource.group_name)
end
def modify_group_members
- if @new_resource.append
- if @new_resource.members && !@new_resource.members.empty?
+ if new_resource.append
+ if new_resource.members && !new_resource.members.empty?
members_to_be_added = [ ]
- @new_resource.members.each do |member|
- members_to_be_added << member if !@current_resource.members.include?(member)
+ new_resource.members.each do |member|
+ members_to_be_added << member unless current_resource.members.include?(member)
end
members_to_be_added.each do |member|
- Chef::Log.debug("#{@new_resource} appending member #{member} to group #{@new_resource.group_name}")
+ logger.trace("#{new_resource} appending member #{member} to group #{new_resource.group_name}")
add_member(member)
end
end
- if @new_resource.excluded_members && !@new_resource.excluded_members.empty?
+ if new_resource.excluded_members && !new_resource.excluded_members.empty?
members_to_be_removed = [ ]
- @new_resource.excluded_members.each do |member|
- members_to_be_removed << member if @current_resource.members.include?(member)
+ new_resource.excluded_members.each do |member|
+ members_to_be_removed << member if current_resource.members.include?(member)
end
members_to_be_removed.each do |member|
- Chef::Log.debug("#{@new_resource} removing member #{member} from group #{@new_resource.group_name}")
+ logger.trace("#{new_resource} removing member #{member} from group #{new_resource.group_name}")
remove_member(member)
end
end
else
- members_description = @new_resource.members.empty? ? "none" : @new_resource.members.join(", ")
- Chef::Log.debug("#{@new_resource} setting group members to: #{members_description}")
- set_members(@new_resource.members)
+ members_description = new_resource.members.empty? ? "none" : new_resource.members.join(", ")
+ logger.trace("#{new_resource} setting group members to: #{members_description}")
+ set_members(new_resource.members)
end
end
@@ -112,22 +107,23 @@ class Chef
# ==== Returns
# <string>:: A string containing the option and then the quoted value
def set_options
- opts = ""
- { :gid => "-g" }.sort { |a, b| a[0] <=> b[0] }.each do |field, option|
- if @current_resource.send(field) != @new_resource.send(field)
- if @new_resource.send(field)
- opts << " #{option} '#{@new_resource.send(field)}'"
- Chef::Log.debug("#{@new_resource} set #{field} to #{@new_resource.send(field)}")
- end
- end
+ opts = []
+ { gid: "-g" }.sort_by { |a| a[0] }.each do |field, option|
+ next unless current_resource.send(field) != new_resource.send(field)
+ next unless new_resource.send(field)
+ opts << option
+ opts << new_resource.send(field)
+ logger.trace("#{new_resource} set #{field} to #{new_resource.send(field)}")
end
- opts << " #{@new_resource.group_name}"
+ opts << new_resource.group_name
+ opts
end
def groupadd_options
- opts = ""
- opts << " -r" if @new_resource.system
- opts << " -o" if @new_resource.non_unique
+ opts = []
+ # Solaris doesn't support system groups.
+ opts << "-r" if new_resource.system && !node.platform?("solaris2")
+ opts << "-o" if new_resource.non_unique
opts
end
diff --git a/lib/chef/provider/group/groupmod.rb b/lib/chef/provider/group/groupmod.rb
index 295d3b3425..13f83db4c4 100644
--- a/lib/chef/provider/group/groupmod.rb
+++ b/lib/chef/provider/group/groupmod.rb
@@ -26,28 +26,26 @@ class Chef
def load_current_resource
super
%w{group user}.each do |binary|
- raise Chef::Exceptions::Group, "Could not find binary /usr/sbin/#{binary} for #{@new_resource}" unless ::File.exists?("/usr/sbin/#{binary}")
+ raise Chef::Exceptions::Group, "Could not find binary /usr/sbin/#{binary} for #{new_resource}" unless ::File.exist?("/usr/sbin/#{binary}")
end
end
# Create the group
def create_group
- command = "group add"
- command << set_options
- shell_out!(command)
+ shell_out_compact!("group", "add", set_options)
- add_group_members(@new_resource.members)
+ add_group_members(new_resource.members)
end
# Manage the group when it already exists
def manage_group
- if @new_resource.append
+ if new_resource.append
members_to_be_added = [ ]
- if @new_resource.excluded_members && !@new_resource.excluded_members.empty?
+ if new_resource.excluded_members && !new_resource.excluded_members.empty?
# First find out if any member needs to be removed
members_to_be_removed = [ ]
- @new_resource.excluded_members.each do |member|
- members_to_be_removed << member if @current_resource.members.include?(member)
+ new_resource.excluded_members.each do |member|
+ members_to_be_removed << member if current_resource.members.include?(member)
end
unless members_to_be_removed.empty?
@@ -56,39 +54,39 @@ class Chef
# Capture the members we need to add in
# members_to_be_added to be added later on.
- @current_resource.members.each do |member|
+ current_resource.members.each do |member|
members_to_be_added << member unless members_to_be_removed.include?(member)
end
end
end
- if @new_resource.members && !@new_resource.members.empty?
- @new_resource.members.each do |member|
- members_to_be_added << member if !@current_resource.members.include?(member)
+ if new_resource.members && !new_resource.members.empty?
+ new_resource.members.each do |member|
+ members_to_be_added << member unless current_resource.members.include?(member)
end
end
- Chef::Log.debug("#{@new_resource} not changing group members, the group has no members to add") if members_to_be_added.empty?
+ logger.trace("#{new_resource} not changing group members, the group has no members to add") if members_to_be_added.empty?
add_group_members(members_to_be_added)
else
# We are resetting the members of a group so use the same trick
reset_group_membership
- Chef::Log.debug("#{@new_resource} setting group members to: none") if @new_resource.members.empty?
- add_group_members(@new_resource.members)
+ logger.trace("#{new_resource} setting group members to: none") if new_resource.members.empty?
+ add_group_members(new_resource.members)
end
end
# Remove the group
def remove_group
- shell_out!("group del #{@new_resource.group_name}")
+ shell_out_compact!("group", "del", new_resource.group_name)
end
# Adds a list of usernames to the group using `user mod`
def add_group_members(members)
- Chef::Log.debug("#{@new_resource} adding members #{members.join(', ')}") if !members.empty?
+ logger.trace("#{new_resource} adding members #{members.join(', ')}") unless members.empty?
members.each do |user|
- shell_out!("user mod -G #{@new_resource.group_name} #{user}")
+ shell_out_compact!("user", "mod", "-G", new_resource.group_name, user)
end
end
@@ -96,15 +94,11 @@ class Chef
# "<name>_bak", create a new group with the same GID and
# "<name>", then set correct members on that group
def reset_group_membership
- rename = "group mod -n #{@new_resource.group_name}_bak #{@new_resource.group_name}"
- shell_out!(rename)
+ shell_out_compact!("group", "mod", "-n", "#{new_resource.group_name}_bak", new_resource.group_name)
- create = "group add"
- create << set_options(:overwrite_gid => true)
- shell_out!(create)
+ shell_out_compact!("group", "add", set_options(overwrite_gid: true))
- remove = "group del #{@new_resource.group_name}_bak"
- shell_out!(remove)
+ shell_out_compact!("group", "del", "#{new_resource.group_name}_bak")
end
# Little bit of magic as per Adam's useradd provider to pull and assign the command line flags
@@ -112,14 +106,15 @@ class Chef
# ==== Returns
# <string>:: A string containing the option and then the quoted value
def set_options(overwrite_gid = false)
- opts = ""
- if overwrite_gid || @new_resource.gid && (@current_resource.gid != @new_resource.gid)
- opts << " -g '#{@new_resource.gid}'"
+ opts = []
+ if overwrite_gid || new_resource.gid && (current_resource.gid != new_resource.gid)
+ opts << "-g"
+ opts << new_resource.gid
end
if overwrite_gid
- opts << " -o"
+ opts << "-o"
end
- opts << " #{@new_resource.group_name}"
+ opts << new_resource.group_name
opts
end
end
diff --git a/lib/chef/provider/group/pw.rb b/lib/chef/provider/group/pw.rb
index 4fd78b6b31..b0393a147e 100644
--- a/lib/chef/provider/group/pw.rb
+++ b/lib/chef/provider/group/pw.rb
@@ -30,46 +30,42 @@ class Chef
super
requirements.assert(:all_actions) do |a|
- a.assertion { ::File.exists?("/usr/sbin/pw") }
- a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/sbin/pw for #{@new_resource}"
+ a.assertion { ::File.exist?("/usr/sbin/pw") }
+ a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/sbin/pw for #{new_resource}"
# No whyrun alternative: this component should be available in the base install of any given system that uses it
end
end
# Create the group
def create_group
- command = "pw groupadd"
- command << set_options
-
- unless @new_resource.members.empty?
+ command = [ "pw", "groupadd", set_options ]
+ unless new_resource.members.empty?
# pw group[add|mod] -M is used to set the full membership list on a
# new or existing group. Because pw groupadd does not support the -m
# and -d options used by manage_group, we treat group creation as a
# special case and use -M.
- Chef::Log.debug("#{@new_resource} setting group members: #{@new_resource.members.join(',')}")
- command += " -M #{@new_resource.members.join(',')}"
+ logger.trace("#{new_resource} setting group members: #{new_resource.members.join(',')}")
+ command += [ "-M", new_resource.members.join(",") ]
end
- run_command(:command => command)
+ shell_out_compact!(command)
end
# Manage the group when it already exists
def manage_group
- command = "pw groupmod"
- command << set_options
member_options = set_members_options
if member_options.empty?
- run_command(:command => command)
+ shell_out_compact!("pw", "groupmod", set_options)
else
member_options.each do |option|
- run_command(:command => command + option)
+ shell_out_compact!("pw", "groupmod", set_options, option)
end
end
end
# Remove the group
def remove_group
- run_command(:command => "pw groupdel #{@new_resource.group_name}")
+ shell_out_compact!("pw", "groupdel", new_resource.group_name)
end
# Little bit of magic as per Adam's useradd provider to pull and assign the command line flags
@@ -77,10 +73,11 @@ class Chef
# ==== Returns
# <string>:: A string containing the option and then the quoted value
def set_options
- opts = " #{@new_resource.group_name}"
- if @new_resource.gid && (@current_resource.gid != @new_resource.gid)
- Chef::Log.debug("#{@new_resource}: current gid (#{@current_resource.gid}) doesnt match target gid (#{@new_resource.gid}), changing it")
- opts << " -g '#{@new_resource.gid}'"
+ opts = [ new_resource.group_name ]
+ if new_resource.gid && (current_resource.gid != new_resource.gid)
+ logger.trace("#{new_resource}: current gid (#{current_resource.gid}) doesnt match target gid (#{new_resource.gid}), changing it")
+ opts << "-g"
+ opts << new_resource.gid
end
opts
end
@@ -91,26 +88,26 @@ class Chef
members_to_be_added = [ ]
members_to_be_removed = [ ]
- if @new_resource.append
+ if new_resource.append
# Append is set so we will only add members given in the
# members list and remove members given in the
# excluded_members list.
- if @new_resource.members && !@new_resource.members.empty?
- @new_resource.members.each do |member|
- members_to_be_added << member if !@current_resource.members.include?(member)
+ if new_resource.members && !new_resource.members.empty?
+ new_resource.members.each do |member|
+ members_to_be_added << member unless current_resource.members.include?(member)
end
end
- if @new_resource.excluded_members && !@new_resource.excluded_members.empty?
- @new_resource.excluded_members.each do |member|
- members_to_be_removed << member if @current_resource.members.include?(member)
+ if new_resource.excluded_members && !new_resource.excluded_members.empty?
+ new_resource.excluded_members.each do |member|
+ members_to_be_removed << member if current_resource.members.include?(member)
end
end
else
# Append is not set so we're resetting the membership of
# the group to the given members.
- members_to_be_added = @new_resource.members.dup
- @current_resource.members.each do |member|
+ members_to_be_added = new_resource.members.dup
+ current_resource.members.each do |member|
# No need to re-add a member if it's present in the new
# list of members
if members_to_be_added.include? member
@@ -122,13 +119,13 @@ class Chef
end
unless members_to_be_added.empty?
- Chef::Log.debug("#{@new_resource} adding group members: #{members_to_be_added.join(',')}")
- opts << " -m #{members_to_be_added.join(',')}"
+ logger.trace("#{new_resource} adding group members: #{members_to_be_added.join(',')}")
+ opts << [ "-m", members_to_be_added.join(",") ]
end
unless members_to_be_removed.empty?
- Chef::Log.debug("#{@new_resource} removing group members: #{members_to_be_removed.join(',')}")
- opts << " -d #{members_to_be_removed.join(',')}"
+ logger.trace("#{new_resource} removing group members: #{members_to_be_removed.join(',')}")
+ opts << [ "-d", members_to_be_removed.join(",") ]
end
opts
diff --git a/lib/chef/provider/group/suse.rb b/lib/chef/provider/group/suse.rb
index a79038e25e..0790d2c2d9 100644
--- a/lib/chef/provider/group/suse.rb
+++ b/lib/chef/provider/group/suse.rb
@@ -17,6 +17,7 @@
#
require "chef/provider/group/groupadd"
+require "etc"
class Chef
class Provider
@@ -32,30 +33,48 @@ class Chef
def define_resource_requirements
super
requirements.assert(:all_actions) do |a|
- a.assertion { ::File.exists?("/usr/sbin/groupmod") }
- a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/sbin/groupmod for #{@new_resource.name}"
+ a.assertion { ::File.exist?("/usr/sbin/groupmod") }
+ a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/sbin/groupmod for #{new_resource.name}"
# No whyrun alternative: this component should be available in the base install of any given system that uses it
end
+
+ requirements.assert(:create, :manage, :modify) do |a|
+ a.assertion do
+ begin
+ to_add(new_resource.members).all? { |member| Etc.getpwnam(member) }
+ rescue
+ false
+ end
+ end
+ a.failure_message Chef::Exceptions::Group, "Could not add users #{to_add(new_resource.members).join(', ')} to #{new_resource.group_name}: one of these users does not exist"
+ a.whyrun "Could not find one of these users: #{to_add(new_resource.members).join(', ')}. Assuming it will be created by a prior step"
+ end
end
def set_members(members)
- to_delete = @current_resource.members - members
- to_delete.each do |member|
+ to_remove(members).each do |member|
remove_member(member)
end
- to_add = members - @current_resource.members
- to_add.each do |member|
+ to_add(members).each do |member|
add_member(member)
end
end
+ def to_add(members)
+ members - current_resource.members
+ end
+
def add_member(member)
- shell_out!("groupmod -A #{member} #{@new_resource.group_name}")
+ shell_out_compact!("groupmod", "-A", member, new_resource.group_name)
+ end
+
+ def to_remove(members)
+ current_resource.members - members
end
def remove_member(member)
- shell_out!("groupmod -R #{member} #{@new_resource.group_name}")
+ shell_out_compact!("groupmod", "-R", member, new_resource.group_name)
end
end
diff --git a/lib/chef/provider/group/usermod.rb b/lib/chef/provider/group/usermod.rb
index e4b19181aa..3874f7b4de 100644
--- a/lib/chef/provider/group/usermod.rb
+++ b/lib/chef/provider/group/usermod.rb
@@ -34,19 +34,19 @@ class Chef
super
requirements.assert(:all_actions) do |a|
- a.assertion { ::File.exists?("/usr/sbin/usermod") }
- a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/sbin/usermod for #{@new_resource}"
+ a.assertion { ::File.exist?("/usr/sbin/usermod") }
+ a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/sbin/usermod for #{new_resource}"
# No whyrun alternative: this component should be available in the base install of any given system that uses it
end
requirements.assert(:modify, :manage) do |a|
- a.assertion { @new_resource.members.empty? || @new_resource.append }
+ a.assertion { new_resource.members.empty? || new_resource.append }
a.failure_message Chef::Exceptions::Group, "setting group members directly is not supported by #{self}, must set append true in group"
# No whyrun alternative - this action is simply not supported.
end
requirements.assert(:all_actions) do |a|
- a.assertion { @new_resource.excluded_members.empty? }
+ a.assertion { new_resource.excluded_members.empty? }
a.failure_message Chef::Exceptions::Group, "excluded_members is not supported by #{self}"
# No whyrun alternative - this action is simply not supported.
end
@@ -57,17 +57,16 @@ class Chef
# This provider only supports adding members with
# append. Only if the action is create we will go
# ahead and add members.
- if @new_resource.action == :create
- members.each do |member|
- add_member(member)
- end
- else
+ unless new_resource.action.include?(:create)
raise Chef::Exceptions::UnsupportedAction, "Setting members directly is not supported by #{self}"
end
+ members.each do |member|
+ add_member(member)
+ end
end
def add_member(member)
- shell_out!("usermod #{append_flags} #{@new_resource.group_name} #{member}")
+ shell_out_compact!("usermod", append_flags, new_resource.group_name, member)
end
def remove_member(member)
@@ -81,7 +80,7 @@ class Chef
when "openbsd", "netbsd", "aix", "solaris2", "smartos", "omnios"
"-G"
when "solaris", "suse", "opensuse"
- "-a -G"
+ [ "-a", "-G" ]
end
end
diff --git a/lib/chef/provider/group/windows.rb b/lib/chef/provider/group/windows.rb
index 5873e42a6b..e0cb3b804b 100644
--- a/lib/chef/provider/group/windows.rb
+++ b/lib/chef/provider/group/windows.rb
@@ -30,26 +30,26 @@ class Chef
def initialize(new_resource, run_context)
super
- @net_group = Chef::Util::Windows::NetGroup.new(@new_resource.group_name)
+ @net_group = Chef::Util::Windows::NetGroup.new(new_resource.group_name)
end
def load_current_resource
- @current_resource = Chef::Resource::Group.new(@new_resource.name)
- @current_resource.group_name(@new_resource.group_name)
+ @current_resource = Chef::Resource::Group.new(new_resource.name)
+ current_resource.group_name(new_resource.group_name)
members = nil
begin
members = @net_group.local_get_members
- rescue => e
+ rescue
@group_exists = false
- Chef::Log.debug("#{@new_resource} group does not exist")
+ logger.trace("#{new_resource} group does not exist")
end
if members
- @current_resource.members(members)
+ current_resource.members(members)
end
- @current_resource
+ current_resource
end
def create_group
@@ -58,10 +58,10 @@ class Chef
end
def manage_group
- if @new_resource.append
+ if new_resource.append
members_to_be_added = [ ]
- @new_resource.members.each do |member|
- members_to_be_added << member if ! has_current_group_member?(member) && validate_member!(member)
+ new_resource.members.each do |member|
+ members_to_be_added << member if !has_current_group_member?(member) && validate_member!(member)
end
# local_add_members will raise ERROR_MEMBER_IN_ALIAS if a
@@ -69,19 +69,19 @@ class Chef
@net_group.local_add_members(members_to_be_added) unless members_to_be_added.empty?
members_to_be_removed = [ ]
- @new_resource.excluded_members.each do |member|
- member_sid = lookup_account_name(member)
+ new_resource.excluded_members.each do |member|
+ lookup_account_name(member)
members_to_be_removed << member if has_current_group_member?(member)
end
@net_group.local_delete_members(members_to_be_removed) unless members_to_be_removed.empty?
else
- @net_group.local_set_members(@new_resource.members)
+ @net_group.local_set_members(new_resource.members)
end
end
def has_current_group_member?(member)
member_sid = lookup_account_name(member)
- @current_resource.members.include?(member_sid)
+ current_resource.members.include?(member_sid)
end
def remove_group
@@ -97,12 +97,10 @@ class Chef
end
def lookup_account_name(account_name)
- begin
- Chef::ReservedNames::Win32::Security.lookup_account_name(locally_qualified_name(account_name))[1].to_s
- rescue Chef::Exceptions::Win32APIError
- Chef::Log.warn("SID for '#{locally_qualified_name(account_name)}' could not be found")
- ""
- end
+ Chef::ReservedNames::Win32::Security.lookup_account_name(locally_qualified_name(account_name))[1].to_s
+ rescue Chef::Exceptions::Win32APIError
+ logger.warn("SID for '#{locally_qualified_name(account_name)}' could not be found")
+ ""
end
end
diff --git a/lib/chef/provider/http_request.rb b/lib/chef/provider/http_request.rb
index e1ee01d9b4..885d473a7a 100644
--- a/lib/chef/provider/http_request.rb
+++ b/lib/chef/provider/http_request.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,83 +27,93 @@ class Chef
attr_accessor :http
- def whyrun_supported?
- true
- end
-
def load_current_resource
- @http = Chef::HTTP::Simple.new(@new_resource.url)
+ @http = Chef::HTTP::Simple.new(new_resource.url)
end
- # Send a HEAD request to @new_resource.url
+ # Send a HEAD request to new_resource.url
def action_head
- message = check_message(@new_resource.message)
+ message = check_message(new_resource.message)
# CHEF-4762: we expect a nil return value from Chef::HTTP for a "200 Success" response
# and false for a "304 Not Modified" response
modified = @http.head(
- "#{@new_resource.url}",
- @new_resource.headers
+ "#{new_resource.url}",
+ new_resource.headers
)
- Chef::Log.info("#{@new_resource} HEAD to #{@new_resource.url} successful")
- Chef::Log.debug("#{@new_resource} HEAD request response: #{modified}")
+ logger.info("#{new_resource} HEAD to #{new_resource.url} successful")
+ logger.trace("#{new_resource} HEAD request response: #{modified}")
# :head is usually used to trigger notifications, which converge_by now does
if modified != false
- converge_by("#{@new_resource} HEAD to #{@new_resource.url} returned modified, trigger notifications") {}
+ converge_by("#{new_resource} HEAD to #{new_resource.url} returned modified, trigger notifications") {}
end
end
- # Send a GET request to @new_resource.url
+ # Send a GET request to new_resource.url
def action_get
- converge_by("#{@new_resource} GET to #{@new_resource.url}") do
+ converge_by("#{new_resource} GET to #{new_resource.url}") do
- message = check_message(@new_resource.message)
+ message = check_message(new_resource.message)
body = @http.get(
- "#{@new_resource.url}",
- @new_resource.headers
+ "#{new_resource.url}",
+ new_resource.headers
+ )
+ logger.info("#{new_resource} GET to #{new_resource.url} successful")
+ logger.trace("#{new_resource} GET request response: #{body}")
+ end
+ end
+
+ # Send a PATCH request to new_resource.url, with the message as the payload
+ def action_patch
+ converge_by("#{new_resource} PATCH to #{new_resource.url}") do
+ message = check_message(new_resource.message)
+ body = @http.patch(
+ "#{new_resource.url}",
+ message,
+ new_resource.headers
)
- Chef::Log.info("#{@new_resource} GET to #{@new_resource.url} successful")
- Chef::Log.debug("#{@new_resource} GET request response: #{body}")
+ logger.info("#{new_resource} PATCH to #{new_resource.url} successful")
+ logger.trace("#{new_resource} PATCH request response: #{body}")
end
end
- # Send a PUT request to @new_resource.url, with the message as the payload
+ # Send a PUT request to new_resource.url, with the message as the payload
def action_put
- converge_by("#{@new_resource} PUT to #{@new_resource.url}") do
- message = check_message(@new_resource.message)
+ converge_by("#{new_resource} PUT to #{new_resource.url}") do
+ message = check_message(new_resource.message)
body = @http.put(
- "#{@new_resource.url}",
+ "#{new_resource.url}",
message,
- @new_resource.headers
+ new_resource.headers
)
- Chef::Log.info("#{@new_resource} PUT to #{@new_resource.url} successful")
- Chef::Log.debug("#{@new_resource} PUT request response: #{body}")
+ logger.info("#{new_resource} PUT to #{new_resource.url} successful")
+ logger.trace("#{new_resource} PUT request response: #{body}")
end
end
- # Send a POST request to @new_resource.url, with the message as the payload
+ # Send a POST request to new_resource.url, with the message as the payload
def action_post
- converge_by("#{@new_resource} POST to #{@new_resource.url}") do
- message = check_message(@new_resource.message)
+ converge_by("#{new_resource} POST to #{new_resource.url}") do
+ message = check_message(new_resource.message)
body = @http.post(
- "#{@new_resource.url}",
+ "#{new_resource.url}",
message,
- @new_resource.headers
+ new_resource.headers
)
- Chef::Log.info("#{@new_resource} POST to #{@new_resource.url} message: #{message.inspect} successful")
- Chef::Log.debug("#{@new_resource} POST request response: #{body}")
+ logger.info("#{new_resource} POST to #{new_resource.url} message: #{message.inspect} successful")
+ logger.trace("#{new_resource} POST request response: #{body}")
end
end
- # Send a DELETE request to @new_resource.url
+ # Send a DELETE request to new_resource.url
def action_delete
- converge_by("#{@new_resource} DELETE to #{@new_resource.url}") do
+ converge_by("#{new_resource} DELETE to #{new_resource.url}") do
body = @http.delete(
- "#{@new_resource.url}",
- @new_resource.headers
+ "#{new_resource.url}",
+ new_resource.headers
)
- @new_resource.updated_by_last_action(true)
- Chef::Log.info("#{@new_resource} DELETE to #{@new_resource.url} successful")
- Chef::Log.debug("#{@new_resource} DELETE request response: #{body}")
+ new_resource.updated_by_last_action(true)
+ logger.info("#{new_resource} DELETE to #{new_resource.url} successful")
+ logger.trace("#{new_resource} DELETE request response: #{body}")
end
end
diff --git a/lib/chef/provider/ifconfig.rb b/lib/chef/provider/ifconfig.rb
index 4cfb257bb9..243c8ee9c3 100644
--- a/lib/chef/provider/ifconfig.rb
+++ b/lib/chef/provider/ifconfig.rb
@@ -17,32 +17,24 @@
#
require "chef/log"
-require "chef/mixin/command"
require "chef/mixin/shell_out"
require "chef/provider"
require "chef/resource/file"
require "chef/exceptions"
require "erb"
-# Recipe example:
-#
-# int = {Hash with your network settings...}
-#
-# ifconfig int['ip'] do
-# ignore_failure true
-# device int['dev']
-# mask int['mask']
-# gateway int['gateway']
-# mtu int['mtu']
-# end
-
class Chef
class Provider
+ # use the ifconfig resource to manage interfaces on *nix systems
+ #
+ # @example set a static ip on eth1
+ # ifconfig '33.33.33.80' do
+ # device 'eth1'
+ # end
class Ifconfig < Chef::Provider
provides :ifconfig
include Chef::Mixin::ShellOut
- include Chef::Mixin::Command
attr_accessor :config_template
attr_accessor :config_path
@@ -53,43 +45,106 @@ class Chef
@config_path = nil
end
- def whyrun_supported?
- true
- end
-
def load_current_resource
- @current_resource = Chef::Resource::Ifconfig.new(@new_resource.name)
+ @current_resource = Chef::Resource::Ifconfig.new(new_resource.name)
@ifconfig_success = true
@interfaces = {}
- @status = shell_out("ifconfig")
- @status.stdout.each_line do |line|
- if !line[0..9].strip.empty?
- @int_name = line[0..9].strip
- @interfaces[@int_name] = { "hwaddr" => (line =~ /(HWaddr)/ ? ($') : "nil").strip.chomp }
- else
- @interfaces[@int_name]["inet_addr"] = (line =~ /inet addr:(\S+)/ ? ($1) : "nil") if line =~ /inet addr:/
- @interfaces[@int_name]["bcast"] = (line =~ /Bcast:(\S+)/ ? ($1) : "nil") if line =~ /Bcast:/
- @interfaces[@int_name]["mask"] = (line =~ /Mask:(\S+)/ ? ($1) : "nil") if line =~ /Mask:/
- @interfaces[@int_name]["mtu"] = (line =~ /MTU:(\S+)/ ? ($1) : "nil") if line =~ /MTU:/
- @interfaces[@int_name]["metric"] = (line =~ /Metric:(\S+)/ ? ($1) : "nil") if line =~ /Metric:/
+ @ifconfig_version = nil
+
+ @net_tools_version = shell_out("ifconfig --version")
+ @net_tools_version.stderr.each_line do |line|
+ if line =~ /^net-tools (\d+.\d+)/
+ @ifconfig_version = line.match(/^net-tools (\d+.\d+)/)[1]
+ end
+ end
+
+ if @ifconfig_version.nil?
+ raise "net-tools not found - this is required for ifconfig"
+ elsif @ifconfig_version.to_f < 2.0
+ # Example output for 1.60 is as follows: (sanitized but format intact)
+ # eth0 Link encap:Ethernet HWaddr 00:00:00:00:00:00
+ # inet addr:192.168.1.1 Bcast:192.168.0.1 Mask:255.255.248.0
+ # inet6 addr: 0000::00:0000:0000:0000/64 Scope:Link
+ # UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
+ # RX packets:65158911 errors:0 dropped:0 overruns:0 frame:0
+ # TX packets:41723949 errors:0 dropped:0 overruns:0 carrier:0
+ # collisions:0 txqueuelen:1000
+ # RX bytes:42664658792 (39.7 GiB) TX bytes:52722603938 (49.1 GiB)
+ # Interrupt:30
+ @status = shell_out("ifconfig")
+ @status.stdout.each_line do |line|
+ if !line[0..9].strip.empty?
+ @int_name = line[0..9].strip
+ @interfaces[@int_name] = { "hwaddr" => (line =~ /(HWaddr)/ ? ($') : "nil").strip.chomp }
+ else
+ @interfaces[@int_name]["inet_addr"] = (line =~ /inet addr:(\S+)/ ? Regexp.last_match(1) : "nil") if line =~ /inet addr:/
+ @interfaces[@int_name]["bcast"] = (line =~ /Bcast:(\S+)/ ? Regexp.last_match(1) : "nil") if line =~ /Bcast:/
+ @interfaces[@int_name]["mask"] = (line =~ /Mask:(\S+)/ ? Regexp.last_match(1) : "nil") if line =~ /Mask:/
+ @interfaces[@int_name]["mtu"] = (line =~ /MTU:(\S+)/ ? Regexp.last_match(1) : "nil") if line =~ /MTU:/
+ @interfaces[@int_name]["metric"] = (line =~ /Metric:(\S+)/ ? Regexp.last_match(1) : "nil") if line =~ /Metric:/
+ end
+
+ next unless @interfaces.key?(new_resource.device)
+ @interface = @interfaces.fetch(new_resource.device)
+
+ current_resource.target(new_resource.target)
+ current_resource.device(new_resource.device)
+ current_resource.inet_addr(@interface["inet_addr"])
+ current_resource.hwaddr(@interface["hwaddr"])
+ current_resource.bcast(@interface["bcast"])
+ current_resource.mask(@interface["mask"])
+ current_resource.mtu(@interface["mtu"])
+ current_resource.metric(@interface["metric"])
end
+ elsif @ifconfig_version.to_f >= 2.0
+ # Example output for 2.10-alpha is as follows: (sanitized but format intact)
+ # eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
+ # inet 192.168.1.1 netmask 255.255.240.0 broadcast 192.168.0.1
+ # inet6 0000::0000:000:0000:0000 prefixlen 64 scopeid 0x20<link>
+ # ether 00:00:00:00:00:00 txqueuelen 1000 (Ethernet)
+ # RX packets 2383836 bytes 1642630840 (1.5 GiB)
+ # RX errors 0 dropped 0 overruns 0 frame 0
+ # TX packets 1244218 bytes 977339327 (932.0 MiB)
+ # TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
+ @status = shell_out("ifconfig")
+ @status.stdout.each_line do |line|
+ addr_regex = /^(\w+):?(\d*):?\ .+$/
+ if line =~ addr_regex
+ if line.match(addr_regex).nil?
+ @int_name = "nil"
+ elsif line.match(addr_regex)[2] == ""
+ @int_name = line.match(addr_regex)[1]
+ @interfaces[@int_name] = Hash.new
+ @interfaces[@int_name]["mtu"] = (line =~ /mtu (\S+)/ ? Regexp.last_match(1) : "nil") if line =~ /mtu/ && @interfaces[@int_name]["mtu"].nil?
+ else
+ @int_name = "#{line.match(addr_regex)[1]}:#{line.match(addr_regex)[2]}"
+ @interfaces[@int_name] = Hash.new
+ @interfaces[@int_name]["mtu"] = (line =~ /mtu (\S+)/ ? Regexp.last_match(1) : "nil") if line =~ /mtu/ && @interfaces[@int_name]["mtu"].nil?
+ end
+ else
+ @interfaces[@int_name]["inet_addr"] = (line =~ /inet (\S+)/ ? Regexp.last_match(1) : "nil") if line =~ /inet/ && @interfaces[@int_name]["inet_addr"].nil?
+ @interfaces[@int_name]["bcast"] = (line =~ /broadcast (\S+)/ ? Regexp.last_match(1) : "nil") if line =~ /broadcast/ && @interfaces[@int_name]["bcast"].nil?
+ @interfaces[@int_name]["mask"] = (line =~ /netmask (\S+)/ ? Regexp.last_match(1) : "nil") if line =~ /netmask/ && @interfaces[@int_name]["mask"].nil?
+ @interfaces[@int_name]["hwaddr"] = (line =~ /ether (\S+)/ ? Regexp.last_match(1) : "nil") if line =~ /ether/ && @interfaces[@int_name]["hwaddr"].nil?
+ @interfaces[@int_name]["metric"] = (line =~ /Metric:(\S+)/ ? Regexp.last_match(1) : "nil") if line =~ /Metric:/ && @interfaces[@int_name]["metric"].nil?
+ end
+
+ next unless @interfaces.key?(new_resource.device)
+ @interface = @interfaces.fetch(new_resource.device)
- if @interfaces.has_key?(@new_resource.device)
- @interface = @interfaces.fetch(@new_resource.device)
-
- @current_resource.target(@new_resource.target)
- @current_resource.device(@new_resource.device)
- @current_resource.inet_addr(@interface["inet_addr"])
- @current_resource.hwaddr(@interface["hwaddr"])
- @current_resource.bcast(@interface["bcast"])
- @current_resource.mask(@interface["mask"])
- @current_resource.mtu(@interface["mtu"])
- @current_resource.metric(@interface["metric"])
+ current_resource.target(new_resource.target)
+ current_resource.device(new_resource.device)
+ current_resource.inet_addr(@interface["inet_addr"])
+ current_resource.hwaddr(@interface["hwaddr"])
+ current_resource.bcast(@interface["bcast"])
+ current_resource.mask(@interface["mask"])
+ current_resource.mtu(@interface["mtu"])
+ current_resource.metric(@interface["metric"])
end
end
- @current_resource
+ current_resource
end
def define_resource_requirements
@@ -104,14 +159,12 @@ class Chef
def action_add
# check to see if load_current_resource found interface in ifconfig
- unless @current_resource.inet_addr
- unless @new_resource.device == loopback_device
+ unless current_resource.inet_addr
+ unless new_resource.device == loopback_device
command = add_command
- converge_by ("run #{command} to add #{@new_resource}") do
- run_command(
- :command => command
- )
- Chef::Log.info("#{@new_resource} added")
+ converge_by("run #{command.join(' ')} to add #{new_resource}") do
+ shell_out_compact!(command)
+ logger.info("#{new_resource} added")
end
end
end
@@ -122,31 +175,25 @@ class Chef
def action_enable
# check to see if load_current_resource found ifconfig
# enables, but does not manage config files
- unless @current_resource.inet_addr
- unless @new_resource.device == loopback_device
- command = enable_command
- converge_by ("run #{command} to enable #{@new_resource}") do
- run_command(
- :command => command
- )
- Chef::Log.info("#{@new_resource} enabled")
- end
- end
+ return if current_resource.inet_addr
+ return if new_resource.device == loopback_device
+ command = enable_command
+ converge_by("run #{command.join(' ')} to enable #{new_resource}") do
+ shell_out_compact!(command)
+ logger.info("#{new_resource} enabled")
end
end
def action_delete
# check to see if load_current_resource found the interface
- if @current_resource.device
+ if current_resource.device
command = delete_command
- converge_by ("run #{command} to delete #{@new_resource}") do
- run_command(
- :command => command
- )
- Chef::Log.info("#{@new_resource} deleted")
+ converge_by("run #{command.join(' ')} to delete #{new_resource}") do
+ shell_out_compact!(command)
+ logger.info("#{new_resource} deleted")
end
else
- Chef::Log.debug("#{@new_resource} does not exist - nothing to do")
+ logger.trace("#{new_resource} does not exist - nothing to do")
end
delete_config
end
@@ -154,21 +201,19 @@ class Chef
def action_disable
# check to see if load_current_resource found the interface
# disables, but leaves config files in place.
- if @current_resource.device
+ if current_resource.device
command = disable_command
- converge_by ("run #{command} to disable #{@new_resource}") do
- run_command(
- :command => command
- )
- Chef::Log.info("#{@new_resource} disabled")
+ converge_by("run #{command.join(' ')} to disable #{new_resource}") do
+ shell_out_compact!(command)
+ logger.info("#{new_resource} disabled")
end
else
- Chef::Log.debug("#{@new_resource} does not exist - nothing to do")
+ logger.trace("#{new_resource} does not exist - nothing to do")
end
end
def can_generate_config?
- ! @config_template.nil? && ! @config_path.nil?
+ !@config_template.nil? && !@config_path.nil?
end
def resource_for_config(path)
@@ -182,40 +227,40 @@ class Chef
config = resource_for_config(@config_path)
config.content(template.result(b))
config.run_action(:create)
- @new_resource.updated_by_last_action(true) if config.updated?
+ new_resource.updated_by_last_action(true) if config.updated?
end
def delete_config
return unless can_generate_config?
config = resource_for_config(@config_path)
config.run_action(:delete)
- @new_resource.updated_by_last_action(true) if config.updated?
+ new_resource.updated_by_last_action(true) if config.updated?
end
private
def add_command
- command = "ifconfig #{@new_resource.device} #{@new_resource.target}"
- command << " netmask #{@new_resource.mask}" if @new_resource.mask
- command << " metric #{@new_resource.metric}" if @new_resource.metric
- command << " mtu #{@new_resource.mtu}" if @new_resource.mtu
+ command = [ "ifconfig", new_resource.device, new_resource.target ]
+ command += [ "netmask", new_resource.mask ] if new_resource.mask
+ command += [ "metric", new_resource.metric ] if new_resource.metric
+ command += [ "mtu", new_resource.mtu ] if new_resource.mtu
command
end
def enable_command
- command = "ifconfig #{@new_resource.device} #{@new_resource.target}"
- command << " netmask #{@new_resource.mask}" if @new_resource.mask
- command << " metric #{@new_resource.metric}" if @new_resource.metric
- command << " mtu #{@new_resource.mtu}" if @new_resource.mtu
+ command = [ "ifconfig", new_resource.device, new_resource.target ]
+ command += [ "netmask", new_resource.mask ] if new_resource.mask
+ command += [ "metric", new_resource.metric ] if new_resource.metric
+ command += [ "mtu", new_resource.mtu ] if new_resource.mtu
command
end
def disable_command
- "ifconfig #{@new_resource.device} down"
+ [ "ifconfig", new_resource.device, "down" ]
end
def delete_command
- "ifconfig #{@new_resource.device} down"
+ [ "ifconfig", new_resource.device, "down" ]
end
def loopback_device
diff --git a/lib/chef/provider/ifconfig/aix.rb b/lib/chef/provider/ifconfig/aix.rb
index 81164db304..b68c5d5364 100644
--- a/lib/chef/provider/ifconfig/aix.rb
+++ b/lib/chef/provider/ifconfig/aix.rb
@@ -22,64 +22,59 @@ class Chef
class Provider
class Ifconfig
class Aix < Chef::Provider::Ifconfig
- provides :ifconfig, platform: %w{aix}
+ provides :ifconfig, platform: "aix"
def load_current_resource
- @current_resource = Chef::Resource::Ifconfig.new(@new_resource.name)
+ @current_resource = Chef::Resource::Ifconfig.new(new_resource.name)
@interface_exists = false
found_interface = false
interface = {}
- @status = shell_out("ifconfig -a")
+ @status = shell_out_compact("ifconfig", "-a")
@status.stdout.each_line do |line|
if !found_interface
if line =~ /^(\S+):\sflags=(\S+)/
- # We have interface name, if this is the interface for @current_resource, load info else skip till next interface is found.
- if $1 == @new_resource.device
+ # We have interface name, if this is the interface for current_resource, load info else skip till next interface is found.
+ if Regexp.last_match(1) == new_resource.device
# Found interface
found_interface = true
@interface_exists = true
- @current_resource.target(@new_resource.target)
- @current_resource.device($1)
- interface[:flags] = $2
- @current_resource.metric($1) if line =~ /metric\s(\S+)/
- end
- end
- else
- # parse interface related information, stop when next interface is found.
- if line =~ /^(\S+):\sflags=(\S+)/
- # we are done parsing interface info and hit another one, so stop.
- found_interface = false
- break
- else
- if found_interface
- # read up interface info
- @current_resource.inet_addr($1) if line =~ /inet\s(\S+)\s/
- @current_resource.bcast($1) if line =~ /broadcast\s(\S+)/
- @current_resource.mask(hex_to_dec_netmask($1)) if line =~ /netmask\s(\S+)\s/
+ current_resource.target(new_resource.target)
+ current_resource.device(Regexp.last_match(1))
+ interface[:flags] = Regexp.last_match(2)
+ current_resource.metric(Regexp.last_match(1)) if line =~ /metric\s(\S+)/
end
end
+ elsif line =~ /^(\S+):\sflags=(\S+)/
+ # we are done parsing interface info and hit another one, so stop.
+ found_interface = false
+ break
+ elsif found_interface
+ # read up interface info
+ current_resource.inet_addr(Regexp.last_match(1)) if line =~ /inet\s(\S+)\s/
+ current_resource.bcast(Regexp.last_match(1)) if line =~ /broadcast\s(\S+)/
+ current_resource.mask(hex_to_dec_netmask(Regexp.last_match(1))) if line =~ /netmask\s(\S+)\s/
end
end
- @current_resource
+ current_resource
end
private
def add_command
# ifconfig changes are temporary, chdev persist across reboots.
- raise Chef::Exceptions::Ifconfig, "interface metric attribute cannot be set for :add action" if @new_resource.metric
- command = "chdev -l #{@new_resource.device} -a netaddr=#{@new_resource.name}"
- command << " -a netmask=#{@new_resource.mask}" if @new_resource.mask
- command << " -a mtu=#{@new_resource.mtu}" if @new_resource.mtu
+ raise Chef::Exceptions::Ifconfig, "interface metric attribute cannot be set for :add action" if new_resource.metric
+ command = [ "chdev", "-l", new_resource.device, "-a", "netaddr=#{new_resource.name}" ]
+ command += [ "-a", "netmask=#{new_resource.mask}" ] if new_resource.mask
+ command += [ "-a", "mtu=#{new_resource.mtu}" ] if new_resource.mtu
command
end
def delete_command
# ifconfig changes are temporary, chdev persist across reboots.
- "chdev -l #{@new_resource.device} -a state=down"
+ [ "chdev", "-l", new_resource.device, "-a", "state=down" ]
end
def loopback_device
diff --git a/lib/chef/provider/ifconfig/debian.rb b/lib/chef/provider/ifconfig/debian.rb
index 872b0db152..aee3ca02dc 100644
--- a/lib/chef/provider/ifconfig/debian.rb
+++ b/lib/chef/provider/ifconfig/debian.rb
@@ -26,32 +26,32 @@ class Chef
provides :ifconfig, platform: %w{ubuntu}, platform_version: ">= 11.10"
provides :ifconfig, platform: %w{debian}, platform_version: ">= 7.0"
- INTERFACES_FILE = "/etc/network/interfaces"
- INTERFACES_DOT_D_DIR = "/etc/network/interfaces.d"
+ INTERFACES_FILE = "/etc/network/interfaces".freeze
+ INTERFACES_DOT_D_DIR = "/etc/network/interfaces.d".freeze
def initialize(new_resource, run_context)
super(new_resource, run_context)
@config_template = %{
-<% if @new_resource.device %>
-<% if @new_resource.onboot == "yes" %>auto <%= @new_resource.device %><% end %>
-<% case @new_resource.bootproto
+<% if new_resource.device %>
+<% if new_resource.onboot == "yes" %>auto <%= new_resource.device %><% end %>
+<% case new_resource.bootproto
when "dhcp" %>
-iface <%= @new_resource.device %> inet dhcp
+iface <%= new_resource.device %> <%= new_resource.family %> dhcp
<% when "bootp" %>
-iface <%= @new_resource.device %> inet bootp
+iface <%= new_resource.device %> <%= new_resource.family %> bootp
<% else %>
-iface <%= @new_resource.device %> inet static
- <% if @new_resource.target %>address <%= @new_resource.target %><% end %>
- <% if @new_resource.mask %>netmask <%= @new_resource.mask %><% end %>
- <% if @new_resource.network %>network <%= @new_resource.network %><% end %>
- <% if @new_resource.bcast %>broadcast <%= @new_resource.bcast %><% end %>
- <% if @new_resource.metric %>metric <%= @new_resource.metric %><% end %>
- <% if @new_resource.hwaddr %>hwaddress <%= @new_resource.hwaddr %><% end %>
- <% if @new_resource.mtu %>mtu <%= @new_resource.mtu %><% end %>
+iface <%= new_resource.device %> <%= new_resource.family %> static
+ <% if new_resource.target %>address <%= new_resource.target %><% end %>
+ <% if new_resource.mask %>netmask <%= new_resource.mask %><% end %>
+ <% if new_resource.network %>network <%= new_resource.network %><% end %>
+ <% if new_resource.bcast %>broadcast <%= new_resource.bcast %><% end %>
+ <% if new_resource.metric %>metric <%= new_resource.metric %><% end %>
+ <% if new_resource.hwaddr %>hwaddress <%= new_resource.hwaddr %><% end %>
+ <% if new_resource.mtu %>mtu <%= new_resource.mtu %><% end %>
<% end %>
<% end %>
}
- @config_path = "#{INTERFACES_DOT_D_DIR}/ifcfg-#{@new_resource.device}"
+ @config_path = "#{INTERFACES_DOT_D_DIR}/ifcfg-#{new_resource.device}"
end
def generate_config
@@ -62,6 +62,8 @@ iface <%= @new_resource.device %> inet static
protected
def enforce_interfaces_dot_d_sanity
+ # on ubuntu 18.04 there's no interfaces file and it uses interfaces.d by default
+ return if ::File.directory?(INTERFACES_DOT_D_DIR) && !::File.exist?(INTERFACES_FILE)
# create /etc/network/interfaces.d via dir resource (to get reporting, etc)
dir = Chef::Resource::Directory.new(INTERFACES_DOT_D_DIR, run_context)
dir.run_action(:create)
@@ -69,12 +71,13 @@ iface <%= @new_resource.device %> inet static
# roll our own file_edit resource, this will not get reported until we have a file_edit resource
interfaces_dot_d_for_regexp = INTERFACES_DOT_D_DIR.gsub(/\./, '\.') # escape dots for the regexp
regexp = %r{^\s*source\s+#{interfaces_dot_d_for_regexp}/\*\s*$}
- unless ::File.exists?(INTERFACES_FILE) && regexp.match(IO.read(INTERFACES_FILE))
- converge_by("modifying #{INTERFACES_FILE} to source #{INTERFACES_DOT_D_DIR}") do
- conf = Chef::Util::FileEdit.new(INTERFACES_FILE)
- conf.insert_line_if_no_match(regexp, "source #{INTERFACES_DOT_D_DIR}/*")
- conf.write_file
- end
+
+ return if ::File.exist?(INTERFACES_FILE) && regexp.match(IO.read(INTERFACES_FILE))
+
+ converge_by("modifying #{INTERFACES_FILE} to source #{INTERFACES_DOT_D_DIR}") do
+ conf = Chef::Util::FileEdit.new(INTERFACES_FILE)
+ conf.insert_line_if_no_match(regexp, "source #{INTERFACES_DOT_D_DIR}/*")
+ conf.write_file
end
end
diff --git a/lib/chef/provider/ifconfig/redhat.rb b/lib/chef/provider/ifconfig/redhat.rb
index 0c28e6407a..bf3d979e86 100644
--- a/lib/chef/provider/ifconfig/redhat.rb
+++ b/lib/chef/provider/ifconfig/redhat.rb
@@ -22,24 +22,28 @@ class Chef
class Provider
class Ifconfig
class Redhat < Chef::Provider::Ifconfig
- provides :ifconfig, platform_family: %w{fedora rhel}
+ provides :ifconfig, platform_family: %w{fedora rhel amazon}
def initialize(new_resource, run_context)
super(new_resource, run_context)
@config_template = %{
-<% if @new_resource.device %>DEVICE=<%= @new_resource.device %><% end %>
-<% if @new_resource.onboot == "yes" %>ONBOOT=<%= @new_resource.onboot %><% end %>
-<% if @new_resource.bootproto %>BOOTPROTO=<%= @new_resource.bootproto %><% end %>
-<% if @new_resource.target %>IPADDR=<%= @new_resource.target %><% end %>
-<% if @new_resource.mask %>NETMASK=<%= @new_resource.mask %><% end %>
-<% if @new_resource.network %>NETWORK=<%= @new_resource.network %><% end %>
-<% if @new_resource.bcast %>BROADCAST=<%= @new_resource.bcast %><% end %>
-<% if @new_resource.onparent %>ONPARENT=<%= @new_resource.onparent %><% end %>
-<% if @new_resource.hwaddr %>HWADDR=<%= @new_resource.hwaddr %><% end %>
-<% if @new_resource.metric %>METRIC=<%= @new_resource.metric %><% end %>
-<% if @new_resource.mtu %>MTU=<%= @new_resource.mtu %><% end %>
+<% if new_resource.device %>DEVICE=<%= new_resource.device %><% end %>
+<% if new_resource.onboot == "yes" %>ONBOOT=<%= new_resource.onboot %><% end %>
+<% if new_resource.bootproto %>BOOTPROTO=<%= new_resource.bootproto %><% end %>
+<% if new_resource.target %>IPADDR=<%= new_resource.target %><% end %>
+<% if new_resource.mask %>NETMASK=<%= new_resource.mask %><% end %>
+<% if new_resource.network %>NETWORK=<%= new_resource.network %><% end %>
+<% if new_resource.bcast %>BROADCAST=<%= new_resource.bcast %><% end %>
+<% if new_resource.onparent %>ONPARENT=<%= new_resource.onparent %><% end %>
+<% if new_resource.hwaddr %>HWADDR=<%= new_resource.hwaddr %><% end %>
+<% if new_resource.metric %>METRIC=<%= new_resource.metric %><% end %>
+<% if new_resource.mtu %>MTU=<%= new_resource.mtu %><% end %>
+<% if new_resource.ethtool_opts %>ETHTOOL_OPTS="<%= new_resource.ethtool_opts %>"<% end %>
+<% if new_resource.bonding_opts %>BONDING_OPTS="<%= new_resource.bonding_opts %>"<% end %>
+<% if new_resource.master %>MASTER=<%= new_resource.master %><% end %>
+<% if new_resource.slave %>SLAVE=<%= new_resource.slave %><% end %>
}
- @config_path = "/etc/sysconfig/network-scripts/ifcfg-#{@new_resource.device}"
+ @config_path = "/etc/sysconfig/network-scripts/ifcfg-#{new_resource.device}"
end
end
diff --git a/lib/chef/provider/launchd.rb b/lib/chef/provider/launchd.rb
index c58d4bfa34..0bac995247 100644
--- a/lib/chef/provider/launchd.rb
+++ b/lib/chef/provider/launchd.rb
@@ -17,7 +17,6 @@
#
require "chef/provider"
-require "chef/resource/launchd"
require "chef/resource/file"
require "chef/resource/cookbook_file"
require "chef/resource/macosx_service"
@@ -30,7 +29,7 @@ class Chef
extend Forwardable
provides :launchd, os: "darwin"
- def_delegators :@new_resource, *[
+ def_delegators :new_resource, *[
:backup,
:cookbook,
:group,
@@ -85,7 +84,12 @@ class Chef
manage_service(:disable)
end
+ def action_restart
+ manage_service(:restart)
+ end
+
def manage_plist(action)
+ return unless manage_agent?(action)
if source
res = cookbook_file_resource
else
@@ -97,11 +101,30 @@ class Chef
end
def manage_service(action)
+ return unless manage_agent?(action)
res = service_resource
res.run_action(action)
new_resource.updated_by_last_action(true) if res.updated?
end
+ def manage_agent?(action)
+ # Gets UID of console_user and converts to string.
+ console_user = Etc.getpwuid(::File.stat("/dev/console").uid).name
+ root = console_user == "root"
+ agent = type == "agent"
+ invalid_action = [:delete, :disable, :enable, :restart].include?(action)
+ lltstype = ""
+ if new_resource.limit_load_to_session_type
+ lltstype = new_resource.limit_load_to_session_type
+ end
+ invalid_type = lltstype != "LoginWindow"
+ if root && agent && invalid_action && invalid_type
+ logger.trace("#{label}: Aqua LaunchAgents shouldn't be loaded as root")
+ return false
+ end
+ true
+ end
+
def service_resource
res = Chef::Resource::MacosxService.new(label, run_context)
res.name(label) if label
@@ -115,7 +138,7 @@ class Chef
res = Chef::Resource::File.new(@path, run_context)
res.name(@path) if @path
res.backup(backup) if backup
- res.content(content) if content
+ res.content(content) if content?
res.group(group) if group
res.mode(mode) if mode
res.owner(owner) if owner
@@ -150,7 +173,7 @@ class Chef
end
def content
- plist_hash = new_resource.hash || gen_hash
+ plist_hash = new_resource.plist_hash || gen_hash
Plist::Emit.dump(plist_hash) unless plist_hash.nil?
end
diff --git a/lib/chef/provider/link.rb b/lib/chef/provider/link.rb
index 5fce97e5b3..783d52d09a 100644
--- a/lib/chef/provider/link.rb
+++ b/lib/chef/provider/link.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -42,46 +42,42 @@ class Chef
private :negative_complement
- def whyrun_supported?
- true
- end
-
def load_current_resource
- @current_resource = Chef::Resource::Link.new(@new_resource.name)
- @current_resource.target_file(@new_resource.target_file)
- if file_class.symlink?(@current_resource.target_file)
- @current_resource.link_type(:symbolic)
- @current_resource.to(
- canonicalize(file_class.readlink(@current_resource.target_file))
+ @current_resource = Chef::Resource::Link.new(new_resource.name)
+ current_resource.target_file(new_resource.target_file)
+ if file_class.symlink?(current_resource.target_file)
+ current_resource.link_type(:symbolic)
+ current_resource.to(
+ canonicalize(file_class.readlink(current_resource.target_file))
)
else
- @current_resource.link_type(:hard)
- if ::File.exists?(@current_resource.target_file)
- if ::File.exists?(@new_resource.to) &&
- file_class.stat(@current_resource.target_file).ino ==
- file_class.stat(@new_resource.to).ino
- @current_resource.to(canonicalize(@new_resource.to))
+ current_resource.link_type(:hard)
+ if ::File.exists?(current_resource.target_file)
+ if ::File.exists?(new_resource.to) &&
+ file_class.stat(current_resource.target_file).ino ==
+ file_class.stat(new_resource.to).ino
+ current_resource.to(canonicalize(new_resource.to))
else
- @current_resource.to("")
+ current_resource.to("")
end
end
end
- ScanAccessControl.new(@new_resource, @current_resource).set_all!
- @current_resource
+ ScanAccessControl.new(new_resource, current_resource).set_all!
+ current_resource
end
def define_resource_requirements
requirements.assert(:delete) do |a|
a.assertion do
- if @current_resource.to
- @current_resource.link_type == @new_resource.link_type &&
- (@current_resource.link_type == :symbolic || @current_resource.to != "")
+ if current_resource.to
+ current_resource.link_type == new_resource.link_type &&
+ (current_resource.link_type == :symbolic || current_resource.to != "")
else
true
end
end
- a.failure_message Chef::Exceptions::Link, "Cannot delete #{@new_resource} at #{@new_resource.target_file}! Not a #{@new_resource.link_type} link."
- a.whyrun("Would assume the link at #{@new_resource.target_file} was previously created")
+ a.failure_message Chef::Exceptions::Link, "Cannot delete #{new_resource} at #{new_resource.target_file}! Not a #{new_resource.link_type} link."
+ a.whyrun("Would assume the link at #{new_resource.target_file} was previously created")
end
end
@@ -95,42 +91,48 @@ class Chef
# to - the location to link to
# target_file - the name of the link
- if @current_resource.to != canonicalize(@new_resource.to) ||
- @current_resource.link_type != @new_resource.link_type
+ if current_resource.to != canonicalize(new_resource.to) ||
+ current_resource.link_type != new_resource.link_type
# Handle the case where the symlink already exists and is pointing at a valid to_file
- if @current_resource.to
+ if current_resource.to
# On Windows, to fix a symlink already pointing at a directory we must first
# ::Dir.unlink the symlink (not the directory), while if we have a symlink
# pointing at file we must use ::File.unlink on the symlink.
# However if the new symlink will point to a file and the current symlink is pointing at a
# directory we want to throw an exception and calling ::File.unlink on the directory symlink
# will throw the correct ones.
- if Chef::Platform.windows? && ::File.directory?(@new_resource.to) &&
- ::File.directory?(@current_resource.target_file)
- converge_by("unlink existing windows symlink to dir at #{@new_resource.target_file}") do
- ::Dir.unlink(@new_resource.target_file)
+ if Chef::Platform.windows? && ::File.directory?(new_resource.to) &&
+ ::File.directory?(current_resource.target_file)
+ converge_by("unlink existing windows symlink to dir at #{new_resource.target_file}") do
+ ::Dir.unlink(new_resource.target_file)
end
else
- converge_by("unlink existing symlink to file at #{@new_resource.target_file}") do
- ::File.unlink(@new_resource.target_file)
+ converge_by("unlink existing symlink to file at #{new_resource.target_file}") do
+ ::File.unlink(new_resource.target_file)
end
end
end
- if @new_resource.link_type == :symbolic
- converge_by("create symlink at #{@new_resource.target_file} to #{@new_resource.to}") do
- file_class.symlink(canonicalize(@new_resource.to), @new_resource.target_file)
- Chef::Log.debug("#{@new_resource} created #{@new_resource.link_type} link from #{@new_resource.target_file} -> #{@new_resource.to}")
- Chef::Log.info("#{@new_resource} created")
+ if new_resource.link_type == :symbolic
+ converge_by("create symlink at #{new_resource.target_file} to #{new_resource.to}") do
+ file_class.symlink(canonicalize(new_resource.to), new_resource.target_file)
+ logger.trace("#{new_resource} created #{new_resource.link_type} link from #{new_resource.target_file} -> #{new_resource.to}")
+ logger.info("#{new_resource} created")
+ # file_class.symlink will create the link with default access controls.
+ # This means that the access controls of the file could be different
+ # than those captured during the initial evaluation of current_resource.
+ # We need to re-evaluate the current_resource to ensure that the desired
+ # access controls are applied.
+ ScanAccessControl.new(new_resource, current_resource).set_all!
end
- elsif @new_resource.link_type == :hard
- converge_by("create hard link at #{@new_resource.target_file} to #{@new_resource.to}") do
- file_class.link(@new_resource.to, @new_resource.target_file)
- Chef::Log.debug("#{@new_resource} created #{@new_resource.link_type} link from #{@new_resource.target_file} -> #{@new_resource.to}")
- Chef::Log.info("#{@new_resource} created")
+ elsif new_resource.link_type == :hard
+ converge_by("create hard link at #{new_resource.target_file} to #{new_resource.to}") do
+ file_class.link(new_resource.to, new_resource.target_file)
+ logger.trace("#{new_resource} created #{new_resource.link_type} link from #{new_resource.target_file} -> #{new_resource.to}")
+ logger.info("#{new_resource} created")
end
end
end
- if @new_resource.link_type == :symbolic
+ if new_resource.link_type == :symbolic
if access_controls.requires_changes?
converge_by(access_controls.describe_changes) do
access_controls.set_all
@@ -140,10 +142,17 @@ class Chef
end
def action_delete
- if @current_resource.to # Exists
- converge_by("delete link at #{@new_resource.target_file}") do
- ::File.delete(@new_resource.target_file)
- Chef::Log.info("#{@new_resource} deleted")
+ if current_resource.to # Exists
+ if Chef::Platform.windows? && ::File.directory?(current_resource.target_file)
+ converge_by("delete link to dir at #{new_resource.target_file}") do
+ ::Dir.delete(new_resource.target_file)
+ logger.info("#{new_resource} deleted")
+ end
+ else
+ converge_by("delete link to file at #{new_resource.target_file}") do
+ ::File.delete(new_resource.target_file)
+ logger.info("#{new_resource} deleted")
+ end
end
end
end
@@ -152,7 +161,7 @@ class Chef
# access control (e.g., use lchmod instead of chmod) if the resource is a
# symlink.
def manage_symlink_access?
- @new_resource.link_type == :symbolic
+ new_resource.link_type == :symbolic
end
end
end
diff --git a/lib/chef/provider/log.rb b/lib/chef/provider/log.rb
index eef4077c07..8d15883ff6 100644
--- a/lib/chef/provider/log.rb
+++ b/lib/chef/provider/log.rb
@@ -1,6 +1,6 @@
#
# Author:: Cary Penniman (<cary@rightscale.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,41 +17,27 @@
#
class Chef
-
class Provider
-
class Log
-
- # Chef log provider, allows logging to chef's logs from recipes
+ # Chef log provider, allows logging to chef's logs
class ChefLog < Chef::Provider
-
provides :log
- def whyrun_supported?
- true
- end
-
# No concept of a 'current' resource for logs, this is a no-op
#
- # === Return
- # true:: Always return true
+ # @return [true] Always returns true
def load_current_resource
true
end
# Write the log to Chef's log
#
- # === Return
- # true:: Always return true
+ # @return [true] Always returns true
def action_write
- Chef::Log.send(@new_resource.level, @new_resource.message)
- @new_resource.updated_by_last_action(true)
+ logger.send(new_resource.level, new_resource.message)
+ new_resource.updated_by_last_action(true) if Chef::Config[:count_log_resource_updates]
end
-
end
-
end
-
end
-
end
diff --git a/lib/chef/provider/lwrp_base.rb b/lib/chef/provider/lwrp_base.rb
index f5ba30ba15..70f2af6539 100644
--- a/lib/chef/provider/lwrp_base.rb
+++ b/lib/chef/provider/lwrp_base.rb
@@ -34,7 +34,6 @@ class Chef
# These were previously provided by Chef::Mixin::RecipeDefinitionDSLCore.
# They are not included by its replacement, Chef::DSL::Recipe, but
# they may be used in existing LWRPs.
- include Chef::DSL::PlatformIntrospection
include Chef::DSL::DataQuery
# Allow include_recipe from within LWRP provider code
@@ -53,7 +52,7 @@ class Chef
def build_from_file(cookbook_name, filename, run_context)
if LWRPBase.loaded_lwrps[filename]
- Chef::Log.debug("LWRP provider #{filename} from cookbook #{cookbook_name} has already been loaded! Skipping the reload.")
+ Chef::Log.trace("LWRP provider #{filename} from cookbook #{cookbook_name} has already been loaded! Skipping the reload.")
return loaded_lwrps[filename]
end
@@ -72,22 +71,13 @@ class Chef
define_singleton_method(:inspect) { to_s }
end
- Chef::Log.debug("Loaded contents of #{filename} into provider #{resource_name} (#{provider_class})")
+ Chef::Log.trace("Loaded contents of #{filename} into provider #{resource_name} (#{provider_class})")
LWRPBase.loaded_lwrps[filename] = true
- Chef::Provider.register_deprecated_lwrp_class(provider_class, convert_to_class_name(resource_name))
-
provider_class
end
- # DSL for defining a provider's actions.
- def action(name, &block)
- define_method("action_#{name}") do
- instance_eval(&block)
- end
- end
-
protected
def loaded_lwrps
diff --git a/lib/chef/provider/mdadm.rb b/lib/chef/provider/mdadm.rb
index f8225ff63a..c5f15a851a 100644
--- a/lib/chef/provider/mdadm.rb
+++ b/lib/chef/provider/mdadm.rb
@@ -25,66 +25,58 @@ class Chef
provides :mdadm
- def popen4
- raise Exception, "deprecated"
- end
-
- def whyrun_supported?
- true
- end
-
def load_current_resource
- @current_resource = Chef::Resource::Mdadm.new(@new_resource.name)
- @current_resource.raid_device(@new_resource.raid_device)
- Chef::Log.debug("#{@new_resource} checking for software raid device #{@current_resource.raid_device}")
+ @current_resource = Chef::Resource::Mdadm.new(new_resource.name)
+ current_resource.raid_device(new_resource.raid_device)
+ logger.trace("#{new_resource} checking for software raid device #{current_resource.raid_device}")
device_not_found = 4
- mdadm = shell_out!("mdadm --detail --test #{@new_resource.raid_device}", :returns => [0, device_not_found])
+ mdadm = shell_out!("mdadm --detail --test #{new_resource.raid_device}", :returns => [0, device_not_found])
exists = (mdadm.status == 0)
- @current_resource.exists(exists)
+ current_resource.exists(exists)
end
def action_create
- unless @current_resource.exists
+ unless current_resource.exists
converge_by("create RAID device #{new_resource.raid_device}") do
- command = "yes | mdadm --create #{@new_resource.raid_device} --level #{@new_resource.level}"
- command << " --chunk=#{@new_resource.chunk}" unless @new_resource.level == 1
- command << " --metadata=#{@new_resource.metadata}"
- command << " --bitmap=#{@new_resource.bitmap}" if @new_resource.bitmap
- command << " --layout=#{@new_resource.layout}" if @new_resource.layout
- command << " --raid-devices #{@new_resource.devices.length} #{@new_resource.devices.join(" ")}"
- Chef::Log.debug("#{@new_resource} mdadm command: #{command}")
+ command = "yes | mdadm --create #{new_resource.raid_device} --level #{new_resource.level}"
+ command << " --chunk=#{new_resource.chunk}" unless new_resource.level == 1
+ command << " --metadata=#{new_resource.metadata}"
+ command << " --bitmap=#{new_resource.bitmap}" if new_resource.bitmap
+ command << " --layout=#{new_resource.layout}" if new_resource.layout
+ command << " --raid-devices #{new_resource.devices.length} #{new_resource.devices.join(" ")}"
+ logger.trace("#{new_resource} mdadm command: #{command}")
shell_out!(command)
- Chef::Log.info("#{@new_resource} created raid device (#{@new_resource.raid_device})")
+ logger.info("#{new_resource} created raid device (#{new_resource.raid_device})")
end
else
- Chef::Log.debug("#{@new_resource} raid device already exists, skipping create (#{@new_resource.raid_device})")
+ logger.trace("#{new_resource} raid device already exists, skipping create (#{new_resource.raid_device})")
end
end
def action_assemble
- unless @current_resource.exists
+ unless current_resource.exists
converge_by("assemble RAID device #{new_resource.raid_device}") do
- command = "yes | mdadm --assemble #{@new_resource.raid_device} #{@new_resource.devices.join(" ")}"
- Chef::Log.debug("#{@new_resource} mdadm command: #{command}")
+ command = "yes | mdadm --assemble #{new_resource.raid_device} #{new_resource.devices.join(" ")}"
+ logger.trace("#{new_resource} mdadm command: #{command}")
shell_out!(command)
- Chef::Log.info("#{@new_resource} assembled raid device (#{@new_resource.raid_device})")
+ logger.info("#{new_resource} assembled raid device (#{new_resource.raid_device})")
end
else
- Chef::Log.debug("#{@new_resource} raid device already exists, skipping assemble (#{@new_resource.raid_device})")
+ logger.trace("#{new_resource} raid device already exists, skipping assemble (#{new_resource.raid_device})")
end
end
def action_stop
- if @current_resource.exists
+ if current_resource.exists
converge_by("stop RAID device #{new_resource.raid_device}") do
- command = "yes | mdadm --stop #{@new_resource.raid_device}"
- Chef::Log.debug("#{@new_resource} mdadm command: #{command}")
+ command = "yes | mdadm --stop #{new_resource.raid_device}"
+ logger.trace("#{new_resource} mdadm command: #{command}")
shell_out!(command)
- Chef::Log.info("#{@new_resource} stopped raid device (#{@new_resource.raid_device})")
+ logger.info("#{new_resource} stopped raid device (#{new_resource.raid_device})")
end
else
- Chef::Log.debug("#{@new_resource} raid device doesn't exist (#{@new_resource.raid_device}) - not stopping")
+ logger.trace("#{new_resource} raid device doesn't exist (#{new_resource.raid_device}) - not stopping")
end
end
diff --git a/lib/chef/provider/mount.rb b/lib/chef/provider/mount.rb
index 9e9ee29bde..994d67939b 100644
--- a/lib/chef/provider/mount.rb
+++ b/lib/chef/provider/mount.rb
@@ -1,7 +1,7 @@
#
# Author:: Joshua Timberman (<joshua@chef.io>)
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software, Inc.
+# Copyright:: Copyright 2009-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,10 +28,6 @@ class Chef
attr_accessor :unmount_retries
- def whyrun_supported?
- true
- end
-
def load_current_resource
true
end
@@ -45,10 +41,10 @@ class Chef
unless current_resource.mounted
converge_by("mount #{current_resource.device} to #{current_resource.mount_point}") do
mount_fs
- Chef::Log.info("#{new_resource} mounted")
+ logger.info("#{new_resource} mounted")
end
else
- Chef::Log.debug("#{new_resource} is already mounted")
+ logger.trace("#{new_resource} is already mounted")
end
end
@@ -56,10 +52,10 @@ class Chef
if current_resource.mounted
converge_by("unmount #{current_resource.device}") do
umount_fs
- Chef::Log.info("#{new_resource} unmounted")
+ logger.info("#{new_resource} unmounted")
end
else
- Chef::Log.debug("#{new_resource} is already unmounted")
+ logger.trace("#{new_resource} is already unmounted")
end
end
@@ -68,32 +64,32 @@ class Chef
if new_resource.supports[:remount]
converge_by("remount #{current_resource.device}") do
remount_fs
- Chef::Log.info("#{new_resource} remounted")
+ logger.info("#{new_resource} remounted")
end
else
converge_by("unmount #{current_resource.device}") do
umount_fs
- Chef::Log.info("#{new_resource} unmounted")
+ logger.info("#{new_resource} unmounted")
end
wait_until_unmounted(unmount_retries)
converge_by("mount #{current_resource.device}") do
mount_fs
- Chef::Log.info("#{new_resource} mounted")
+ logger.info("#{new_resource} mounted")
end
end
else
- Chef::Log.debug("#{new_resource} not mounted, nothing to remount")
+ logger.trace("#{new_resource} not mounted, nothing to remount")
end
end
def action_enable
- unless current_resource.enabled && mount_options_unchanged?
+ unless current_resource.enabled && mount_options_unchanged? && device_unchanged?
converge_by("enable #{current_resource.device}") do
enable_fs
- Chef::Log.info("#{new_resource} enabled")
+ logger.info("#{new_resource} enabled")
end
else
- Chef::Log.debug("#{new_resource} already enabled")
+ logger.trace("#{new_resource} already enabled")
end
end
@@ -101,13 +97,15 @@ class Chef
if current_resource.enabled
converge_by("disable #{current_resource.device}") do
disable_fs
- Chef::Log.info("#{new_resource} disabled")
+ logger.info("#{new_resource} disabled")
end
else
- Chef::Log.debug("#{new_resource} already disabled")
+ logger.trace("#{new_resource} already disabled")
end
end
+ alias :action_unmount :action_umount
+
#
# Abstract Methods to be implemented by subclasses
#
@@ -122,6 +120,14 @@ class Chef
raise Chef::Exceptions::UnsupportedAction, "#{self} does not implement #mount_options_unchanged?"
end
+ # It's entirely plausible that a site might prefer UUIDs or labels, so
+ # we need to be able to update fstab to conform with their wishes
+ # without necessarily needing to remount the device.
+ # See #6851 for more.
+ def device_unchanged?
+ @current_resource.device == @new_resource.device
+ end
+
#
# NOTE: for the following methods, this superclass will already have checked if the filesystem is
# enabled and/or mounted and they will be called in converge_by blocks, so most defensive checking
diff --git a/lib/chef/provider/mount/aix.rb b/lib/chef/provider/mount/aix.rb
index 12f0d67e6b..c1ed499957 100644
--- a/lib/chef/provider/mount/aix.rb
+++ b/lib/chef/provider/mount/aix.rb
@@ -1,6 +1,5 @@
#
-# Author::
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,14 +21,14 @@ class Chef
class Provider
class Mount
class Aix < Chef::Provider::Mount::Mount
- provides :mount, platform: %w{aix}
+ provides :mount, platform: "aix"
# Override for aix specific handling
def initialize(new_resource, run_context)
super
# options and fstype are set to "defaults" and "auto" respectively in the Mount Resource class. These options are not valid for AIX, override them.
if @new_resource.options[0] == "defaults"
- @new_resource.options.clear
+ @new_resource.options([])
end
if @new_resource.fstype == "auto"
@new_resource.send(:clear_fstype)
@@ -40,30 +39,60 @@ class Chef
# Check to see if there is an entry in /etc/filesystems. Last entry for a volume wins. Using command "lsfs" to fetch entries.
enabled = false
+ regex_arr = device_fstab_regex.split(":")
+ if regex_arr.size == 2
+ nodename = regex_arr[0]
+ devicename = regex_arr[1]
+ else
+ devicename = regex_arr[0]
+ end
# lsfs o/p = #MountPoint:Device:Vfs:Nodename:Type:Size:Options:AutoMount:Acct
# search only for current mount point
shell_out("lsfs -c #{@new_resource.mount_point}").stdout.each_line do |line|
case line
when /^#\s/
next
- when /^#{Regexp.escape(@new_resource.mount_point)}:#{device_fstab_regex}:(\S+):(\[\S+\])?:(\S+)?:(\S+):(\S+):(\S+):(\S+)/
+ when /^#{Regexp.escape(@new_resource.mount_point)}:#{devicename}:(\S+):#{nodename}:(\S+)?:(\S+):(\S+):(\S+):(\S+)/
# mount point entry with ipv6 address for nodename (ipv6 address use ':')
enabled = true
@current_resource.fstype($1)
- @current_resource.options($5)
- Chef::Log.debug("Found mount #{device_fstab} to #{@new_resource.mount_point} in /etc/filesystems")
+ @current_resource.options($4)
+ logger.trace("Found mount point #{@new_resource.mount_point} :: device_type #{@current_resource.device_type} in /etc/filesystems")
next
- when /^#{Regexp.escape(@new_resource.mount_point)}:#{device_fstab_regex}::(\S+):(\S+)?:(\S+)?:(\S+):(\S+):(\S+):(\S+)/
+ when /^#{Regexp.escape(@new_resource.mount_point)}:#{nodename}:(\S+)::(\S+)?:(\S+):(\S+):(\S+):(\S+)/
# mount point entry with hostname or ipv4 address
enabled = true
@current_resource.fstype($1)
+ @current_resource.options($4)
+ @current_resource.device("")
+ logger.trace("Found mount point #{@new_resource.mount_point} :: device_type #{@current_resource.device_type} in /etc/filesystems")
+ next
+ when /^#{Regexp.escape(@new_resource.mount_point)}:(\S+)?:(\S+):#{devicename}:(\S+)?:(\S+):(\S+):(\S+):(\S+)/
+ # mount point entry with hostname or ipv4 address
+ enabled = true
+ @current_resource.fstype($2)
@current_resource.options($5)
- Chef::Log.debug("Found mount #{device_fstab} to #{@new_resource.mount_point} in /etc/filesystems")
+ @current_resource.device(devicename + "/")
+ logger.trace("Found mount point #{@new_resource.mount_point} :: device_type #{@current_resource.device_type} in /etc/filesystems")
next
- when /^#{Regexp.escape(@new_resource.mount_point)}/
- enabled = false
- Chef::Log.debug("Found conflicting mount point #{@new_resource.mount_point} in /etc/filesystems")
+ when /^#{Regexp.escape(@new_resource.mount_point)}:(.*?):(.*?):(.*?):(.*?):(.*?):(.*?):(.*?):(.*?)/
+ if $3.split("=")[0] == "LABEL" || $1.split("=")[0] == "LABEL"
+ @current_resource.device_type("label")
+ elsif $3.split("=")[0] == "UUID" || $1.split("=")[0] == "UUID"
+ @current_resource.device_type("uuid")
+ else
+ @current_resource.device_type("device")
+ end
+
+ if @current_resource.device_type != @new_resource.device_type
+ enabled = true
+ logger.trace("Found mount point #{@new_resource.mount_point} :: device_type #{@current_resource.device_type} in /etc/filesystems")
+ else
+ enabled = false
+ logger.trace("Found conflicting mount point #{@new_resource.mount_point} in /etc/filesystems")
+ end
end
+
end
@current_resource.enabled(enabled)
end
@@ -80,10 +109,10 @@ class Chef
case line
when /#{search_device}\s+#{Regexp.escape(@new_resource.mount_point)}/
mounted = true
- Chef::Log.debug("Special device #{device_logstring} mounted as #{@new_resource.mount_point}")
+ logger.trace("Special device #{device_logstring} mounted as #{@new_resource.mount_point}")
when /^[\/\w]+\s+#{Regexp.escape(@new_resource.mount_point)}\s+/
mounted = false
- Chef::Log.debug("Found conflicting mount point #{@new_resource.mount_point} in /etc/fstab")
+ logger.trace("Found conflicting mount point #{@new_resource.mount_point} in /etc/fstab")
end
end
@current_resource.mounted(mounted)
@@ -108,23 +137,23 @@ class Chef
end
command << " #{@new_resource.mount_point}"
shell_out!(command)
- Chef::Log.debug("#{@new_resource} is mounted at #{@new_resource.mount_point}")
+ logger.trace("#{@new_resource} is mounted at #{@new_resource.mount_point}")
else
- Chef::Log.debug("#{@new_resource} is already mounted at #{@new_resource.mount_point}")
+ logger.trace("#{@new_resource} is already mounted at #{@new_resource.mount_point}")
end
end
def remount_command
if !(@new_resource.options.nil? || @new_resource.options.empty?)
- return "mount -o remount,#{@new_resource.options.join(',')} #{@new_resource.device} #{@new_resource.mount_point}"
+ "mount -o remount,#{@new_resource.options.join(',')} #{@new_resource.device} #{@new_resource.mount_point}"
else
- return "mount -o remount #{@new_resource.device} #{@new_resource.mount_point}"
+ "mount -o remount #{@new_resource.device} #{@new_resource.mount_point}"
end
end
def enable_fs
if @current_resource.enabled && mount_options_unchanged?
- Chef::Log.debug("#{@new_resource} is already enabled - nothing to do")
+ logger.trace("#{@new_resource} is already enabled - nothing to do")
return nil
end
@@ -145,10 +174,21 @@ class Chef
fstab.puts("\tvfs\t\t= #{@new_resource.fstype}")
fstab.puts("\tmount\t\t= false")
fstab.puts "\toptions\t\t= #{@new_resource.options.join(',')}" unless @new_resource.options.nil? || @new_resource.options.empty?
- Chef::Log.debug("#{@new_resource} is enabled at #{@new_resource.mount_point}")
+ logger.trace("#{@new_resource} is enabled at #{@new_resource.mount_point}")
end
end
+ def mount_options_unchanged?
+ current_resource_options = @current_resource.options.delete_if { |x| x == "rw" }
+
+ @current_resource.device == @new_resource.device &&
+ @current_resource.fsck_device == @new_resource.fsck_device &&
+ @current_resource.fstype == @new_resource.fstype &&
+ current_resource_options == @new_resource.options &&
+ @current_resource.dump == @new_resource.dump &&
+ @current_resource.pass == @new_resource.pass
+ end
+
def disable_fs
contents = []
if @current_resource.enabled
@@ -170,7 +210,7 @@ class Chef
contents.each { |line| fstab.puts line }
end
else
- Chef::Log.debug("#{@new_resource} is not enabled - nothing to do")
+ logger.trace("#{@new_resource} is not enabled - nothing to do")
end
end
diff --git a/lib/chef/provider/mount/mount.rb b/lib/chef/provider/mount/mount.rb
index 07da6ac361..d0da30a30a 100644
--- a/lib/chef/provider/mount/mount.rb
+++ b/lib/chef/provider/mount/mount.rb
@@ -47,7 +47,7 @@ class Chef
elsif @new_resource.mount_point != "none" && !::File.exists?(@new_resource.mount_point)
raise Chef::Exceptions::Mount, "Mount point #{@new_resource.mount_point} does not exist"
end
- return true
+ true
end
def enabled?
@@ -57,17 +57,18 @@ class Chef
case line
when /^[#\s]/
next
- when /^#{device_fstab_regex}\s+#{Regexp.escape(@new_resource.mount_point)}\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/
+ when /^(#{device_fstab_regex})\s+#{Regexp.escape(@new_resource.mount_point)}\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/
enabled = true
- @current_resource.fstype($1)
- @current_resource.options($2)
- @current_resource.dump($3.to_i)
- @current_resource.pass($4.to_i)
- Chef::Log.debug("Found mount #{device_fstab} to #{@new_resource.mount_point} in /etc/fstab")
+ @current_resource.device($1)
+ @current_resource.fstype($2)
+ @current_resource.options($3)
+ @current_resource.dump($4.to_i)
+ @current_resource.pass($5.to_i)
+ logger.trace("Found mount #{device_fstab} to #{@new_resource.mount_point} in /etc/fstab")
next
when /^[\/\w]+\s+#{Regexp.escape(@new_resource.mount_point)}\s+/
enabled = false
- Chef::Log.debug("Found conflicting mount point #{@new_resource.mount_point} in /etc/fstab")
+ logger.trace("Found conflicting mount point #{@new_resource.mount_point} in /etc/fstab")
end
end
@current_resource.enabled(enabled)
@@ -89,10 +90,10 @@ class Chef
case line
when /^#{device_mount_regex}\s+on\s+#{Regexp.escape(real_mount_point)}\s/
mounted = true
- Chef::Log.debug("Special device #{device_logstring} mounted as #{real_mount_point}")
+ logger.trace("Special device #{device_logstring} mounted as #{real_mount_point}")
when /^([\/\w])+\son\s#{Regexp.escape(real_mount_point)}\s+/
mounted = false
- Chef::Log.debug("Special device #{$~[1]} mounted as #{real_mount_point}")
+ logger.trace("Special device #{$~[1]} mounted as #{real_mount_point}")
end
end
@current_resource.mounted(mounted)
@@ -113,42 +114,47 @@ class Chef
end
command << " #{@new_resource.mount_point}"
shell_out!(command)
- Chef::Log.debug("#{@new_resource} is mounted at #{@new_resource.mount_point}")
+ logger.trace("#{@new_resource} is mounted at #{@new_resource.mount_point}")
else
- Chef::Log.debug("#{@new_resource} is already mounted at #{@new_resource.mount_point}")
+ logger.trace("#{@new_resource} is already mounted at #{@new_resource.mount_point}")
end
end
def umount_fs
if @current_resource.mounted
shell_out!("umount #{@new_resource.mount_point}")
- Chef::Log.debug("#{@new_resource} is no longer mounted at #{@new_resource.mount_point}")
+ logger.trace("#{@new_resource} is no longer mounted at #{@new_resource.mount_point}")
else
- Chef::Log.debug("#{@new_resource} is not mounted at #{@new_resource.mount_point}")
+ logger.trace("#{@new_resource} is not mounted at #{@new_resource.mount_point}")
end
end
def remount_command
- return "mount -o remount,#{@new_resource.options.join(',')} #{@new_resource.mount_point}"
+ "mount -o remount,#{@new_resource.options.join(',')} #{@new_resource.mount_point}"
end
def remount_fs
if @current_resource.mounted && @new_resource.supports[:remount]
shell_out!(remount_command)
@new_resource.updated_by_last_action(true)
- Chef::Log.debug("#{@new_resource} is remounted at #{@new_resource.mount_point}")
+ logger.trace("#{@new_resource} is remounted at #{@new_resource.mount_point}")
elsif @current_resource.mounted
umount_fs
sleep 1
mount_fs
else
- Chef::Log.debug("#{@new_resource} is not mounted at #{@new_resource.mount_point} - nothing to do")
+ logger.trace("#{@new_resource} is not mounted at #{@new_resource.mount_point} - nothing to do")
end
end
+ # Return appropriate default mount options according to the given os.
+ def default_mount_options
+ node[:os] == "linux" ? "defaults" : "rw"
+ end
+
def enable_fs
- if @current_resource.enabled && mount_options_unchanged?
- Chef::Log.debug("#{@new_resource} is already enabled - nothing to do")
+ if @current_resource.enabled && mount_options_unchanged? && device_unchanged?
+ logger.trace("#{@new_resource} is already enabled - nothing to do")
return nil
end
@@ -158,8 +164,8 @@ class Chef
disable_fs
end
::File.open("/etc/fstab", "a") do |fstab|
- fstab.puts("#{device_fstab} #{@new_resource.mount_point} #{@new_resource.fstype} #{@new_resource.options.nil? ? "defaults" : @new_resource.options.join(",")} #{@new_resource.dump} #{@new_resource.pass}")
- Chef::Log.debug("#{@new_resource} is enabled at #{@new_resource.mount_point}")
+ fstab.puts("#{device_fstab} #{@new_resource.mount_point} #{@new_resource.fstype} #{@new_resource.options.nil? ? default_mount_options : @new_resource.options.join(",")} #{@new_resource.dump} #{@new_resource.pass}")
+ logger.trace("#{@new_resource} is enabled at #{@new_resource.mount_point}")
end
end
@@ -171,7 +177,7 @@ class Chef
::File.readlines("/etc/fstab").reverse_each do |line|
if !found && line =~ /^#{device_fstab_regex}\s+#{Regexp.escape(@new_resource.mount_point)}\s/
found = true
- Chef::Log.debug("#{@new_resource} is removed from fstab")
+ logger.trace("#{@new_resource} is removed from fstab")
next
else
contents << line
@@ -182,7 +188,7 @@ class Chef
contents.reverse_each { |line| fstab.puts line }
end
else
- Chef::Log.debug("#{@new_resource} is not enabled - nothing to do")
+ logger.trace("#{@new_resource} is not enabled - nothing to do")
end
end
@@ -193,7 +199,7 @@ class Chef
def device_should_exist?
( @new_resource.device != "none" ) &&
( not network_device? ) &&
- ( not %w{ cgroup tmpfs fuse vboxsf }.include? @new_resource.fstype )
+ ( not %w{ cgroup tmpfs fuse vboxsf zfs }.include? @new_resource.fstype )
end
private
@@ -210,7 +216,7 @@ class Chef
end
def device_real
- if @real_device == nil
+ if @real_device.nil?
if @new_resource.device_type == :device
@real_device = @new_resource.device
else
@@ -253,7 +259,7 @@ class Chef
if @new_resource.device_type == :device
device_mount_regex
else
- device_fstab
+ Regexp.union(device_fstab, device_mount_regex)
end
end
diff --git a/lib/chef/provider/mount/solaris.rb b/lib/chef/provider/mount/solaris.rb
index a5a7a327cb..095c07432a 100644
--- a/lib/chef/provider/mount/solaris.rb
+++ b/lib/chef/provider/mount/solaris.rb
@@ -74,8 +74,8 @@ class Chef
end
def mount_fs
- actual_options = options || []
- actual_options.delete("noauto")
+ actual_options = native_options(options)
+ actual_options.delete("-")
command = "mount -F #{fstype}"
command << " -o #{actual_options.join(',')}" unless actual_options.empty?
command << " #{device} #{mount_point}"
@@ -88,8 +88,8 @@ class Chef
def remount_fs
# FIXME: Should remount always do the remount or only if the options change?
- actual_options = options || []
- actual_options.delete("noauto")
+ actual_options = native_options(options)
+ actual_options.delete("-")
mount_options = actual_options.empty? ? "" : ",#{actual_options.join(',')}"
shell_out!("mount -o remount#{mount_options} #{mount_point}")
end
@@ -112,7 +112,7 @@ class Chef
else
# this is likely some kind of internal error, since we should only call disable_fs when there
# the filesystem we want to disable is enabled.
- Chef::Log.warn("#{new_resource} did not find the mountpoint to disable in the vfstab")
+ logger.warn("#{new_resource} did not find the mountpoint to disable in the vfstab")
end
end
@@ -121,8 +121,8 @@ class Chef
end
def mount_options_unchanged?
- new_options = options_remove_noauto(options)
- current_options = options_remove_noauto(current_resource.nil? ? nil : current_resource.options)
+ new_options = native_options(options)
+ current_options = native_options(current_resource.nil? ? nil : current_resource.options)
current_resource.fsck_device == fsck_device &&
current_resource.fstype == fstype &&
@@ -153,10 +153,10 @@ class Chef
shell_out!("mount -v").stdout.each_line do |line|
case line
when /^#{device_regex}\s+on\s+#{Regexp.escape(mount_point)}\s+/
- Chef::Log.debug("Special device #{device} is mounted as #{mount_point}")
+ logger.trace("Special device #{device} is mounted as #{mount_point}")
mounted = true
when /^([\/\w]+)\son\s#{Regexp.escape(mount_point)}\s+/
- Chef::Log.debug("Special device #{Regexp.last_match[1]} is mounted as #{mount_point}")
+ logger.trace("Special device #{Regexp.last_match[1]} is mounted as #{mount_point}")
mounted = false
end
end
@@ -168,7 +168,8 @@ class Chef
def read_vfstab_status
# Check to see if there is an entry in /etc/vfstab. Last entry for a volume wins.
enabled = false
- fstype = options = pass = nil
+ pass = false
+ fstype = options = nil
::File.foreach(VFSTAB) do |line|
case line
when /^[#\s]/
@@ -190,12 +191,12 @@ class Chef
end
end
pass = (Regexp.last_match[2] == "-") ? 0 : Regexp.last_match[2].to_i
- Chef::Log.debug("Found mount #{device} to #{mount_point} in #{VFSTAB}")
+ logger.trace("Found mount #{device} to #{mount_point} in #{VFSTAB}")
next
when /^[-\/\w]+\s+[-\/\w]+\s+#{Regexp.escape(mount_point)}\s+/
# if we find a mountpoint on top of our mountpoint, then we are not enabled
enabled = false
- Chef::Log.debug("Found conflicting mount point #{mount_point} in #{VFSTAB}")
+ logger.trace("Found conflicting mount point #{mount_point} in #{VFSTAB}")
end
end
[enabled, fstype, options, pass]
@@ -220,11 +221,7 @@ class Chef
end
def vfstab_entry
- actual_options = unless options.nil?
- tempops = options.dup
- tempops.delete("noauto")
- tempops
- end
+ actual_options = native_options(options)
autostr = mount_at_boot? ? "yes" : "no"
passstr = pass == 0 ? "-" : pass
optstr = (actual_options.nil? || actual_options.empty?) ? "-" : actual_options.join(",")
@@ -237,7 +234,7 @@ class Chef
::File.readlines(VFSTAB).reverse_each do |line|
if !found && line =~ /^#{device_regex}\s+\S+\s+#{Regexp.escape(mount_point)}/
found = true
- Chef::Log.debug("#{new_resource} is removed from vfstab")
+ logger.trace("#{new_resource} is removed from vfstab")
next
end
contents << line
@@ -251,11 +248,15 @@ class Chef
contents << vfstab_entry
end
- def options_remove_noauto(temp_options)
- new_options = []
- new_options += temp_options.nil? ? [] : temp_options
- new_options.delete("noauto")
- new_options
+ def native_options(temp_options)
+ if temp_options == %w{defaults}
+ ["-"]
+ else
+ new_options = []
+ new_options += temp_options.nil? ? [] : temp_options.dup
+ new_options.delete("noauto")
+ new_options
+ end
end
def device_regex
diff --git a/lib/chef/provider/mount/windows.rb b/lib/chef/provider/mount/windows.rb
index 0fb5aa7645..1bd932729d 100644
--- a/lib/chef/provider/mount/windows.rb
+++ b/lib/chef/provider/mount/windows.rb
@@ -47,15 +47,15 @@ class Chef
@current_resource = Chef::Resource::Mount.new(@new_resource.name)
@current_resource.mount_point(@new_resource.mount_point)
- Chef::Log.debug("Checking for mount point #{@current_resource.mount_point}")
+ logger.trace("Checking for mount point #{@current_resource.mount_point}")
begin
@current_resource.device(@mount.device)
- Chef::Log.debug("#{@current_resource.device} mounted on #{@new_resource.mount_point}")
+ logger.trace("#{@current_resource.device} mounted on #{@new_resource.mount_point}")
@current_resource.mounted(true)
rescue ArgumentError => e
@current_resource.mounted(false)
- Chef::Log.debug("#{@new_resource.mount_point} is not mounted: #{e.message}")
+ logger.trace("#{@new_resource.mount_point} is not mounted: #{e.message}")
end
end
@@ -65,18 +65,18 @@ class Chef
:username => @new_resource.username,
:domainname => @new_resource.domain,
:password => @new_resource.password)
- Chef::Log.debug("#{@new_resource} is mounted at #{@new_resource.mount_point}")
+ logger.trace("#{@new_resource} is mounted at #{@new_resource.mount_point}")
else
- Chef::Log.debug("#{@new_resource} is already mounted at #{@new_resource.mount_point}")
+ logger.trace("#{@new_resource} is already mounted at #{@new_resource.mount_point}")
end
end
def umount_fs
if @current_resource.mounted
@mount.delete
- Chef::Log.debug("#{@new_resource} is no longer mounted at #{@new_resource.mount_point}")
+ logger.trace("#{@new_resource} is no longer mounted at #{@new_resource.mount_point}")
else
- Chef::Log.debug("#{@new_resource} is not mounted at #{@new_resource.mount_point}")
+ logger.trace("#{@new_resource} is not mounted at #{@new_resource.mount_point}")
end
end
diff --git a/lib/chef/provider/noop.rb b/lib/chef/provider/noop.rb
index 207bf7dedb..077da3f0b4 100644
--- a/lib/chef/provider/noop.rb
+++ b/lib/chef/provider/noop.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) 2016-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,7 +27,7 @@ class Chef
def method_missing(method_sym, *arguments, &block)
if method_sym.to_s =~ /^action_/
- Chef::Log.debug("NoOp-ing for #{method_sym}")
+ logger.trace("NoOp-ing for #{method_sym}")
else
super
end
diff --git a/lib/chef/provider/ohai.rb b/lib/chef/provider/ohai.rb
index 6b5a605ed5..0b65a1d28a 100644
--- a/lib/chef/provider/ohai.rb
+++ b/lib/chef/provider/ohai.rb
@@ -23,25 +23,21 @@ class Chef
class Ohai < Chef::Provider
provides :ohai
- def whyrun_supported?
- true
- end
-
def load_current_resource
true
end
- def action_reload
+ action :reload do
converge_by("re-run ohai and merge results into node attributes") do
ohai = ::Ohai::System.new
- # If @new_resource.plugin is nil, ohai will reload all the plugins
+ # If new_resource.plugin is nil, ohai will reload all the plugins
# Otherwise it will only reload the specified plugin
# Note that any changes to plugins, or new plugins placed on
# the path are picked up by ohai.
- ohai.all_plugins @new_resource.plugin
+ ohai.all_plugins new_resource.plugin
node.automatic_attrs.merge! ohai.data
- Chef::Log.info("#{@new_resource} reloaded")
+ logger.info("#{new_resource} reloaded")
end
end
end
diff --git a/lib/chef/provider/osx_profile.rb b/lib/chef/provider/osx_profile.rb
index 6ac67e0560..e753f84d86 100644
--- a/lib/chef/provider/osx_profile.rb
+++ b/lib/chef/provider/osx_profile.rb
@@ -25,75 +25,71 @@ require "uuidtools"
class Chef
class Provider
class OsxProfile < Chef::Provider
- include Chef::Mixin::Command
- provides :osx_profile, os: "darwin"
- provides :osx_config_profile, os: "darwin"
-
- def whyrun_supported?
- true
- end
+ provides :osx_profile
+ provides :osx_config_profile
def load_current_resource
- @current_resource = Chef::Resource::OsxProfile.new(@new_resource.name)
- @current_resource.profile_name(@new_resource.profile_name)
+ @current_resource = Chef::Resource::OsxProfile.new(new_resource.name)
+ current_resource.profile_name(new_resource.profile_name)
all_profiles = get_installed_profiles
- @new_resource.profile(
- @new_resource.profile ||
- @new_resource.profile_name
+ new_resource.profile(
+ new_resource.profile ||
+ new_resource.profile_name
)
- @new_profile_hash = get_profile_hash(@new_resource.profile)
- @new_profile_hash["PayloadUUID"] =
- config_uuid(@new_profile_hash) if @new_profile_hash
+ @new_profile_hash = get_profile_hash(new_resource.profile)
+ if @new_profile_hash
+ @new_profile_hash["PayloadUUID"] =
+ config_uuid(@new_profile_hash)
+ end
if @new_profile_hash
@new_profile_identifier = @new_profile_hash["PayloadIdentifier"]
else
- @new_profile_identifier = @new_resource.identifier ||
- @new_resource.profile_name
+ @new_profile_identifier = new_resource.identifier ||
+ new_resource.profile_name
end
- if all_profiles.empty?
- current_profile = nil
- else
+ current_profile = nil
+ if all_profiles && all_profiles.key?("_computerlevel")
current_profile = all_profiles["_computerlevel"].find do |item|
item["ProfileIdentifier"] == @new_profile_identifier
end
end
- @current_resource.profile(current_profile)
+ current_resource.profile(current_profile)
end
def define_resource_requirements
requirements.assert(:remove) do |a|
if @new_profile_identifier
- a.assertion {
+ a.assertion do
!@new_profile_identifier.nil? &&
!@new_profile_identifier.end_with?(".mobileconfig") &&
- /^\w+(?:\.\w+)+$/.match(@new_profile_identifier)
- }
+ /^\w+(?:(\.| )\w+)+$/.match(@new_profile_identifier)
+ end
a.failure_message RuntimeError, "when removing using the identifier attribute, it must match the profile identifier"
else
- new_profile_name = @new_resource.profile_name
- a.assertion {
+ new_profile_name = new_resource.profile_name
+ a.assertion do
!new_profile_name.end_with?(".mobileconfig") &&
- /^\w+(?:\.\w+)+$/.match(new_profile_name)
- }
+ /^\w+(?:(\.| )\w+)+$/.match(new_profile_name)
+ end
a.failure_message RuntimeError, "When removing by resource name, it must match the profile identifier "
end
end
requirements.assert(:install) do |a|
if @new_profile_hash.is_a?(Hash)
- a.assertion {
+ a.assertion do
@new_profile_hash.include?("PayloadIdentifier")
- }
+ end
a.failure_message RuntimeError, "The specified profile does not seem to be valid"
end
if @new_profile_hash.is_a?(String)
- a.assertion {
+ a.assertion do
@new_profile_hash.end_with?(".mobileconfig")
- }
+ end
a.failure_message RuntimeError, "#{new_profile_hash}' is not a valid profile"
end
end
@@ -127,21 +123,21 @@ class Chef
raise Chef::Exceptions::FileNotFound, error_string
end
cookbook_profile = cache_cookbook_profile(new_profile)
- return read_plist(cookbook_profile)
+ read_plist(cookbook_profile)
else
- return nil
+ nil
end
end
def cookbook_file_available?(cookbook_file)
run_context.has_cookbook_file_in_cookbook?(
- @new_resource.cookbook_name, cookbook_file
+ new_resource.cookbook_name, cookbook_file
)
end
def get_cache_dir
cache_dir = Chef::FileCache.create_cache_path(
- "profiles/#{@new_resource.cookbook_name}"
+ "profiles/#{new_resource.cookbook_name}"
)
end
@@ -149,7 +145,7 @@ class Chef
Chef::FileCache.create_cache_path(
::File.join(
"profiles",
- @new_resource.cookbook_name,
+ new_resource.cookbook_name,
::File.dirname(cookbook_file)
)
)
@@ -160,7 +156,7 @@ class Chef
),
run_context
)
- remote_file.cookbook_name = @new_resource.cookbook_name
+ remote_file.cookbook_name = new_resource.cookbook_name
remote_file.source(cookbook_file)
remote_file.backup(false)
remote_file.run_action(:create)
@@ -169,9 +165,9 @@ class Chef
def get_profile_hash(new_profile)
if new_profile.is_a?(Hash)
- return new_profile
+ new_profile
elsif new_profile.is_a?(String)
- return load_profile_hash(new_profile)
+ load_profile_hash(new_profile)
end
end
@@ -184,8 +180,8 @@ class Chef
end
def write_profile_to_disk
- @new_resource.path(Chef::FileCache.create_cache_path("profiles"))
- tempfile = Chef::FileContentManagement::Tempfile.new(@new_resource).tempfile
+ new_resource.path(Chef::FileCache.create_cache_path("profiles"))
+ tempfile = Chef::FileContentManagement::Tempfile.new(new_resource).tempfile
tempfile.write(@new_profile_hash.to_plist)
tempfile.close
tempfile.path
@@ -193,14 +189,14 @@ class Chef
def install_profile(profile_path)
cmd = "profiles -I -F '#{profile_path}'"
- Chef::Log.debug("cmd: #{cmd}")
+ logger.trace("cmd: #{cmd}")
shellout_results = shell_out(cmd)
shellout_results.exitstatus
end
def remove_profile
cmd = "profiles -R -p '#{@new_profile_identifier}'"
- Chef::Log.debug("cmd: #{cmd}")
+ logger.trace("cmd: #{cmd}")
shellout_results = shell_out(cmd)
shellout_results.exitstatus
end
@@ -218,7 +214,7 @@ class Chef
tempfile = generate_tempfile
write_installed_profiles(tempfile)
installed_profiles = read_plist(tempfile)
- Chef::Log.debug("Saved profiles to run_state")
+ logger.trace("Saved profiles to run_state")
# Clean up the temp file as we do not need it anymore
::File.unlink(tempfile)
installed_profiles
@@ -239,13 +235,13 @@ class Chef
def profile_installed?
# Profile Identifier and UUID must match a currently installed profile
- if @current_resource.profile.nil? || @current_resource.profile.empty?
+ if current_resource.profile.nil? || current_resource.profile.empty?
false
else
- if @new_resource.action.include?(:remove)
+ if new_resource.action.include?(:remove)
true
else
- @current_resource.profile["ProfileUUID"] ==
+ current_resource.profile["ProfileUUID"] ==
@new_profile_hash["PayloadUUID"]
end
end
diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb
index ca9b526920..133f87dad9 100644
--- a/lib/chef/provider/package.rb
+++ b/lib/chef/provider/package.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,16 +17,16 @@
#
require "chef/mixin/shell_out"
-require "chef/mixin/command"
require "chef/mixin/subclass_directive"
require "chef/log"
require "chef/file_cache"
require "chef/platform"
+require "chef/decorator/lazy_array"
+require "shellwords"
class Chef
class Provider
class Package < Chef::Provider
- include Chef::Mixin::Command
include Chef::Mixin::ShellOut
extend Chef::Mixin::SubclassDirective
@@ -34,6 +34,9 @@ class Chef
subclass_directive :use_multipackage_api
# subclasses declare this if they want sources (filenames) pulled from their package names
subclass_directive :use_package_name_for_source
+ # keeps package_names_for_targets and versions_for_targets indexed the same as package_name at
+ # the cost of having the subclass needing to deal with nils
+ subclass_directive :allow_nils
#
# Hook that subclasses use to populate the candidate_version(s)
@@ -46,28 +49,27 @@ class Chef
@candidate_version = nil
end
- def whyrun_supported?
- true
+ def options
+ new_resource.options
end
def check_resource_semantics!
# FIXME: this is not universally true and subclasses are needing to override this and no-ops it. It should be turned into
# another "subclass_directive" and the apt and yum providers should declare that they need this behavior.
- if new_resource.package_name.is_a?(Array) && new_resource.source != nil
+ if new_resource.package_name.is_a?(Array) && !new_resource.source.nil?
raise Chef::Exceptions::InvalidResourceSpecification, "You may not specify both multipackage and source"
end
end
- def load_current_resource
- end
+ def load_current_resource; end
def define_resource_requirements
# XXX: upgrade with a specific version doesn't make a whole lot of sense, but why don't we throw this anyway if it happens?
# if not, shouldn't we raise to tell the user to use install instead of upgrade if they want to pin a version?
requirements.assert(:install) do |a|
a.assertion { candidates_exist_for_all_forced_changes? }
- a.failure_message(Chef::Exceptions::Package, "No version specified, and no candidate version available for #{forced_packages_missing_candidates.join(", ")}")
- a.whyrun("Assuming a repository that offers #{forced_packages_missing_candidates.join(", ")} would have been configured")
+ a.failure_message(Chef::Exceptions::Package, "No version specified, and no candidate version available for #{forced_packages_missing_candidates.join(', ')}")
+ a.whyrun("Assuming a repository that offers #{forced_packages_missing_candidates.join(', ')} would have been configured")
end
# XXX: Does it make sense to pass in a source with :upgrade? Probably
@@ -75,19 +77,19 @@ class Chef
# so we'll just leave things as-is for now.
requirements.assert(:upgrade, :install) do |a|
a.assertion { candidates_exist_for_all_uninstalled? || new_resource.source }
- a.failure_message(Chef::Exceptions::Package, "No candidate version available for #{packages_missing_candidates.join(", ")}")
- a.whyrun("Assuming a repository that offers #{packages_missing_candidates.join(", ")} would have been configured")
+ a.failure_message(Chef::Exceptions::Package, "No candidate version available for #{packages_missing_candidates.join(', ')}")
+ a.whyrun("Assuming a repository that offers #{packages_missing_candidates.join(', ')} would have been configured")
end
end
- def action_install
+ action :install do
unless target_version_array.any?
- Chef::Log.debug("#{@new_resource} is already installed - nothing to do")
+ logger.trace("#{new_resource} is already installed - nothing to do")
return
end
# @todo: move the preseed code out of the base class (and complete the fix for Array of preseeds? ugh...)
- if @new_resource.response_file
+ if new_resource.response_file
if preseed_file = get_preseed_file(package_names_for_targets, versions_for_targets)
converge_by("preseed package #{package_names_for_targets}") do
preseed_package(preseed_file)
@@ -99,7 +101,7 @@ class Chef
multipackage_api_adapter(package_names_for_targets, versions_for_targets) do |name, version|
install_package(name, version)
end
- Chef::Log.info("#{@new_resource} installed #{package_names_for_targets} at #{versions_for_targets}")
+ logger.info("#{new_resource} installed #{package_names_for_targets} at #{versions_for_targets}")
end
end
@@ -115,9 +117,9 @@ class Chef
private :install_description
- def action_upgrade
- if !target_version_array.any?
- Chef::Log.debug("#{@new_resource} no versions to upgrade - nothing to do")
+ action :upgrade do
+ unless target_version_array.any?
+ logger.trace("#{new_resource} no versions to upgrade - nothing to do")
return
end
@@ -126,7 +128,7 @@ class Chef
upgrade_package(name, version)
end
log_allow_downgrade = allow_downgrade ? "(allow_downgrade)" : ""
- Chef::Log.info("#{@new_resource} upgraded#{log_allow_downgrade} #{package_names_for_targets} to #{versions_for_targets}")
+ logger.info("#{new_resource} upgraded#{log_allow_downgrade} #{package_names_for_targets} to #{versions_for_targets}")
end
end
@@ -145,17 +147,17 @@ class Chef
private :upgrade_description
- def action_remove
+ action :remove do
if removing_package?
- description = @new_resource.version ? "version #{@new_resource.version} of " : ""
- converge_by("remove #{description}package #{@current_resource.package_name}") do
- multipackage_api_adapter(@current_resource.package_name, @new_resource.version) do |name, version|
+ description = new_resource.version ? "version #{new_resource.version} of " : ""
+ converge_by("remove #{description}package #{current_resource.package_name}") do
+ multipackage_api_adapter(current_resource.package_name, new_resource.version) do |name, version|
remove_package(name, version)
end
- Chef::Log.info("#{@new_resource} removed")
+ logger.info("#{new_resource} removed")
end
else
- Chef::Log.debug("#{@new_resource} package does not exist - nothing to do")
+ logger.trace("#{new_resource} package does not exist - nothing to do")
end
end
@@ -180,43 +182,86 @@ class Chef
end
end
- def action_purge
+ action :purge do
if removing_package?
- description = @new_resource.version ? "version #{@new_resource.version} of" : ""
- converge_by("purge #{description} package #{@current_resource.package_name}") do
- multipackage_api_adapter(@current_resource.package_name, @new_resource.version) do |name, version|
+ description = new_resource.version ? "version #{new_resource.version} of" : ""
+ converge_by("purge #{description} package #{current_resource.package_name}") do
+ multipackage_api_adapter(current_resource.package_name, new_resource.version) do |name, version|
purge_package(name, version)
end
- Chef::Log.info("#{@new_resource} purged")
+ logger.info("#{new_resource} purged")
end
end
end
- def action_reconfig
- if @current_resource.version == nil then
- Chef::Log.debug("#{@new_resource} is NOT installed - nothing to do")
+ action :reconfig do
+ if current_resource.version.nil?
+ logger.trace("#{new_resource} is NOT installed - nothing to do")
return
end
- unless @new_resource.response_file then
- Chef::Log.debug("#{@new_resource} no response_file provided - nothing to do")
+ unless new_resource.response_file
+ logger.trace("#{new_resource} no response_file provided - nothing to do")
return
end
- if preseed_file = get_preseed_file(@new_resource.package_name, @current_resource.version)
- converge_by("reconfigure package #{@new_resource.package_name}") do
+ if preseed_file = get_preseed_file(new_resource.package_name, current_resource.version)
+ converge_by("reconfigure package #{new_resource.package_name}") do
preseed_package(preseed_file)
- multipackage_api_adapter(@new_resource.package_name, @current_resource.version) do |name, version|
+ multipackage_api_adapter(new_resource.package_name, current_resource.version) do |name, version|
reconfig_package(name, version)
end
- Chef::Log.info("#{@new_resource} reconfigured")
+ logger.info("#{new_resource} reconfigured")
+ end
+ else
+ logger.trace("#{new_resource} preseeding has not changed - nothing to do")
+ end
+ end
+
+ def action_lock
+ packages_locked = if respond_to?(:packages_all_locked?, true)
+ packages_all_locked?(Array(new_resource.package_name), Array(new_resource.version))
+ else
+ package_locked(new_resource.package_name, new_resource.version)
+ end
+ unless packages_locked
+ description = new_resource.version ? "version #{new_resource.version} of " : ""
+ converge_by("lock #{description}package #{current_resource.package_name}") do
+ multipackage_api_adapter(current_resource.package_name, new_resource.version) do |name, version|
+ lock_package(name, version)
+ logger.info("#{new_resource} locked")
+ end
end
else
- Chef::Log.debug("#{@new_resource} preseeding has not changed - nothing to do")
+ logger.trace("#{new_resource} is already locked")
end
end
+ def action_unlock
+ packages_unlocked = if respond_to?(:packages_all_unlocked?, true)
+ packages_all_unlocked?(Array(new_resource.package_name), Array(new_resource.version))
+ else
+ !package_locked(new_resource.package_name, new_resource.version)
+ end
+ unless packages_unlocked
+ description = new_resource.version ? "version #{new_resource.version} of " : ""
+ converge_by("unlock #{description}package #{current_resource.package_name}") do
+ multipackage_api_adapter(current_resource.package_name, new_resource.version) do |name, version|
+ unlock_package(name, version)
+ logger.info("#{new_resource} unlocked")
+ end
+ end
+ else
+ logger.trace("#{new_resource} is already unlocked")
+ end
+ end
+
+ # for multipackage just implement packages_all_[un]locked? properly and omit implementing this API
+ def package_locked(name, version)
+ raise Chef::Exceptions::UnsupportedAction, "#{self} has no way to detect if package is locked"
+ end
+
# @todo use composition rather than inheritance
def multipackage_api_adapter(name, version)
@@ -251,21 +296,94 @@ class Chef
raise( Chef::Exceptions::UnsupportedAction, "#{self} does not support :reconfig" )
end
+ def lock_package(name, version)
+ raise( Chef::Exceptions::UnsupportedAction, "#{self} does not support :lock" )
+ end
+
+ def unlock_package(name, version)
+ raise( Chef::Exceptions::UnsupportedAction, "#{self} does not support :unlock" )
+ end
+
# used by subclasses. deprecated. use #a_to_s instead.
def expand_options(options)
- options ? " #{options}" : ""
+ # its deprecated but still work to do to deprecate it fully
+ #Chef.deprecated(:package_misc, "expand_options is deprecated, use shell_out_compact or shell_out_compact_timeout instead")
+ if options
+ " #{options.is_a?(Array) ? Shellwords.join(options) : options}"
+ else
+ ""
+ end
end
- # this is public and overridden by subclasses (rubygems package implements '>=' and '~>' operators)
- def target_version_already_installed?(current_version, new_version)
- new_version == current_version
+ # Check the current_version against either the candidate_version or the new_version
+ #
+ # For some reason the windows provider subclasses this (to implement passing Arrays to
+ # versions for some reason other than multipackage stuff, which is mildly terrifying).
+ #
+ # This MUST have 'equality' semantics -- the exact thing matches the exact thing.
+ #
+ # The name is not just bad, but i find it completely misleading, consider:
+ #
+ # target_version_already_installed?(current_version, new_version)
+ # target_version_already_installed?(current_version, candidate_version)
+ #
+ # Which of those is the 'target_version'? I'd say the new_version and I'm confused when
+ # i see it called with the candidate_version.
+ #
+ # `version_equals?(v1, v2)` would be a better name.
+ #
+ # Note that most likely we need a spaceship operator on versions that subclasses can implement
+ # and we should have `version_compare(v1, v2)` that returns `v1 <=> v2`.
+
+ # This method performs a strict equality check between two strings representing version numbers
+ #
+ # This function will eventually be deprecated in favour of the below version_equals function.
+
+ def target_version_already_installed?(current_version, target_version)
+ version_equals?(current_version, target_version)
+ end
+
+ # Note that most likely we need a spaceship operator on versions that subclasses can implement
+ # and we should have `version_compare(v1, v2)` that returns `v1 <=> v2`.
+
+ # This method performs a strict equality check between two strings representing version numbers
+ #
+ def version_equals?(v1, v2)
+ return false unless v1 && v2
+ v1 == v2
+ end
+
+ # This function compares two version numbers and returns 'spaceship operator' style results, ie:
+ # if v1 < v2 then return -1
+ # if v1 = v2 then return 0
+ # if v1 > v2 then return 1
+ # if v1 and v2 are not comparable then return nil
+ #
+ # By default, this function will use Gem::Version comparison. Subclasses can reimplement this method
+ # for package-management system specific versions.
+ def version_compare(v1, v2)
+ gem_v1 = Gem::Version.new(v1)
+ gem_v2 = Gem::Version.new(v2)
+
+ gem_v1 <=> gem_v2
+ end
+
+ # Check the current_version against the new_resource.version, possibly using fuzzy
+ # matching criteria.
+ #
+ # Subclasses MAY override this to provide fuzzy matching on the resource ('>=' and '~>' stuff)
+ #
+ # `version_satisfied_by?(version, constraint)` might be a better name to make this generic.
+ #
+ def version_requirement_satisfied?(current_version, new_version)
+ target_version_already_installed?(current_version, new_version)
end
# @todo: extract apt/dpkg specific preseeding to a helper class
def get_preseed_file(name, version)
resource = preseed_resource(name, version)
resource.run_action(:create)
- Chef::Log.debug("#{@new_resource} fetched preseed file to #{resource.path}")
+ logger.trace("#{new_resource} fetched preseed file to #{resource.path}")
if resource.updated_by_last_action?
resource.path
@@ -277,26 +395,26 @@ class Chef
# @todo: extract apt/dpkg specific preseeding to a helper class
def preseed_resource(name, version)
# A directory in our cache to store this cookbook's preseed files in
- file_cache_dir = Chef::FileCache.create_cache_path("preseed/#{@new_resource.cookbook_name}")
+ file_cache_dir = Chef::FileCache.create_cache_path("preseed/#{new_resource.cookbook_name}")
# The full path where the preseed file will be stored
cache_seed_to = "#{file_cache_dir}/#{name}-#{version}.seed"
- Chef::Log.debug("#{@new_resource} fetching preseed file to #{cache_seed_to}")
+ logger.trace("#{new_resource} fetching preseed file to #{cache_seed_to}")
- if template_available?(@new_resource.response_file)
- Chef::Log.debug("#{@new_resource} fetching preseed file via Template")
+ if template_available?(new_resource.response_file)
+ logger.trace("#{new_resource} fetching preseed file via Template")
remote_file = Chef::Resource::Template.new(cache_seed_to, run_context)
- remote_file.variables(@new_resource.response_file_variables)
- elsif cookbook_file_available?(@new_resource.response_file)
- Chef::Log.debug("#{@new_resource} fetching preseed file via cookbook_file")
+ remote_file.variables(new_resource.response_file_variables)
+ elsif cookbook_file_available?(new_resource.response_file)
+ logger.trace("#{new_resource} fetching preseed file via cookbook_file")
remote_file = Chef::Resource::CookbookFile.new(cache_seed_to, run_context)
else
- message = "No template or cookbook file found for response file #{@new_resource.response_file}"
+ message = "No template or cookbook file found for response file #{new_resource.response_file}"
raise Chef::Exceptions::FileNotFound, message
end
- remote_file.cookbook_name = @new_resource.cookbook_name
- remote_file.source(@new_resource.response_file)
+ remote_file.cookbook_name = new_resource.cookbook_name
+ remote_file.source(new_resource.response_file)
remote_file.backup(false)
remote_file
end
@@ -319,9 +437,12 @@ class Chef
def package_names_for_targets
package_names_for_targets = []
target_version_array.each_with_index do |target_version, i|
- next if target_version.nil?
- package_name = package_name_array[i]
- package_names_for_targets.push(package_name)
+ if !target_version.nil?
+ package_name = package_name_array[i]
+ package_names_for_targets.push(package_name)
+ else
+ package_names_for_targets.push(nil) if allow_nils?
+ end
end
multipackage? ? package_names_for_targets : package_names_for_targets[0]
end
@@ -336,8 +457,11 @@ class Chef
def versions_for_targets
versions_for_targets = []
target_version_array.each_with_index do |target_version, i|
- next if target_version.nil?
- versions_for_targets.push(target_version)
+ if !target_version.nil?
+ versions_for_targets.push(target_version)
+ else
+ versions_for_targets.push(nil) if allow_nils?
+ end
end
multipackage? ? versions_for_targets : versions_for_targets[0]
end
@@ -354,33 +478,42 @@ class Chef
each_package do |package_name, new_version, current_version, candidate_version|
case action
when :upgrade
-
- if !candidate_version
- Chef::Log.debug("#{new_resource} #{package_name} has no candidate_version to upgrade to")
+ if version_equals?(current_version, new_version)
+ # this is an odd use case
+ logger.trace("#{new_resource} #{package_name} #{new_version} is already installed -- you are equality pinning with an :upgrade action, this may be deprecated in the future")
+ target_version_array.push(nil)
+ elsif version_equals?(current_version, candidate_version)
+ logger.trace("#{new_resource} #{package_name} #{candidate_version} is already installed")
target_version_array.push(nil)
- elsif current_version == candidate_version
- Chef::Log.debug("#{new_resource} #{package_name} the #{candidate_version} is already installed")
+ elsif candidate_version.nil?
+ logger.trace("#{new_resource} #{package_name} has no candidate_version to upgrade to")
+ target_version_array.push(nil)
+ elsif current_version.nil?
+ logger.trace("#{new_resource} has no existing installed version. Installing install #{candidate_version}")
+ target_version_array.push(candidate_version)
+ elsif version_compare(current_version, candidate_version) == 1 && !allow_downgrade
+ logger.trace("#{new_resource} #{package_name} has installed version #{current_version}, which is newer than available version #{candidate_version}. Skipping...)")
target_version_array.push(nil)
else
- Chef::Log.debug("#{new_resource} #{package_name} is out of date, will upgrade to #{candidate_version}")
+ logger.trace("#{new_resource} #{package_name} is out of date, will upgrade to #{candidate_version}")
target_version_array.push(candidate_version)
end
when :install
if new_version
- if target_version_already_installed?(current_version, new_version)
- Chef::Log.debug("#{new_resource} #{package_name} #{current_version} satisifies #{new_version} requirement")
+ if version_requirement_satisfied?(current_version, new_version)
+ logger.trace("#{new_resource} #{package_name} #{current_version} satisifies #{new_version} requirement")
target_version_array.push(nil)
else
- Chef::Log.debug("#{new_resource} #{package_name} #{current_version} needs updating to #{new_version}")
+ logger.trace("#{new_resource} #{package_name} #{current_version} needs updating to #{new_version}")
target_version_array.push(new_version)
end
elsif current_version.nil?
- Chef::Log.debug("#{new_resource} #{package_name} not installed, installing #{candidate_version}")
+ logger.trace("#{new_resource} #{package_name} not installed, installing #{candidate_version}")
target_version_array.push(candidate_version)
else
- Chef::Log.debug("#{new_resource} #{package_name} #{current_version} already installed")
+ logger.trace("#{new_resource} #{package_name} #{current_version} already installed")
target_version_array.push(nil)
end
@@ -411,7 +544,7 @@ class Chef
begin
missing = []
each_package do |package_name, new_version, current_version, candidate_version|
- missing.push(package_name) if candidate_version.nil? && current_version.nil?
+ missing.push(package_name) if current_version.nil? && candidate_version.nil?
end
missing
end
@@ -436,7 +569,7 @@ class Chef
missing = []
each_package do |package_name, new_version, current_version, candidate_version|
next if new_version.nil? || current_version.nil?
- if candidate_version.nil? && !target_version_already_installed?(current_version, new_version)
+ if !version_requirement_satisfied?(current_version, new_version) && candidate_version.nil?
missing.push(package_name)
end
end
@@ -458,27 +591,29 @@ class Chef
# @return [Boolean] if we're doing a multipackage install or not
def multipackage?
- new_resource.package_name.is_a?(Array)
+ @multipackage_bool ||= new_resource.package_name.is_a?(Array)
end
# @return [Array] package_name(s) as an array
def package_name_array
- [ new_resource.package_name ].flatten
+ @package_name_array ||= [ new_resource.package_name ].flatten
end
# @return [Array] candidate_version(s) as an array
def candidate_version_array
- [ candidate_version ].flatten
+ # NOTE: even with use_multipackage_api candidate_version may be a bare nil and need wrapping
+ # ( looking at you, dpkg provider... )
+ Chef::Decorator::LazyArray.new { [ candidate_version ].flatten }
end
# @return [Array] current_version(s) as an array
def current_version_array
- [ current_resource.version ].flatten
+ @current_version_array ||= [ current_resource.version ].flatten
end
# @return [Array] new_version(s) as an array
def new_version_array
- [ new_resource.version ].flatten.map { |v| v.to_s.empty? ? nil : v }
+ @new_version_array ||= [ new_resource.version ].flatten.map { |v| v.to_s.empty? ? nil : v }
end
# TIP: less error prone to simply always call resolved_source_array, even if you
@@ -486,11 +621,14 @@ class Chef
#
# @return [Array] new_resource.source as an array
def source_array
- if new_resource.source.nil?
- package_name_array.map { nil }
- else
- [ new_resource.source ].flatten
- end
+ @source_array ||=
+ begin
+ if new_resource.source.nil?
+ package_name_array.map { nil }
+ else
+ [ new_resource.source ].flatten
+ end
+ end
end
# Helper to handle use_package_name_for_source to convert names into local packages to install.
@@ -503,7 +641,7 @@ class Chef
package_name = package_name_array[i]
# we require at least one '/' in the package_name to avoid [XXX_]package 'foo' breaking due to a random 'foo' file in cwd
if use_package_name_for_source? && source.nil? && package_name.match(/#{::File::SEPARATOR}/) && ::File.exist?(package_name)
- Chef::Log.debug("No package source specified, but #{package_name} exists on filesystem, using #{package_name} as source.")
+ logger.trace("No package source specified, but #{package_name} exists on filesystem, using #{package_name} as source.")
package_name
else
source
@@ -523,8 +661,8 @@ class Chef
end
def allow_downgrade
- if @new_resource.respond_to?("allow_downgrade")
- @new_resource.allow_downgrade
+ if new_resource.respond_to?("allow_downgrade")
+ new_resource.allow_downgrade
else
false
end
@@ -539,27 +677,19 @@ class Chef
end
def add_timeout_option(command_args)
+ # this is deprecated but its not quite done yet
+ #Chef.deprecated(:package_misc, "shell_out_with_timeout and add_timeout_option are deprecated methods, use shell_out_compact_timeout instead")
args = command_args.dup
if args.last.is_a?(Hash)
options = args.pop.dup
options[:timeout] = new_resource.timeout if new_resource.timeout
- options[:timeout] = 900 unless options.has_key?(:timeout)
+ options[:timeout] = 900 unless options.key?(:timeout)
args << options
else
- args << { :timeout => new_resource.timeout ? new_resource.timeout : 900 }
+ args << { timeout: new_resource.timeout ? new_resource.timeout : 900 }
end
args
end
-
- # Helper for sublcasses to convert an array of string args into a string. It
- # will compact nil or empty strings in the array and will join the array elements
- # with spaces, without introducing any double spaces for nil/empty elements.
- #
- # @param args [String] variable number of string arguments
- # @return [String] nicely concatenated string or empty string
- def a_to_s(*args)
- args.reject { |i| i.nil? || i == "" }.join(" ")
- end
end
end
end
diff --git a/lib/chef/provider/package/aix.rb b/lib/chef/provider/package/aix.rb
deleted file mode 100644
index a1709c4af7..0000000000
--- a/lib/chef/provider/package/aix.rb
+++ /dev/null
@@ -1,136 +0,0 @@
-#
-# Author:: Deepali Jagtap
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-#
-require "chef/provider/package"
-require "chef/mixin/command"
-require "chef/resource/package"
-require "chef/mixin/get_source_from_package"
-
-class Chef
- class Provider
- class Package
- class Aix < Chef::Provider::Package
-
- provides :package, os: "aix"
- provides :bff_package, os: "aix"
-
- include Chef::Mixin::GetSourceFromPackage
-
- def define_resource_requirements
- super
- requirements.assert(:install) do |a|
- a.assertion { @new_resource.source }
- a.failure_message Chef::Exceptions::Package, "Source for package #{@new_resource.name} required for action install"
- end
- requirements.assert(:all_actions) do |a|
- a.assertion { !@new_resource.source || @package_source_found }
- a.failure_message Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}"
- a.whyrun "would assume #{@new_resource.source} would be have previously been made available"
- end
- end
-
- def load_current_resource
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
-
- if @new_resource.source
- @package_source_found = ::File.exists?(@new_resource.source)
- if @package_source_found
- Chef::Log.debug("#{@new_resource} checking pkg status")
- ret = shell_out_with_timeout("installp -L -d #{@new_resource.source}")
- ret.stdout.each_line do |line|
- case line
- when /#{@new_resource.package_name}:/
- fields = line.split(":")
- @new_resource.version(fields[2])
- end
- end
- raise Chef::Exceptions::Package, "package source #{@new_resource.source} does not provide package #{@new_resource.package_name}" unless @new_resource.version
- end
- end
-
- Chef::Log.debug("#{@new_resource} checking install state")
- ret = shell_out_with_timeout("lslpp -lcq #{@current_resource.package_name}")
- ret.stdout.each_line do |line|
- case line
- when /#{@current_resource.package_name}/
- fields = line.split(":")
- Chef::Log.debug("#{@new_resource} version #{fields[2]} is already installed")
- @current_resource.version(fields[2])
- end
- end
-
- unless ret.exitstatus == 0 || ret.exitstatus == 1
- raise Chef::Exceptions::Package, "lslpp failed - #{ret.format_for_exception}!"
- end
-
- @current_resource
- end
-
- def candidate_version
- return @candidate_version if @candidate_version
- ret = shell_out_with_timeout("installp -L -d #{@new_resource.source}")
- ret.stdout.each_line do |line|
- case line
- when /\w:#{Regexp.escape(@new_resource.package_name)}:(.*)/
- fields = line.split(":")
- @candidate_version = fields[2]
- @new_resource.version(fields[2])
- Chef::Log.debug("#{@new_resource} setting install candidate version to #{@candidate_version}")
- end
- end
- unless ret.exitstatus == 0
- raise Chef::Exceptions::Package, "installp -L -d #{@new_resource.source} - #{ret.format_for_exception}!"
- end
- @candidate_version
- end
-
- #
- # The install/update action needs to be tested with various kinds of packages
- # on AIX viz. packages with or without licensing file dependencies, packages
- # with dependencies on other packages which will help to test additional
- # options of installp.
- # So far, the code has been tested only with standalone packages.
- #
- def install_package(name, version)
- Chef::Log.debug("#{@new_resource} package install options: #{@new_resource.options}")
- if @new_resource.options.nil?
- shell_out_with_timeout!( "installp -aYF -d #{@new_resource.source} #{@new_resource.package_name}" )
- Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}")
- else
- shell_out_with_timeout!( "installp -aYF #{expand_options(@new_resource.options)} -d #{@new_resource.source} #{@new_resource.package_name}" )
- Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}")
- end
- end
-
- alias_method :upgrade_package, :install_package
-
- def remove_package(name, version)
- if @new_resource.options.nil?
- shell_out_with_timeout!( "installp -u #{name}" )
- Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}")
- else
- shell_out_with_timeout!( "installp -u #{expand_options(@new_resource.options)} #{name}" )
- Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}")
- end
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/provider/package/apt.rb b/lib/chef/provider/package/apt.rb
index ac730202b8..798abf4680 100644
--- a/lib/chef/provider/package/apt.rb
+++ b/lib/chef/provider/package/apt.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,132 +17,82 @@
#
require "chef/provider/package"
-require "chef/mixin/command"
-require "chef/resource/package"
+require "chef/resource/apt_package"
class Chef
class Provider
class Package
class Apt < Chef::Provider::Package
+ use_multipackage_api
provides :package, platform_family: "debian"
- provides :apt_package, os: "linux"
-
- # return [Hash] mapping of package name to Boolean value
- attr_accessor :is_virtual_package
+ provides :apt_package
def initialize(new_resource, run_context)
super
- @is_virtual_package = {}
end
def load_current_resource
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
- check_all_packages_state(@new_resource.package_name)
- @current_resource
+ @current_resource = Chef::Resource::AptPackage.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
+ current_resource.version(get_current_versions)
+ current_resource
end
def define_resource_requirements
super
requirements.assert(:all_actions) do |a|
- a.assertion { !@new_resource.source }
+ a.assertion { !new_resource.source }
a.failure_message(Chef::Exceptions::Package, "apt package provider cannot handle source attribute. Use dpkg provider instead")
end
end
- def default_release_options
- # Use apt::Default-Release option only if provider supports it
- "-o APT::Default-Release=#{@new_resource.default_release}" if @new_resource.respond_to?(:default_release) && @new_resource.default_release
+ def package_data
+ @package_data ||= Hash.new do |hash, key|
+ hash[key] = package_data_for(key)
+ end
end
- def check_package_state(pkg)
- is_virtual_package = false
- installed = false
- installed_version = nil
- candidate_version = nil
+ def get_current_versions
+ package_name_array.map do |package_name|
+ package_data[package_name][:current_version]
+ end
+ end
- shell_out_with_timeout!("apt-cache#{expand_options(default_release_options)} policy #{pkg}").stdout.each_line do |line|
- case line
- when /^\s{2}Installed: (.+)$/
- installed_version = $1
- if installed_version == "(none)"
- Chef::Log.debug("#{@new_resource} current version is nil")
- installed_version = nil
- else
- Chef::Log.debug("#{@new_resource} current version is #{installed_version}")
- installed = true
- end
- when /^\s{2}Candidate: (.+)$/
- candidate_version = $1
- if candidate_version == "(none)"
- # This may not be an appropriate assumption, but it shouldn't break anything that already worked -- btm
- is_virtual_package = true
- showpkg = shell_out_with_timeout!("apt-cache showpkg #{pkg}").stdout
- providers = Hash.new
- showpkg.rpartition(/Reverse Provides: ?#{$/}/)[2].each_line do |line|
- provider, version = line.split
- providers[provider] = version
- end
- # Check if the package providing this virtual package is installed
- num_providers = providers.length
- raise Chef::Exceptions::Package, "#{@new_resource.package_name} has no candidate in the apt-cache" if num_providers == 0
- # apt will only install a virtual package if there is a single providing package
- raise Chef::Exceptions::Package, "#{@new_resource.package_name} is a virtual package provided by #{num_providers} packages, you must explicitly select one to install" if num_providers > 1
- # Check if the package providing this virtual package is installed
- Chef::Log.info("#{@new_resource} is a virtual package, actually acting on package[#{providers.keys.first}]")
- ret = check_package_state(providers.keys.first)
- installed = ret[:installed]
- installed_version = ret[:installed_version]
- else
- Chef::Log.debug("#{@new_resource} candidate version is #{$1}")
- end
- end
+ def get_candidate_versions
+ package_name_array.map do |package_name|
+ package_data[package_name][:candidate_version]
end
+ end
- return {
- installed_version: installed_version,
- installed: installed,
- candidate_version: candidate_version,
- is_virtual_package: is_virtual_package,
- }
+ def candidate_version
+ @candidate_version ||= get_candidate_versions
end
- def check_all_packages_state(package)
- installed_version = {}
- candidate_version = {}
- installed = {}
+ def packages_all_locked?(names, versions)
+ names.all? { |n| locked_packages.include? n }
+ end
- [package].flatten.each do |pkg|
- ret = check_package_state(pkg)
- is_virtual_package[pkg] = ret[:is_virtual_package]
- installed[pkg] = ret[:installed]
- installed_version[pkg] = ret[:installed_version]
- candidate_version[pkg] = ret[:candidate_version]
- end
+ def packages_all_unlocked?(names, versions)
+ names.all? { |n| !locked_packages.include? n }
+ end
- if package.is_a?(Array)
- @candidate_version = []
- final_installed_version = []
- [package].flatten.each do |pkg|
- @candidate_version << candidate_version[pkg]
- final_installed_version << installed_version[pkg]
+ def locked_packages
+ @locked_packages ||=
+ begin
+ locked = shell_out_compact_timeout!("apt-mark", "showhold")
+ locked.stdout.each_line.map do |line|
+ line.strip
+ end
end
- @current_resource.version(final_installed_version)
- else
- @candidate_version = candidate_version[package]
- @current_resource.version(installed_version[package])
- end
end
def install_package(name, version)
- name_array = [ name ].flatten
- version_array = [ version ].flatten
- package_name = name_array.zip(version_array).map do |n, v|
- is_virtual_package[n] ? n : "#{n}=#{v}"
- end.join(" ")
- run_noninteractive("apt-get -q -y#{expand_options(default_release_options)}#{expand_options(@new_resource.options)} install #{package_name}")
+ package_name = name.zip(version).map do |n, v|
+ package_data[n][:virtual] ? n : "#{n}=#{v}"
+ end
+ run_noninteractive("apt-get", "-q", "-y", config_file_options, default_release_options, options, "install", package_name)
end
def upgrade_package(name, version)
@@ -150,33 +100,135 @@ class Chef
end
def remove_package(name, version)
- package_name = [ name ].flatten.join(" ")
- run_noninteractive("apt-get -q -y#{expand_options(@new_resource.options)} remove #{package_name}")
+ package_name = name.map do |n|
+ package_data[n][:virtual] ? resolve_virtual_package_name(n) : n
+ end
+ run_noninteractive("apt-get", "-q", "-y", options, "remove", package_name)
end
def purge_package(name, version)
- package_name = [ name ].flatten.join(" ")
- run_noninteractive("apt-get -q -y#{expand_options(@new_resource.options)} purge #{package_name}")
+ package_name = name.map do |n|
+ package_data[n][:virtual] ? resolve_virtual_package_name(n) : n
+ end
+ run_noninteractive("apt-get", "-q", "-y", options, "purge", package_name)
end
def preseed_package(preseed_file)
- Chef::Log.info("#{@new_resource} pre-seeding package installation instructions")
- run_noninteractive("debconf-set-selections #{preseed_file}")
+ logger.info("#{new_resource} pre-seeding package installation instructions")
+ run_noninteractive("debconf-set-selections", preseed_file)
end
def reconfig_package(name, version)
- package_name = [ name ].flatten.join(" ")
- Chef::Log.info("#{@new_resource} reconfiguring")
- run_noninteractive("dpkg-reconfigure #{package_name}")
+ logger.info("#{new_resource} reconfiguring")
+ run_noninteractive("dpkg-reconfigure", name)
+ end
+
+ def lock_package(name, version)
+ run_noninteractive("apt-mark", options, "hold", name)
+ end
+
+ def unlock_package(name, version)
+ run_noninteractive("apt-mark", options, "unhold", name)
end
private
+ # compare 2 versions to each other to see which is newer.
+ # this differs from the standard package method because we
+ # need to be able to parse debian version strings which contain
+ # tildes which Gem cannot properly parse
+ #
+ # @return [Integer] 1 if v1 > v2. 0 if they're equal. -1 if v1 < v2
+ def version_compare(v1, v2)
+ if !shell_out_compact_timeout("dpkg", "--compare-versions", v1.to_s, "gt", v2.to_s).error?
+ 1
+ elsif !shell_out_compact_timeout("dpkg", "--compare-versions", v1.to_s, "eq", v2.to_s).error?
+ 0
+ else
+ -1
+ end
+ end
+
# Runs command via shell_out with magic environment to disable
# interactive prompts. Command is run with default localization rather
# than forcing locale to "C", so command output may not be stable.
- def run_noninteractive(command)
- shell_out_with_timeout!(command, :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil })
+ def run_noninteractive(*args)
+ shell_out_compact_timeout!(*args, env: { "DEBIAN_FRONTEND" => "noninteractive" })
+ end
+
+ def default_release_options
+ # Use apt::Default-Release option only if provider supports it
+ if new_resource.respond_to?(:default_release) && new_resource.default_release
+ [ "-o", "APT::Default-Release=#{new_resource.default_release}" ]
+ end
+ end
+
+ def config_file_options
+ # If the user has specified config file options previously, respect those.
+ return if Array(options).any? { |opt| opt =~ /--force-conf/ }
+
+ # It doesn't make sense to install packages in a scenario that can
+ # result in a prompt. Have users decide up-front whether they want to
+ # forcibly overwrite the config file, otherwise preserve it.
+ if new_resource.overwrite_config_files
+ [ "-o", "Dpkg::Options::=--force-confnew" ]
+ else
+ [ "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confold" ]
+ end
+ end
+
+ def resolve_package_versions(pkg)
+ current_version = nil
+ candidate_version = nil
+ run_noninteractive("apt-cache", default_release_options, "policy", pkg).stdout.each_line do |line|
+ case line
+ when /^\s{2}Installed: (.+)$/
+ current_version = ( $1 != "(none)" ) ? $1 : nil
+ logger.trace("#{new_resource} installed version for #{pkg} is #{$1}")
+ when /^\s{2}Candidate: (.+)$/
+ candidate_version = ( $1 != "(none)" ) ? $1 : nil
+ logger.trace("#{new_resource} candidate version for #{pkg} is #{$1}")
+ end
+ end
+ [ current_version, candidate_version ]
+ end
+
+ def resolve_virtual_package_name(pkg)
+ showpkg = run_noninteractive("apt-cache", "showpkg", pkg).stdout
+ partitions = showpkg.rpartition(/Reverse Provides: ?#{$/}/)
+ return nil if partitions[0] == "" && partitions[1] == "" # not found in output
+ set = partitions[2].lines.each_with_object(Set.new) do |line, acc|
+ # there may be multiple reverse provides for a single package
+ acc.add(line.split[0])
+ end
+ if set.size > 1
+ raise Chef::Exceptions::Package, "#{new_resource.package_name} is a virtual package provided by multiple packages, you must explicitly select one"
+ end
+ set.to_a.first
+ end
+
+ def package_data_for(pkg)
+ virtual = false
+ current_version = nil
+ candidate_version = nil
+
+ current_version, candidate_version = resolve_package_versions(pkg)
+
+ if candidate_version.nil?
+ newpkg = resolve_virtual_package_name(pkg)
+
+ if newpkg
+ virtual = true
+ logger.info("#{new_resource} is a virtual package, actually acting on package[#{newpkg}]")
+ current_version, candidate_version = resolve_package_versions(newpkg)
+ end
+ end
+
+ {
+ current_version: current_version,
+ candidate_version: candidate_version,
+ virtual: virtual,
+ }
end
end
diff --git a/lib/chef/provider/package/bff.rb b/lib/chef/provider/package/bff.rb
new file mode 100644
index 0000000000..44fadd92df
--- /dev/null
+++ b/lib/chef/provider/package/bff.rb
@@ -0,0 +1,142 @@
+#
+# Author:: Deepali Jagtap
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+require "chef/provider/package"
+require "chef/resource/package"
+require "chef/mixin/get_source_from_package"
+
+class Chef
+ class Provider
+ class Package
+ class Bff < Chef::Provider::Package
+
+ provides :package, os: "aix"
+ provides :bff_package
+
+ include Chef::Mixin::GetSourceFromPackage
+
+ def define_resource_requirements
+ super
+ requirements.assert(:install) do |a|
+ a.assertion { new_resource.source }
+ a.failure_message Chef::Exceptions::Package, "Source for package #{new_resource.name} required for action install"
+ end
+ requirements.assert(:all_actions) do |a|
+ a.assertion { !new_resource.source || package_source_found? }
+ a.failure_message Chef::Exceptions::Package, "Package #{new_resource.name} not found: #{new_resource.source}"
+ a.whyrun "would assume #{new_resource.source} would be have previously been made available"
+ end
+ end
+
+ def load_current_resource
+ @current_resource = Chef::Resource::Package.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
+
+ if package_source_found?
+ logger.trace("#{new_resource} checking pkg status")
+ ret = shell_out_compact_timeout("installp", "-L", "-d", new_resource.source)
+ ret.stdout.each_line do |line|
+ case line
+ when /:#{new_resource.package_name}:/
+ fields = line.split(":")
+ new_resource.version(fields[2])
+ when /^#{new_resource.package_name}:/
+ logger.warn("You are installing a bff package by product name. For idempotent installs, please install individual filesets")
+ fields = line.split(":")
+ new_resource.version(fields[2])
+ end
+ end
+ raise Chef::Exceptions::Package, "package source #{new_resource.source} does not provide package #{new_resource.package_name}" unless new_resource.version
+ end
+
+ logger.trace("#{new_resource} checking install state")
+ ret = shell_out_compact_timeout("lslpp", "-lcq", current_resource.package_name)
+ ret.stdout.each_line do |line|
+ case line
+ when /#{current_resource.package_name}/
+ fields = line.split(":")
+ logger.trace("#{new_resource} version #{fields[2]} is already installed")
+ current_resource.version(fields[2])
+ end
+ end
+
+ unless ret.exitstatus == 0 || ret.exitstatus == 1
+ raise Chef::Exceptions::Package, "lslpp failed - #{ret.format_for_exception}!"
+ end
+
+ current_resource
+ end
+
+ def candidate_version
+ return @candidate_version if @candidate_version
+ if package_source_found?
+ ret = shell_out_compact_timeout("installp", "-L", "-d", new_resource.source)
+ ret.stdout.each_line do |line|
+ case line
+ when /\w:#{Regexp.escape(new_resource.package_name)}:(.*)/
+ fields = line.split(":")
+ @candidate_version = fields[2]
+ new_resource.version(fields[2])
+ logger.trace("#{new_resource} setting install candidate version to #{@candidate_version}")
+ end
+ end
+ unless ret.exitstatus == 0
+ raise Chef::Exceptions::Package, "installp -L -d #{new_resource.source} - #{ret.format_for_exception}!"
+ end
+ end
+ @candidate_version
+ end
+
+ #
+ # The install/update action needs to be tested with various kinds of packages
+ # on AIX viz. packages with or without licensing file dependencies, packages
+ # with dependencies on other packages which will help to test additional
+ # options of installp.
+ # So far, the code has been tested only with standalone packages.
+ #
+ def install_package(name, version)
+ logger.trace("#{new_resource} package install options: #{options}")
+ if options.nil?
+ shell_out_compact_timeout!("installp", "-aYF", "-d", new_resource.source, new_resource.package_name)
+ logger.trace("#{new_resource} installed version #{new_resource.version} from: #{new_resource.source}")
+ else
+ shell_out_compact_timeout!("installp", "-aYF", options, "-d", new_resource.source, new_resource.package_name)
+ logger.trace("#{new_resource} installed version #{new_resource.version} from: #{new_resource.source}")
+ end
+ end
+
+ alias upgrade_package install_package
+
+ def remove_package(name, version)
+ if options.nil?
+ shell_out_compact_timeout!("installp", "-u", name)
+ logger.trace("#{new_resource} removed version #{new_resource.version}")
+ else
+ shell_out_compact_timeout!("installp", "-u", options, name)
+ logger.trace("#{new_resource} removed version #{new_resource.version}")
+ end
+ end
+
+ def package_source_found?
+ @package_source_found ||= new_resource.source && ::File.exist?(new_resource.source)
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/cab.rb b/lib/chef/provider/package/cab.rb
new file mode 100644
index 0000000000..79292293d2
--- /dev/null
+++ b/lib/chef/provider/package/cab.rb
@@ -0,0 +1,183 @@
+#
+# Author:: Vasundhara Jagdale (<vasundhara.jagdale@msystechnologies.com>)
+# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/provider/package"
+require "chef/resource/cab_package"
+require "chef/mixin/shell_out"
+require "chef/mixin/uris"
+require "chef/mixin/checksum"
+
+class Chef
+ class Provider
+ class Package
+ class Cab < Chef::Provider::Package
+ include Chef::Mixin::ShellOut
+ include Chef::Mixin::Uris
+ include Chef::Mixin::Checksum
+
+ provides :cab_package, os: "windows"
+
+ def load_current_resource
+ @current_resource = Chef::Resource::CabPackage.new(new_resource.name)
+ current_resource.source(cab_file_source)
+ new_resource.version(package_version)
+ current_resource.version(installed_version)
+ current_resource
+ end
+
+ def cab_file_source
+ @cab_file_source ||= uri_scheme?(new_resource.source) ? download_source_file : new_resource.source
+ end
+
+ def download_source_file
+ source_resource.run_action(:create)
+ logger.trace("#{new_resource} fetched source file to #{source_resource.path}")
+ source_resource.path
+ end
+
+ def source_resource
+ @source_resource ||= declare_resource(:remote_file, new_resource.name) do
+ path default_download_cache_path
+ source new_resource.source
+ backup false
+ end
+ end
+
+ def default_download_cache_path
+ uri = ::URI.parse(new_resource.source)
+ filename = ::File.basename(::URI.unescape(uri.path))
+ file_cache_dir = Chef::FileCache.create_cache_path("package/")
+ Chef::Util::PathHelper.cleanpath("#{file_cache_dir}/#{filename}")
+ end
+
+ def install_package(name, version)
+ dism_command("/Add-Package /PackagePath:\"#{cab_file_source}\"")
+ end
+
+ def remove_package(name, version)
+ dism_command("/Remove-Package /PackagePath:\"#{cab_file_source}\"")
+ end
+
+ def dism_command(command)
+ shellout = Mixlib::ShellOut.new("dism.exe /Online /English #{command} /NoRestart", timeout: new_resource.timeout)
+ with_os_architecture(nil) do
+ shellout.run_command
+ end
+ end
+
+ def installed_version
+ # e.g. Package_for_KB2975719~31bf3856ad364e35~amd64~~6.3.1.8
+ package = new_cab_identity
+ # Search for just the package name to catch a different version being installed
+ logger.trace("#{new_resource} searching for installed package #{package['name']}")
+ existing_package_identities = installed_packages.map do |p|
+ split_package_identity(p["package_identity"])
+ end
+ found_packages = existing_package_identities.select do |existing_package_ident|
+ existing_package_ident["name"] == package["name"]
+ end
+ if found_packages.empty?
+ nil
+ elsif found_packages.length == 1
+ found_packages.first["version"]
+ else
+ # Presuming this won't happen, otherwise we need to handle it
+ raise Chef::Exceptions::Package, "Found multiple packages installed matching name #{package['name']}, found: #{found_packages.length} matches"
+ end
+ end
+
+ def cab_identity_from_cab_file
+ stdout = dism_command("/Get-PackageInfo /PackagePath:\"#{cab_file_source}\"").stdout
+ package_info = parse_dism_get_package_info(stdout)
+ split_package_identity(package_info["package_information"]["package_identity"])
+ end
+
+ def new_cab_identity
+ logger.trace("#{new_resource} getting product version for package at #{cab_file_source}")
+ @new_cab_identity ||= cab_identity_from_cab_file
+ end
+
+ def package_version
+ new_cab_identity["version"].chomp
+ end
+
+ # returns a hash of package state information given the output of dism /get-packages
+ # expected keys: package_identity
+ def parse_dism_get_packages(text)
+ packages = []
+ text.each_line do |line|
+ key, value = line.split(":") if line.start_with?("Package Identity")
+ next if key.nil? || value.nil?
+ package = {}
+ package[key.downcase.strip.tr(" ", "_")] = value.strip.chomp
+ packages << package
+ end
+ packages
+ end
+
+ # returns a hash of package information given the output of dism /get-packageinfo
+ def parse_dism_get_package_info(text)
+ package_data = {}
+ errors = []
+ in_section = false
+ section_headers = [ "Package information", "Custom Properties", "Features" ]
+ text.each_line do |line|
+ if line =~ /Error: (.*)/
+ errors << $1.strip
+ elsif section_headers.any? { |header| line =~ /^(#{header})/ }
+ in_section = $1.downcase.tr(" ", "_")
+ elsif line =~ /(.*) ?: (.*)/
+ v = $2 # has to be first or the gsub below replaces this variable
+ k = $1.downcase.strip.tr(" ", "_")
+ if in_section
+ package_data[in_section] = {} unless package_data[in_section]
+ package_data[in_section][k] = v
+ else
+ package_data[k] = v
+ end
+ end
+ end
+ unless errors.empty?
+ if errors.include?("0x80070003") || errors.include?("0x80070002")
+ raise Chef::Exceptions::Package, "DISM: The system cannot find the path or file specified."
+ elsif errors.include?("740")
+ raise Chef::Exceptions::Package, "DISM: Error 740: Elevated permissions are required to run DISM."
+ else
+ raise Chef::Exceptions::Package, "Unknown errors encountered parsing DISM output: #{errors}"
+ end
+ end
+ package_data
+ end
+
+ def split_package_identity(identity)
+ data = {}
+ data["name"], data["publisher"], data["arch"], data["resource_id"], data["version"] = identity.split("~")
+ data
+ end
+
+ def installed_packages
+ @packages ||= begin
+ output = dism_command("/Get-Packages").stdout
+ packages = parse_dism_get_packages(output)
+ packages
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/chocolatey.rb b/lib/chef/provider/package/chocolatey.rb
index 44fb1de235..a6abdd5b46 100644
--- a/lib/chef/provider/package/chocolatey.rb
+++ b/lib/chef/provider/package/chocolatey.rb
@@ -25,13 +25,13 @@ class Chef
class Chocolatey < Chef::Provider::Package
include Chef::Mixin::PowershellOut
- provides :chocolatey_package, os: "windows"
+ provides :chocolatey_package
# Declare that our arguments should be arrays
use_multipackage_api
- PATHFINDING_POWERSHELL_COMMAND = "[System.Environment]::GetEnvironmentVariable('ChocolateyInstall', 'MACHINE')"
- CHOCO_MISSING_MSG = <<-EOS
+ PATHFINDING_POWERSHELL_COMMAND = "[System.Environment]::GetEnvironmentVariable('ChocolateyInstall', 'MACHINE')".freeze
+ CHOCO_MISSING_MSG = <<-EOS.freeze
Could not locate your Chocolatey install. To install chocolatey, we recommend
the 'chocolatey' cookbook (https://github.com/chocolatey/chocolatey-cookbook).
If Chocolatey is installed, ensure that the 'ChocolateyInstall' environment
@@ -59,8 +59,8 @@ EOS
# so we want to assert candidates exist for the alternate source
requirements.assert(:upgrade, :install) do |a|
a.assertion { candidates_exist_for_all_uninstalled? }
- a.failure_message(Chef::Exceptions::Package, "No candidate version available for #{packages_missing_candidates.join(", ")}")
- a.whyrun("Assuming a repository that offers #{packages_missing_candidates.join(", ")} would have been configured")
+ a.failure_message(Chef::Exceptions::Package, "No candidate version available for #{packages_missing_candidates.join(', ')}")
+ a.whyrun("Assuming a repository that offers #{packages_missing_candidates.join(', ')} would have been configured")
end
end
@@ -84,7 +84,7 @@ EOS
# choco does not support installing multiple packages with version pins
name_has_versions.each do |name, version|
- choco_command("install -y -version", version, cmd_args, name)
+ choco_command("install -y --version", version, cmd_args, name)
end
# but we can do all the ones without version pins at once
@@ -106,7 +106,7 @@ EOS
# choco does not support installing multiple packages with version pins
name_has_versions.each do |name, version|
- choco_command("upgrade -y -version", version, cmd_args, name)
+ choco_command("upgrade -y --version", version, cmd_args, name)
end
# but we can do all the ones without version pins at once
@@ -124,24 +124,26 @@ EOS
choco_command("uninstall -y", cmd_args(include_source: false), *names)
end
- # Support :uninstall as an action in order for users to easily convert
- # from the `chocolatey` provider in the cookbook. It is, however,
- # already deprecated.
- def action_uninstall
- Chef::Log.deprecation "The use of action :uninstall on the chocolatey_package provider is deprecated, please use :remove"
- action_remove
- end
-
# Choco does not have dpkg's distinction between purge and remove
- alias_method :purge_package, :remove_package
+ alias purge_package remove_package
# Override the superclass check. The semantics for our new_resource.source is not files to
# install from, but like the rubygem provider's sources which are more like repos.
- def check_resource_semantics!
- end
+ def check_resource_semantics!; end
private
+ def version_compare(v1, v2)
+ if v1 == "latest" || v2 == "latest"
+ return 0
+ end
+
+ gem_v1 = Gem::Version.new(v1)
+ gem_v2 = Gem::Version.new(v2)
+
+ gem_v1 <=> gem_v2
+ end
+
# Magic to find where chocolatey is installed in the system, and to
# return the full path of choco.exe
#
@@ -160,7 +162,7 @@ EOS
def choco_install_path
@choco_install_path ||= powershell_out!(
PATHFINDING_POWERSHELL_COMMAND
- ).stdout.chomp
+ ).stdout.chomp
end
# Helper to dispatch a choco command through shell_out using the timeout
@@ -169,7 +171,7 @@ EOS
# @param args [String] variable number of string arguments
# @return [Mixlib::ShellOut] object returned from shell_out!
def choco_command(*args)
- shell_out_with_timeout!(args_to_string(choco_exe, *args))
+ shell_out_with_timeout!(args_to_string(choco_exe, *args), returns: new_resource.returns)
end
# Use the available_packages Hash helper to create an array suitable for
@@ -227,17 +229,21 @@ EOS
#
# @return [Hash] name-to-version mapping of available packages
def available_packages
- @available_packages ||=
- begin
- cmd = [ "list -ar #{package_name_array.join ' '}" ]
- cmd.push( "-source #{new_resource.source}" ) if new_resource.source
- parse_list_output(*cmd).each_with_object({}) do |name_version, available|
- name, version = name_version
- if desired_name_versions[name].nil? || desired_name_versions[name] == version
- available[name] = version
+ return @available_packages if @available_packages
+ @available_packages = {}
+ package_name_array.each do |pkg|
+ available_versions =
+ begin
+ cmd = [ "list -r #{pkg}" ]
+ cmd.push( "-source #{new_resource.source}" ) if new_resource.source
+ raw = parse_list_output(*cmd)
+ raw.keys.each_with_object({}) do |name, available|
+ available[name] = desired_name_versions[name] || raw[name]
end
end
- end
+ @available_packages.merge! available_versions
+ end
+ @available_packages
end
# Installed packages in chocolatey as a Hash of names mapped to versions
@@ -246,20 +252,22 @@ EOS
# @return [Hash] name-to-version mapping of installed packages
def installed_packages
@installed_packages ||= Hash[*parse_list_output("list -l -r").flatten]
+ @installed_packages
end
# Helper to convert choco.exe list output to a Hash
# (names are downcased for case-insenstive matching)
#
# @param cmd [String] command to run
- # @return [Array] list output converted to ruby Hash
+ # @return [Hash] list output converted to ruby Hash
def parse_list_output(*args)
- list = []
+ hash = {}
choco_command(*args).stdout.each_line do |line|
+ next if line.start_with?("Chocolatey v")
name, version = line.split("|")
- list << [ name.downcase, version.chomp ]
+ hash[name.downcase] = version&.chomp
end
- list
+ hash
end
# Helper to downcase all names in an array
@@ -267,7 +275,7 @@ EOS
# @param names [Array] original mixed case names
# @return [Array] same names in lower case
def lowercase_names(names)
- names.map { |name| name.downcase }
+ names.map(&:downcase)
end
end
end
diff --git a/lib/chef/provider/package/dnf.rb b/lib/chef/provider/package/dnf.rb
new file mode 100644
index 0000000000..90a5596727
--- /dev/null
+++ b/lib/chef/provider/package/dnf.rb
@@ -0,0 +1,196 @@
+#
+# Copyright:: Copyright 2016-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/provider/package"
+require "chef/resource/dnf_package"
+require "chef/mixin/which"
+require "chef/mixin/shell_out"
+require "chef/mixin/get_source_from_package"
+require "chef/provider/package/dnf/python_helper"
+require "chef/provider/package/dnf/version"
+
+class Chef
+ class Provider
+ class Package
+ class Dnf < Chef::Provider::Package
+ extend Chef::Mixin::Which
+ extend Chef::Mixin::ShellOut
+ include Chef::Mixin::GetSourceFromPackage
+
+ allow_nils
+ use_multipackage_api
+ use_package_name_for_source
+
+ # all rhel variants >= 8 will use DNF
+ provides :package, platform_family: "rhel", platform_version: ">= 8"
+
+ # fedora >= 22 uses DNF
+ provides :package, platform: "fedora", platform_version: ">= 22"
+
+ # amazon will eventually use DNF
+ provides :package, platform: "amazon" do
+ which("dnf")
+ end
+
+ provides :dnf_package
+
+ #
+ # Most of the magic in this class happens in the python helper script. The ruby side of this
+ # provider knows only enough to translate Chef-style new_resource name+package+version into
+ # a request to the python side. The python side is then responsible for knowing everything
+ # about RPMs and what is installed and what is available. The ruby side of this class should
+ # remain a lightweight translation layer to translate Chef requests into RPC requests to
+ # python. This class knows nothing about how to compare RPM versions, and does not maintain
+ # any cached state of installed/available versions and should be kept that way.
+ #
+ def python_helper
+ @python_helper ||= PythonHelper.instance
+ end
+
+ def load_current_resource
+ flushcache if new_resource.flush_cache[:before]
+
+ @current_resource = Chef::Resource::DnfPackage.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
+ current_resource.version(get_current_versions)
+
+ current_resource
+ end
+
+ def define_resource_requirements
+ requirements.assert(:install, :upgrade, :remove, :purge) do |a|
+ a.assertion { !new_resource.source || ::File.exist?(new_resource.source) }
+ a.failure_message Chef::Exceptions::Package, "Package #{new_resource.package_name} not found: #{new_resource.source}"
+ a.whyrun "assuming #{new_resource.source} would have previously been created"
+ end
+
+ super
+ end
+
+ def candidate_version
+ package_name_array.each_with_index.map do |pkg, i|
+ available_version(i).version_with_arch
+ end
+ end
+
+ def get_current_versions
+ package_name_array.each_with_index.map do |pkg, i|
+ installed_version(i).version_with_arch
+ end
+ end
+
+ def install_package(names, versions)
+ if new_resource.source
+ dnf(options, "-y install", new_resource.source)
+ else
+ resolved_names = names.each_with_index.map { |name, i| available_version(i).to_s unless name.nil? }
+ dnf(options, "-y install", resolved_names)
+ end
+ flushcache
+ end
+
+ # dnf upgrade does not work on uninstalled packaged, while install will upgrade
+ alias upgrade_package install_package
+
+ def remove_package(names, versions)
+ resolved_names = names.each_with_index.map { |name, i| installed_version(i).to_s unless name.nil? }
+ dnf(options, "-y remove", resolved_names)
+ flushcache
+ end
+
+ alias purge_package remove_package
+
+ action :flush_cache do
+ flushcache
+ end
+
+ private
+
+ def resolve_source_to_version_obj
+ shell_out_with_timeout!("rpm -qp --queryformat '%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n' #{new_resource.source}").stdout.each_line do |line|
+ # this is another case of committing the sin of doing some lightweight mangling of RPM versions in ruby -- but the output of the rpm command
+ # does not match what the dnf library accepts.
+ case line
+ when /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$/
+ return Version.new($1, "#{$2 == '(none)' ? '0' : $2}:#{$3}-#{$4}", $5)
+ end
+ end
+ end
+
+ def version_compare(v1, v2)
+ python_helper.compare_versions(v1, v2)
+ end
+
+ # @returns Array<Version>
+ def available_version(index)
+ @available_version ||= []
+
+ @available_version[index] ||= if new_resource.source
+ resolve_source_to_version_obj
+ else
+ python_helper.query(:whatavailable, package_name_array[index], safe_version_array[index], safe_arch_array[index])
+ end
+
+ @available_version[index]
+ end
+
+ # @return [Array<Version>]
+ def installed_version(index)
+ @installed_version ||= []
+ @installed_version[index] ||= if new_resource.source
+ python_helper.query(:whatinstalled, available_version(index).name, safe_version_array[index], safe_arch_array[index])
+ else
+ python_helper.query(:whatinstalled, package_name_array[index], safe_version_array[index], safe_arch_array[index])
+ end
+ @installed_version[index]
+ end
+
+ # cache flushing is accomplished by simply restarting the python helper. this produces a roughly
+ # 15% hit to the runtime of installing/removing/upgrading packages. correctly using multipackage
+ # array installs (and the multipackage cookbook) can produce 600% improvements in runtime.
+ def flushcache
+ python_helper.restart
+ end
+
+ def dnf(*args)
+ shell_out_with_timeout!(a_to_s("dnf", *args))
+ end
+
+ def safe_version_array
+ if new_resource.version.is_a?(Array)
+ new_resource.version
+ elsif new_resource.version.nil?
+ package_name_array.map { nil }
+ else
+ [ new_resource.version ]
+ end
+ end
+
+ def safe_arch_array
+ if new_resource.arch.is_a?(Array)
+ new_resource.arch
+ elsif new_resource.arch.nil?
+ package_name_array.map { nil }
+ else
+ [ new_resource.arch ]
+ end
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/dnf/dnf_helper.py b/lib/chef/provider/package/dnf/dnf_helper.py
new file mode 100644
index 0000000000..501d6fceee
--- /dev/null
+++ b/lib/chef/provider/package/dnf/dnf_helper.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
+
+import sys
+import dnf
+import hawkey
+import signal
+import os
+import json
+
+base = None
+
+def get_sack():
+ global base
+ if base is None:
+ base = dnf.Base()
+ base.read_all_repos()
+ base.fill_sack()
+ return base.sack
+
+# FIXME: leaks memory and does not work
+def flushcache():
+ try:
+ os.remove('/var/cache/dnf/@System.solv')
+ except OSError:
+ pass
+ get_sack().load_system_repo(build_cache=True)
+
+def versioncompare(versions):
+ sack = get_sack()
+ if (versions[0] is None) or (versions[1] is None):
+ sys.stdout.write('0\n')
+ else:
+ evr_comparison = sack.evr_cmp(versions[0], versions[1])
+ sys.stdout.write('{}\n'.format(evr_comparison))
+
+def query(command):
+ sack = get_sack()
+
+ subj = dnf.subject.Subject(command['provides'])
+ q = subj.get_best_query(sack, with_provides=True)
+
+ if command['action'] == "whatinstalled":
+ q = q.installed()
+
+ if command['action'] == "whatavailable":
+ q = q.available()
+
+ if 'epoch' in command:
+ q = q.filterm(epoch=int(command['epoch']))
+ if 'version' in command:
+ q = q.filterm(version__glob=command['version'])
+ if 'release' in command:
+ q = q.filterm(release__glob=command['release'])
+
+ if 'arch' in command:
+ q = q.filterm(arch__glob=command['arch'])
+
+ # only apply the default arch query filter if it returns something
+ archq = q.filter(arch=[ 'noarch', hawkey.detect_arch() ])
+ if len(archq.run()) > 0:
+ q = archq
+
+ pkgs = q.latest(1).run()
+
+ if not pkgs:
+ sys.stdout.write('{} nil nil\n'.format(command['provides'].split().pop(0)))
+ else:
+ # make sure we picked the package with the highest version
+ pkgs.sort
+ pkg = pkgs.pop()
+ sys.stdout.write('{} {}:{}-{} {}\n'.format(pkg.name, pkg.epoch, pkg.version, pkg.release, pkg.arch))
+
+# the design of this helper is that it should try to be 'brittle' and fail hard and exit in order
+# to keep process tables clean. additional error handling should probably be added to the retry loop
+# on the ruby side.
+def exit_handler(signal, frame):
+ sys.exit(0)
+
+signal.signal(signal.SIGINT, exit_handler)
+signal.signal(signal.SIGHUP, exit_handler)
+signal.signal(signal.SIGPIPE, exit_handler)
+
+while 1:
+ # kill self if we get orphaned (tragic)
+ ppid = os.getppid()
+ if ppid == 1:
+ sys.exit(0)
+ line = sys.stdin.readline()
+ command = json.loads(line)
+ if command['action'] == "whatinstalled":
+ query(command)
+ elif command['action'] == "whatavailable":
+ query(command)
+ elif command['action'] == "flushcache":
+ flushcache()
+ elif command['action'] == "versioncompare":
+ versioncompare(command['versions'])
+ else:
+ raise RuntimeError("bad command")
diff --git a/lib/chef/provider/package/dnf/python_helper.rb b/lib/chef/provider/package/dnf/python_helper.rb
new file mode 100644
index 0000000000..5524740fc4
--- /dev/null
+++ b/lib/chef/provider/package/dnf/python_helper.rb
@@ -0,0 +1,172 @@
+#
+# Copyright:: Copyright 2016-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/mixin/which"
+require "chef/mixin/shell_out"
+require "chef/provider/package/dnf/version"
+require "timeout"
+
+class Chef
+ class Provider
+ class Package
+ class Dnf < Chef::Provider::Package
+ class PythonHelper
+ include Singleton
+ include Chef::Mixin::Which
+ include Chef::Mixin::ShellOut
+
+ attr_accessor :stdin
+ attr_accessor :stdout
+ attr_accessor :stderr
+ attr_accessor :wait_thr
+
+ DNF_HELPER = ::File.expand_path(::File.join(::File.dirname(__FILE__), "dnf_helper.py")).freeze
+
+ def dnf_command
+ @dnf_command ||= which("python", "python3", "python2", "python2.7") do |f|
+ shell_out("#{f} -c 'import dnf'").exitstatus == 0
+ end + " #{DNF_HELPER}"
+ end
+
+ def start
+ ENV["PYTHONUNBUFFERED"] = "1"
+ @stdin, @stdout, @stderr, @wait_thr = Open3.popen3(dnf_command)
+ end
+
+ def reap
+ unless wait_thr.nil?
+ Process.kill("KILL", wait_thr.pid) rescue nil
+ stdin.close unless stdin.nil?
+ stdout.close unless stdout.nil?
+ stderr.close unless stderr.nil?
+ wait_thr.value # this calls waitpit()
+ end
+ end
+
+ def check
+ start if stdin.nil?
+ end
+
+ def compare_versions(version1, version2)
+ with_helper do
+ json = build_version_query("versioncompare", [version1, version2])
+ Chef::Log.trace "sending '#{json}' to python helper"
+ stdin.syswrite json + "\n"
+ stdout.sysread(4096).chomp.to_i
+ end
+ end
+
+ # @returns Array<Version>
+ def query(action, provides, version = nil, arch = nil)
+ with_helper do
+ json = build_query(action, provides, version, arch)
+ Chef::Log.trace "sending '#{json}' to python helper"
+ stdin.syswrite json + "\n"
+ output = stdout.sysread(4096).chomp
+ Chef::Log.trace "got '#{output}' from python helper"
+ version = parse_response(output)
+ Chef::Log.trace "parsed #{version} from python helper"
+ version
+ end
+ end
+
+ def restart
+ reap
+ start
+ end
+
+ private
+
+ # i couldn't figure out how to decompose an evr on the python side, it seems reasonably
+ # painless to do it in ruby (generally massaging nevras in the ruby side is HIGHLY
+ # discouraged -- this is an "every rule has an exception" exception -- any additional
+ # functionality should probably trigger moving this regexp logic into python)
+ def add_version(hash, version)
+ epoch = nil
+ if version =~ /(\S+):(\S+)/
+ epoch = $1
+ version = $2
+ end
+ if version =~ /(\S+)-(\S+)/
+ version = $1
+ release = $2
+ end
+ hash["epoch"] = epoch unless epoch.nil?
+ hash["release"] = release unless release.nil?
+ hash["version"] = version
+ end
+
+ def build_query(action, provides, version, arch)
+ hash = { "action" => action }
+ hash["provides"] = provides
+ add_version(hash, version) unless version.nil?
+ hash["arch" ] = arch unless arch.nil?
+ FFI_Yajl::Encoder.encode(hash)
+ end
+
+ def build_version_query(action, versions)
+ hash = { "action" => action }
+ hash["versions"] = versions
+ FFI_Yajl::Encoder.encode(hash)
+ end
+
+ def parse_response(output)
+ array = output.split.map { |x| x == "nil" ? nil : x }
+ array.each_slice(3).map { |x| Version.new(*x) }.first
+ end
+
+ def drain_stderr
+ output = ""
+ until IO.select([stderr], nil, nil, 0).nil?
+ output += stderr.sysread(4096).chomp
+ end
+ output
+ rescue
+ # we must rescue EOFError, and we don't much care about errors on stderr anyway
+ output
+ end
+
+ def with_helper
+ max_retries ||= 5
+ ret = nil
+ Timeout.timeout(600) do
+ check
+ ret = yield
+ end
+ output = drain_stderr
+ unless output.empty?
+ Chef::Log.trace "discarding output on stderr from python helper: #{output}"
+ end
+ ret
+ rescue EOFError, Errno::EPIPE, Timeout::Error, Errno::ESRCH => e
+ output = drain_stderr
+ if ( max_retries -= 1 ) > 0
+ unless output.empty?
+ Chef::Log.trace "discarding output on stderr from python helper: #{output}"
+ end
+ restart
+ retry
+ else
+ raise e if output.empty?
+ raise "dnf-helper.py had stderr output:\n\n#{output}"
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/dnf/version.rb b/lib/chef/provider/package/dnf/version.rb
new file mode 100644
index 0000000000..3cff5b0437
--- /dev/null
+++ b/lib/chef/provider/package/dnf/version.rb
@@ -0,0 +1,56 @@
+#
+# Copyright:: Copyright 2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ class Provider
+ class Package
+ class Dnf < Chef::Provider::Package
+
+ # helper class to assist in passing around name/version/arch triples
+ class Version
+ attr_accessor :name
+ attr_accessor :version
+ attr_accessor :arch
+
+ def initialize(name, version, arch)
+ @name = name
+ @version = version
+ @arch = arch
+ end
+
+ def to_s
+ "#{name}-#{version}.#{arch}"
+ end
+
+ def version_with_arch
+ "#{version}.#{arch}" unless version.nil?
+ end
+
+ def matches_name_and_arch?(other)
+ other.version == version && other.arch == arch
+ end
+
+ def ==(other)
+ name == other.name && version == other.version && arch == other.arch
+ end
+
+ alias eql? ==
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/dpkg.rb b/lib/chef/provider/package/dpkg.rb
index a5a80e14d6..aa53f6145f 100644
--- a/lib/chef/provider/package/dpkg.rb
+++ b/lib/chef/provider/package/dpkg.rb
@@ -23,11 +23,11 @@ class Chef
class Provider
class Package
class Dpkg < Chef::Provider::Package
- DPKG_REMOVED = /^Status: deinstall ok config-files/
+ DPKG_REMOVED = /^Status: deinstall ok config-files/
DPKG_INSTALLED = /^Status: install ok installed/
- DPKG_VERSION = /^Version: (.+)$/
+ DPKG_VERSION = /^Version: (.+)$/
- provides :dpkg_package, os: "linux"
+ provides :dpkg_package
use_multipackage_api
use_package_name_for_source
@@ -73,18 +73,18 @@ class Chef
def install_package(name, version)
sources = name.map { |n| name_sources[n] }
- Chef::Log.info("#{new_resource} installing package(s): #{name.join(' ')}")
- run_noninteractive("dpkg -i", new_resource.options, *sources)
+ logger.info("#{new_resource} installing package(s): #{name.join(' ')}")
+ run_noninteractive("dpkg", "-i", *options, *sources)
end
def remove_package(name, version)
- Chef::Log.info("#{new_resource} removing package(s): #{name.join(' ')}")
- run_noninteractive("dpkg -r", new_resource.options, *name)
+ logger.info("#{new_resource} removing package(s): #{name.join(' ')}")
+ run_noninteractive("dpkg", "-r", *options, *name)
end
def purge_package(name, version)
- Chef::Log.info("#{new_resource} purging packages(s): #{name.join(' ')}")
- run_noninteractive("dpkg -P", new_resource.options, *name)
+ logger.info("#{new_resource} purging packages(s): #{name.join(' ')}")
+ run_noninteractive("dpkg", "-P", *options, *name)
end
def upgrade_package(name, version)
@@ -92,24 +92,39 @@ class Chef
end
def preseed_package(preseed_file)
- Chef::Log.info("#{new_resource} pre-seeding package installation instructions")
+ logger.info("#{new_resource} pre-seeding package installation instructions")
run_noninteractive("debconf-set-selections", *preseed_file)
end
def reconfig_package(name, version)
- Chef::Log.info("#{new_resource} reconfiguring")
+ logger.info("#{new_resource} reconfiguring")
run_noninteractive("dpkg-reconfigure", *name)
end
# Override the superclass check. Multiple sources are required here.
- def check_resource_semantics!
- end
+ def check_resource_semantics!; end
private
+ # compare 2 versions to each other to see which is newer.
+ # this differs from the standard package method because we
+ # need to be able to parse debian version strings which contain
+ # tildes which Gem cannot properly parse
+ #
+ # @return [Integer] 1 if v1 > v2. 0 if they're equal. -1 if v1 < v2
+ def version_compare(v1, v2)
+ if !shell_out_compact_timeout("dpkg", "--compare-versions", v1.to_s, "gt", v2.to_s).error?
+ 1
+ elsif !shell_out_compact_timeout("dpkg", "--compare-versions", v1.to_s, "eq", v2.to_s).error?
+ 0
+ else
+ -1
+ end
+ end
+
def read_current_version_of_package(package_name)
- Chef::Log.debug("#{new_resource} checking install state of #{package_name}")
- status = shell_out_with_timeout!("dpkg -s #{package_name}", returns: [0, 1])
+ logger.trace("#{new_resource} checking install state of #{package_name}")
+ status = shell_out_compact_timeout!("dpkg", "-s", package_name, returns: [0, 1])
package_installed = false
status.stdout.each_line do |line|
case line
@@ -120,12 +135,12 @@ class Chef
package_installed = true
when DPKG_VERSION
if package_installed
- Chef::Log.debug("#{new_resource} current version is #{$1}")
+ logger.trace("#{new_resource} current version is #{$1}")
return $1
end
end
end
- return nil
+ nil
end
def get_current_version_from(array)
@@ -137,7 +152,7 @@ class Chef
# Runs command via shell_out_with_timeout with magic environment to disable
# interactive prompts.
def run_noninteractive(*command)
- shell_out_with_timeout!(a_to_s(*command), :env => { "DEBIAN_FRONTEND" => "noninteractive" })
+ shell_out_compact_timeout!(*command, env: { "DEBIAN_FRONTEND" => "noninteractive" })
end
# Returns true if all sources exist. Returns false if any do not, or if no
@@ -176,8 +191,8 @@ class Chef
@name_pkginfo ||=
begin
pkginfos = resolved_source_array.map do |src|
- Chef::Log.debug("#{new_resource} checking #{src} dpkg status")
- status = shell_out_with_timeout!("dpkg-deb -W #{src}")
+ logger.trace("#{new_resource} checking #{src} dpkg status")
+ status = shell_out_compact_timeout!("dpkg-deb", "-W", src)
status.stdout
end
Hash[*package_name_array.zip(pkginfos).flatten]
diff --git a/lib/chef/provider/package/easy_install.rb b/lib/chef/provider/package/easy_install.rb
deleted file mode 100644
index 989f2ab9d2..0000000000
--- a/lib/chef/provider/package/easy_install.rb
+++ /dev/null
@@ -1,135 +0,0 @@
-#
-# Author:: Joe Williams (<joe@joetify.com>)
-# Copyright:: Copyright 2009-2016, Joe Williams
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/provider/package"
-require "chef/mixin/command"
-require "chef/resource/package"
-
-class Chef
- class Provider
- class Package
- class EasyInstall < Chef::Provider::Package
-
- provides :easy_install_package
-
- def install_check(name)
- check = false
-
- begin
- # first check to see if we can import it
- output = shell_out_with_timeout!("#{python_binary_path} -c \"import #{name}\"", :returns => [0, 1]).stderr
- if output.include? "ImportError"
- # then check to see if its on the path
- output = shell_out_with_timeout!("#{python_binary_path} -c \"import sys; print sys.path\"", :returns => [0, 1]).stdout
- if output.downcase.include? "#{name.downcase}"
- check = true
- end
- else
- check = true
- end
- rescue
- # it's probably not installed
- end
-
- check
- end
-
- def easy_install_binary_path
- path = @new_resource.easy_install_binary
- path ? path : "easy_install"
- end
-
- def python_binary_path
- path = @new_resource.python_binary
- path ? path : "python"
- end
-
- def module_name
- m = @new_resource.module_name
- m ? m : @new_resource.name
- end
-
- def load_current_resource
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
-
- # get the currently installed version if installed
- package_version = nil
- if install_check(module_name)
- begin
- output = shell_out_with_timeout!("#{python_binary_path} -c \"import #{module_name}; print #{module_name}.__version__\"").stdout
- package_version = output.strip
- rescue
- output = shell_out_with_timeout!("#{python_binary_path} -c \"import sys; print sys.path\"", :returns => [0, 1]).stdout
-
- output_array = output.gsub(/[\[\]]/, "").split(/\s*,\s*/)
- package_path = ""
-
- output_array.each do |entry|
- if entry.downcase.include?(@new_resource.package_name)
- package_path = entry
- end
- end
-
- package_path[/\S\S(.*)\/(.*)-(.*)-py(.*).egg\S/]
- package_version = $3
- end
- end
-
- if package_version == @new_resource.version
- Chef::Log.debug("#{@new_resource} at version #{@new_resource.version}")
- @current_resource.version(@new_resource.version)
- else
- Chef::Log.debug("#{@new_resource} at version #{package_version}")
- @current_resource.version(package_version)
- end
-
- @current_resource
- end
-
- def candidate_version
- return @candidate_version if @candidate_version
-
- # do a dry run to get the latest version
- result = shell_out_with_timeout!("#{easy_install_binary_path} -n #{@new_resource.package_name}", :returns => [0, 1])
- @candidate_version = result.stdout[/(.*)Best match: (.*) (.*)$/, 3]
- @candidate_version
- end
-
- def install_package(name, version)
- Chef.log_deprecation("The easy_install package provider is deprecated and will be removed in Chef 13.")
- run_command(:command => "#{easy_install_binary_path}#{expand_options(@new_resource.options)} \"#{name}==#{version}\"")
- end
-
- def upgrade_package(name, version)
- install_package(name, version)
- end
-
- def remove_package(name, version)
- Chef.log_deprecation("The easy_install package provider is deprecated and will be removed in Chef 13.")
- run_command(:command => "#{easy_install_binary_path }#{expand_options(@new_resource.options)} -m #{name}")
- end
-
- def purge_package(name, version)
- remove_package(name, version)
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/provider/package/freebsd/base.rb b/lib/chef/provider/package/freebsd/base.rb
index 7104a71f70..fc62fa7cc0 100644
--- a/lib/chef/provider/package/freebsd/base.rb
+++ b/lib/chef/provider/package/freebsd/base.rb
@@ -47,7 +47,7 @@ class Chef
# Otherwise look up the path to the ports directory using 'whereis'
else
- whereis = shell_out_with_timeout!("whereis -s #{port}", :env => nil)
+ whereis = shell_out_compact_timeout!("whereis", "-s", port, env: nil)
unless path = whereis.stdout[/^#{Regexp.escape(port)}:\s+(.+)$/, 1]
raise Chef::Exceptions::Package, "Could not find port with the name #{port}"
end
@@ -56,9 +56,9 @@ class Chef
end
def makefile_variable_value(variable, dir = nil)
- options = dir ? { :cwd => dir } : {}
- make_v = shell_out_with_timeout!("make -V #{variable}", options.merge!(:env => nil, :returns => [0, 1]))
- make_v.exitstatus.zero? ? make_v.stdout.strip.split($\).first : nil # $\ is the line separator, i.e. newline.
+ options = dir ? { cwd: dir } : {}
+ make_v = shell_out_compact_timeout!("make", "-V", variable, options.merge!(env: nil, returns: [0, 1]))
+ make_v.exitstatus == 0 ? make_v.stdout.strip.split($OUTPUT_RECORD_SEPARATOR).first : nil # $\ is the line separator, i.e. newline.
end
end
@@ -67,19 +67,19 @@ class Chef
def initialize(*args)
super
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
+ @current_resource = Chef::Resource::Package.new(new_resource.name)
end
def load_current_resource
- @current_resource.package_name(@new_resource.package_name)
+ current_resource.package_name(new_resource.package_name)
- @current_resource.version(current_installed_version)
- Chef::Log.debug("#{@new_resource} current version is #{@current_resource.version}") if @current_resource.version
+ current_resource.version(current_installed_version)
+ logger.trace("#{new_resource} current version is #{current_resource.version}") if current_resource.version
@candidate_version = candidate_version
- Chef::Log.debug("#{@new_resource} candidate version is #{@candidate_version}") if @candidate_version
+ logger.trace("#{new_resource} candidate version is #{@candidate_version}") if @candidate_version
- @current_resource
+ current_resource
end
end
diff --git a/lib/chef/provider/package/freebsd/pkg.rb b/lib/chef/provider/package/freebsd/pkg.rb
index 78d9449454..04e6e5c427 100644
--- a/lib/chef/provider/package/freebsd/pkg.rb
+++ b/lib/chef/provider/package/freebsd/pkg.rb
@@ -30,28 +30,28 @@ class Chef
include PortsHelper
def install_package(name, version)
- unless @current_resource.version
- case @new_resource.source
+ unless current_resource.version
+ case new_resource.source
when /^http/, /^ftp/
- if @new_resource.source =~ /\/$/
- shell_out_with_timeout!("pkg_add -r #{package_name}", :env => { "PACKAGESITE" => @new_resource.source, "LC_ALL" => nil }).status
+ if new_resource.source =~ /\/$/
+ shell_out_compact_timeout!("pkg_add", "-r", package_name, env: { "PACKAGESITE" => new_resource.source, "LC_ALL" => nil }).status
else
- shell_out_with_timeout!("pkg_add -r #{package_name}", :env => { "PACKAGEROOT" => @new_resource.source, "LC_ALL" => nil }).status
+ shell_out_compact_timeout!("pkg_add", "-r", package_name, env: { "PACKAGEROOT" => new_resource.source, "LC_ALL" => nil }).status
end
- Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}")
+ logger.trace("#{new_resource} installed from: #{new_resource.source}")
when /^\//
- shell_out_with_timeout!("pkg_add #{file_candidate_version_path}", :env => { "PKG_PATH" => @new_resource.source , "LC_ALL" => nil }).status
- Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}")
+ shell_out_compact_timeout!("pkg_add", file_candidate_version_path, env: { "PKG_PATH" => new_resource.source, "LC_ALL" => nil }).status
+ logger.trace("#{new_resource} installed from: #{new_resource.source}")
else
- shell_out_with_timeout!("pkg_add -r #{latest_link_name}", :env => nil).status
+ shell_out_compact_timeout!("pkg_add", "-r", latest_link_name, env: nil).status
end
end
end
def remove_package(name, version)
- shell_out_with_timeout!("pkg_delete #{package_name}-#{version || @current_resource.version}", :env => nil).status
+ shell_out_compact_timeout!("pkg_delete", "#{package_name}-#{version || current_resource.version}", env: nil).status
end
# The name of the package (without the version number) as understood by pkg_add and pkg_info.
@@ -63,7 +63,7 @@ class Chef
raise Chef::Exceptions::Package, "Unexpected form for PKGNAME variable in #{port_path}/Makefile"
end
else
- @new_resource.package_name
+ new_resource.package_name
end
end
@@ -72,12 +72,12 @@ class Chef
end
def current_installed_version
- pkg_info = shell_out_with_timeout!("pkg_info -E \"#{package_name}*\"", :env => nil, :returns => [0, 1])
+ pkg_info = shell_out_compact_timeout!("pkg_info", "-E", "#{package_name}*", env: nil, returns: [0, 1])
pkg_info.stdout[/^#{Regexp.escape(package_name)}-(.+)/, 1]
end
def candidate_version
- case @new_resource.source
+ case new_resource.source
when /^http/, /^ftp/
repo_candidate_version
when /^\//
@@ -88,7 +88,7 @@ class Chef
end
def file_candidate_version_path
- Dir[Chef::Util::PathHelper.escape_glob_dir("#{@new_resource.source}/#{@current_resource.package_name}") + "*"][-1].to_s
+ Dir[Chef::Util::PathHelper.escape_glob_dir("#{new_resource.source}/#{current_resource.package_name}") + "*"][-1].to_s
end
def file_candidate_version
@@ -104,7 +104,7 @@ class Chef
end
def port_path
- port_dir @new_resource.package_name
+ port_dir new_resource.package_name
end
end
diff --git a/lib/chef/provider/package/freebsd/pkgng.rb b/lib/chef/provider/package/freebsd/pkgng.rb
index de7bea6387..c9c0947f9b 100644
--- a/lib/chef/provider/package/freebsd/pkgng.rb
+++ b/lib/chef/provider/package/freebsd/pkgng.rb
@@ -25,46 +25,44 @@ class Chef
class Pkgng < Base
def install_package(name, version)
- unless @current_resource.version
- case @new_resource.source
+ unless current_resource.version
+ case new_resource.source
when /^(http|ftp|\/)/
- shell_out_with_timeout!("pkg add#{expand_options(@new_resource.options)} #{@new_resource.source}", :env => { "LC_ALL" => nil }).status
- Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}")
-
+ shell_out_compact_timeout!("pkg", "add", options, new_resource.source, env: { "LC_ALL" => nil }).status
+ logger.trace("#{new_resource} installed from: #{new_resource.source}")
else
- shell_out_with_timeout!("pkg install -y#{expand_options(@new_resource.options)} #{name}", :env => { "LC_ALL" => nil }).status
+ shell_out_compact_timeout!("pkg", "install", "-y", options, name, env: { "LC_ALL" => nil }).status
end
end
end
def remove_package(name, version)
- options = @new_resource.options && @new_resource.options.sub(repo_regex, "")
- options && !options.empty? || options = nil
- shell_out_with_timeout!("pkg delete -y#{expand_options(options)} #{name}#{version ? '-' + version : ''}", :env => nil).status
+ options_dup = options && options.map { |str| str.sub(repo_regex, "") }.reject!(&:empty?)
+ shell_out_compact_timeout!("pkg", "delete", "-y", options_dup, "#{name}#{version ? '-' + version : ''}", env: nil).status
end
def current_installed_version
- pkg_info = shell_out_with_timeout!("pkg info \"#{@new_resource.package_name}\"", :env => nil, :returns => [0, 70])
+ pkg_info = shell_out_compact_timeout!("pkg", "info", new_resource.package_name, env: nil, returns: [0, 70])
pkg_info.stdout[/^Version +: (.+)$/, 1]
end
def candidate_version
- @new_resource.source ? file_candidate_version : repo_candidate_version
+ new_resource.source ? file_candidate_version : repo_candidate_version
end
private
def file_candidate_version
- @new_resource.source[/#{Regexp.escape(@new_resource.package_name)}-(.+)\.txz/, 1]
+ new_resource.source[/#{Regexp.escape(new_resource.package_name)}-(.+)\.txz/, 1]
end
def repo_candidate_version
- if @new_resource.options && @new_resource.options.match(repo_regex)
- options = $1
+ if options && options.join(" ").match(repo_regex)
+ options = $1.split(" ")
end
- pkg_query = shell_out_with_timeout!("pkg rquery#{expand_options(options)} '%v' #{@new_resource.package_name}", :env => nil)
- pkg_query.exitstatus.zero? ? pkg_query.stdout.strip.split(/\n/).last : nil
+ pkg_query = shell_out_compact_timeout!("pkg", "rquery", options, "%v", new_resource.package_name, env: nil)
+ pkg_query.exitstatus == 0 ? pkg_query.stdout.strip.split(/\n/).last : nil
end
def repo_regex
diff --git a/lib/chef/provider/package/freebsd/port.rb b/lib/chef/provider/package/freebsd/port.rb
index 3eb3c5ab01..e87be4d304 100644
--- a/lib/chef/provider/package/freebsd/port.rb
+++ b/lib/chef/provider/package/freebsd/port.rb
@@ -26,20 +26,20 @@ class Chef
include PortsHelper
def install_package(name, version)
- shell_out_with_timeout!("make -DBATCH install clean", :timeout => 1800, :env => nil, :cwd => port_dir).status
+ shell_out_compact_timeout!("make", "-DBATCH", "install", "clean", timeout: 1800, env: nil, cwd: port_dir).status
end
def remove_package(name, version)
- shell_out_with_timeout!("make deinstall", :timeout => 300, :env => nil, :cwd => port_dir).status
+ shell_out_compact_timeout!("make", "deinstall", timeout: 300, env: nil, cwd: port_dir).status
end
def current_installed_version
- pkg_info = if @new_resource.supports_pkgng?
- shell_out_with_timeout!("pkg info \"#{@new_resource.package_name}\"", :env => nil, :returns => [0, 70])
+ pkg_info = if new_resource.supports_pkgng?
+ shell_out_compact_timeout!("pkg", "info", new_resource.package_name, env: nil, returns: [0, 70])
else
- shell_out_with_timeout!("pkg_info -E \"#{@new_resource.package_name}*\"", :env => nil, :returns => [0, 1])
+ shell_out_compact_timeout!("pkg_info", "-E", "#{new_resource.package_name}*", env: nil, returns: [0, 1])
end
- pkg_info.stdout[/^#{Regexp.escape(@new_resource.package_name)}-(.+)/, 1]
+ pkg_info.stdout[/^#{Regexp.escape(new_resource.package_name)}-(.+)/, 1]
end
def candidate_version
@@ -51,7 +51,7 @@ class Chef
end
def port_dir
- super(@new_resource.package_name)
+ super(new_resource.package_name)
end
end
end
diff --git a/lib/chef/provider/package/homebrew.rb b/lib/chef/provider/package/homebrew.rb
index a105f6d7d0..643faf23c6 100644
--- a/lib/chef/provider/package/homebrew.rb
+++ b/lib/chef/provider/package/homebrew.rb
@@ -32,21 +32,21 @@ class Chef
include Chef::Mixin::HomebrewUser
def load_current_resource
- self.current_resource = Chef::Resource::Package.new(new_resource.name)
+ self.current_resource = Chef::Resource::HomebrewPackage.new(new_resource.name)
current_resource.package_name(new_resource.package_name)
current_resource.version(current_installed_version)
- Chef::Log.debug("#{new_resource} current version is #{current_resource.version}") if current_resource.version
+ logger.trace("#{new_resource} current version is #{current_resource.version}") if current_resource.version
@candidate_version = candidate_version
- Chef::Log.debug("#{new_resource} candidate version is #{@candidate_version}") if @candidate_version
+ logger.trace("#{new_resource} candidate version is #{@candidate_version}") if @candidate_version
current_resource
end
def install_package(name, version)
unless current_resource.version == version
- brew("install", new_resource.options, name)
+ brew("install", options, name)
end
end
@@ -56,24 +56,25 @@ class Chef
if current_version.nil? || current_version.empty?
install_package(name, version)
elsif current_version != version
- brew("upgrade", new_resource.options, name)
+ brew("upgrade", options, name)
end
end
def remove_package(name, version)
if current_resource.version
- brew("uninstall", new_resource.options, name)
+ brew("uninstall", options, name)
end
end
# Homebrew doesn't really have a notion of purging, do a "force remove"
def purge_package(name, version)
- new_resource.options((new_resource.options || "") << " --force").strip
- remove_package(name, version)
+ if current_resource.version
+ brew("uninstall", "--force", options, name)
+ end
end
def brew(*args)
- get_response_from_command("brew #{args.join(' ')}")
+ get_response_from_command("brew", *args)
end
# We implement a querying method that returns the JSON-as-Hash
@@ -121,13 +122,13 @@ class Chef
private
- def get_response_from_command(command)
+ def get_response_from_command(*command)
homebrew_uid = find_homebrew_uid(new_resource.respond_to?(:homebrew_user) && new_resource.homebrew_user)
homebrew_user = Etc.getpwuid(homebrew_uid)
- Chef::Log.debug "Executing '#{command}' as user '#{homebrew_user.name}'"
+ logger.trace "Executing '#{command.join(' ')}' as user '#{homebrew_user.name}'"
# FIXME: this 1800 second default timeout should be deprecated
- output = shell_out_with_timeout!(command, :timeout => 1800, :user => homebrew_uid, :environment => { "HOME" => homebrew_user.dir, "RUBYOPT" => nil, "TMPDIR" => nil })
+ output = shell_out_compact_timeout!(*command, timeout: 1800, user: homebrew_uid, environment: { "HOME" => homebrew_user.dir, "RUBYOPT" => nil, "TMPDIR" => nil })
output.stdout.chomp
end
diff --git a/lib/chef/provider/package/ips.rb b/lib/chef/provider/package/ips.rb
index 85053d47f2..cabc7fc68b 100644
--- a/lib/chef/provider/package/ips.rb
+++ b/lib/chef/provider/package/ips.rb
@@ -1,7 +1,7 @@
#
# Author:: Jason J. W. Williams (<williamsjj@digitar.com>)
# Author:: Stephen Nelson-Smith (<sns@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright 2011-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,7 +19,6 @@
require "open3"
require "chef/provider/package"
-require "chef/mixin/command"
require "chef/resource/package"
class Chef
@@ -28,7 +27,7 @@ class Chef
class Ips < Chef::Provider::Package
provides :package, platform: %w{openindiana opensolaris omnios solaris2}
- provides :ips_package, os: "solaris2"
+ provides :ips_package
attr_accessor :virtual
@@ -36,45 +35,40 @@ class Chef
super
requirements.assert(:all_actions) do |a|
- a.assertion { ! @candidate_version.nil? }
- a.failure_message Chef::Exceptions::Package, "Package #{@new_resource.package_name} not found"
- a.whyrun "Assuming package #{@new_resource.package_name} would have been made available."
+ a.assertion { !@candidate_version.nil? }
+ a.failure_message Chef::Exceptions::Package, "Package #{new_resource.package_name} not found"
+ a.whyrun "Assuming package #{new_resource.package_name} would have been made available."
end
end
def get_current_version
- shell_out_with_timeout("pkg info #{@new_resource.package_name}").stdout.each_line do |line|
+ shell_out_compact_timeout("pkg", "info", new_resource.package_name).stdout.each_line do |line|
return $1.split[0] if line =~ /^\s+Version: (.*)/
end
- return nil
+ nil
end
def get_candidate_version
- shell_out_with_timeout!("pkg info -r #{new_resource.package_name}").stdout.each_line do |line|
+ shell_out_compact_timeout!("pkg", "info", "-r", new_resource.package_name).stdout.each_line do |line|
return $1.split[0] if line =~ /Version: (.*)/
end
- return nil
+ nil
end
def load_current_resource
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
- Chef::Log.debug("Checking package status for #{@new_resource.name}")
- @current_resource.version(get_current_version)
+ @current_resource = Chef::Resource::IpsPackage.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
+ logger.trace("Checking package status for #{new_resource.name}")
+ current_resource.version(get_current_version)
@candidate_version = get_candidate_version
- @current_resource
+ current_resource
end
def install_package(name, version)
- package_name = "#{name}@#{version}"
- normal_command = "pkg#{expand_options(@new_resource.options)} install -q #{package_name}"
- command =
- if @new_resource.respond_to?(:accept_license) && @new_resource.accept_license
- normal_command.gsub("-q", "-q --accept")
- else
- normal_command
- end
- shell_out_with_timeout(command)
+ command = [ "pkg", options, "install", "-q" ]
+ command << "--accept" if new_resource.accept_license
+ command << "#{name}@#{version}"
+ shell_out_compact_timeout!(command)
end
def upgrade_package(name, version)
@@ -83,7 +77,7 @@ class Chef
def remove_package(name, version)
package_name = "#{name}@#{version}"
- shell_out_with_timeout!( "pkg#{expand_options(@new_resource.options)} uninstall -q #{package_name}" )
+ shell_out_compact_timeout!( "pkg", options, "uninstall", "-q", package_name )
end
end
end
diff --git a/lib/chef/provider/package/macports.rb b/lib/chef/provider/package/macports.rb
index 7bbc68aba8..ddaf19a76f 100644
--- a/lib/chef/provider/package/macports.rb
+++ b/lib/chef/provider/package/macports.rb
@@ -7,25 +7,25 @@ class Chef
provides :macports_package
def load_current_resource
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
+ @current_resource = Chef::Resource::Package.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
- @current_resource.version(current_installed_version)
- Chef::Log.debug("#{@new_resource} current version is #{@current_resource.version}") if @current_resource.version
+ current_resource.version(current_installed_version)
+ logger.trace("#{new_resource} current version is #{current_resource.version}") if current_resource.version
@candidate_version = macports_candidate_version
- if !@new_resource.version && !@candidate_version
- raise Chef::Exceptions::Package, "Could not get a candidate version for this package -- #{@new_resource.name} does not seem to be a valid package!"
+ if !new_resource.version && !@candidate_version
+ raise Chef::Exceptions::Package, "Could not get a candidate version for this package -- #{new_resource.name} does not seem to be a valid package!"
end
- Chef::Log.debug("#{@new_resource} candidate version is #{@candidate_version}") if @candidate_version
+ logger.trace("#{new_resource} candidate version is #{@candidate_version}") if @candidate_version
- @current_resource
+ current_resource
end
def current_installed_version
- command = "port installed #{@new_resource.package_name}"
+ command = [ "port", "installed", new_resource.package_name ]
output = get_response_from_command(command)
response = nil
@@ -37,7 +37,7 @@ class Chef
end
def macports_candidate_version
- command = "port info --version #{@new_resource.package_name}"
+ command = [ "port", "info", "--version", new_resource.package_name ]
output = get_response_from_command(command)
match = output.match(/^version: (.+)$/)
@@ -46,37 +46,37 @@ class Chef
end
def install_package(name, version)
- unless @current_resource.version == version
- command = "port#{expand_options(@new_resource.options)} install #{name}"
- command << " @#{version}" if version && !version.empty?
- shell_out_with_timeout!(command)
+ unless current_resource.version == version
+ command = [ "port", options, "install", name ]
+ command << "@#{version}" if version && !version.empty?
+ shell_out_compact_timeout!(command)
end
end
def purge_package(name, version)
- command = "port#{expand_options(@new_resource.options)} uninstall #{name}"
- command << " @#{version}" if version && !version.empty?
- shell_out_with_timeout!(command)
+ command = [ "port", options, "uninstall", name ]
+ command << "@#{version}" if version && !version.empty?
+ shell_out_compact_timeout!(command)
end
def remove_package(name, version)
- command = "port#{expand_options(@new_resource.options)} deactivate #{name}"
- command << " @#{version}" if version && !version.empty?
+ command = [ "port", options, "deactivate", name ]
+ command << "@#{version}" if version && !version.empty?
- shell_out_with_timeout!(command)
+ shell_out_compact_timeout!(command)
end
def upgrade_package(name, version)
# Saving this to a variable -- weird rSpec behavior
# happens otherwise...
- current_version = @current_resource.version
+ current_version = current_resource.version
if current_version.nil? || current_version.empty?
# Macports doesn't like when you upgrade a package
# that hasn't been installed.
install_package(name, version)
elsif current_version != version
- shell_out_with_timeout!( "port#{expand_options(@new_resource.options)} upgrade #{name} @#{version}" )
+ shell_out_compact_timeout!( "port", options, "upgrade", name, "@#{version}" )
end
end
@@ -84,14 +84,14 @@ class Chef
def get_response_from_command(command)
output = nil
- status = shell_out_with_timeout(command)
+ status = shell_out_compact_timeout(command)
begin
output = status.stdout
rescue Exception
raise Chef::Exceptions::Package, "Could not read from STDOUT on command: #{command}"
end
unless status.exitstatus == 0 || status.exitstatus == 1
- raise Chef::Exceptions::Package, "#{command} failed - #{status.insect}!"
+ raise Chef::Exceptions::Package, "#{command} failed - #{status.inspect}!"
end
output
end
diff --git a/lib/chef/provider/package/msu.rb b/lib/chef/provider/package/msu.rb
new file mode 100644
index 0000000000..c4e53a0fdf
--- /dev/null
+++ b/lib/chef/provider/package/msu.rb
@@ -0,0 +1,161 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# msu_package leverages cab_package
+# The contents of msu file are extracted, which contains one or more cab files.
+# The extracted cab files are installed using Chef::Resource::Package::CabPackage
+# Reference: https://support.microsoft.com/en-in/kb/934307
+require "chef/provider/package"
+require "chef/resource/msu_package"
+require "chef/mixin/shell_out"
+require "chef/provider/package/cab"
+require "chef/util/path_helper"
+require "chef/mixin/uris"
+require "chef/mixin/checksum"
+
+class Chef
+ class Provider
+ class Package
+ class Msu < Chef::Provider::Package
+ include Chef::Mixin::ShellOut
+ include Chef::Mixin::Uris
+ include Chef::Mixin::Checksum
+
+ provides :msu_package
+
+ def load_current_resource
+ @current_resource = Chef::Resource::MsuPackage.new(new_resource.name)
+
+ # download file if source is a url
+ msu_file = uri_scheme?(new_resource.source) ? download_source_file : new_resource.source
+
+ # temp directory where the contents of msu file get extracted
+ @temp_directory = Dir.mktmpdir("chef")
+ extract_msu_contents(msu_file, @temp_directory)
+ @cab_files = read_cab_files_from_xml(@temp_directory)
+
+ if @cab_files.empty?
+ raise Chef::Exceptions::Package, "Corrupt MSU package: MSU package XML does not contain any cab file"
+ else
+ current_resource.version(get_current_versions)
+ end
+ current_resource
+ end
+
+ def get_current_versions
+ @cab_files.map do |cabfile|
+ cab_pkg = get_cab_package(cabfile)
+ cab_pkg.installed_version
+ end
+ end
+
+ def get_candidate_versions
+ @cab_files.map do |cabfile|
+ cab_pkg = get_cab_package(cabfile)
+ cab_pkg.package_version
+ end
+ end
+
+ def candidate_version
+ @candidate_version ||= get_candidate_versions
+ end
+
+ def get_cab_package(cab_file)
+ cab_resource = new_resource
+ cab_resource.source = cab_file
+ Chef::Provider::Package::Cab.new(cab_resource, nil)
+ end
+
+ def download_source_file
+ source_resource.run_action(:create)
+ logger.trace("#{new_resource} fetched source file to #{source_resource.path}")
+ source_resource.path
+ end
+
+ def source_resource
+ @source_resource ||= declare_resource(:remote_file, new_resource.name) do
+ path default_download_cache_path
+ source new_resource.source
+ checksum new_resource.checksum
+ backup false
+ end
+ end
+
+ def default_download_cache_path
+ uri = ::URI.parse(new_resource.source)
+ filename = ::File.basename(::URI.unescape(uri.path))
+ file_cache_dir = Chef::FileCache.create_cache_path("package/")
+ Chef::Util::PathHelper.cleanpath("#{file_cache_dir}/#{filename}")
+ end
+
+ def install_package(name, version)
+ # use cab_package resource to install the extracted cab packages
+ @cab_files.each do |cab_file|
+ declare_resource(:cab_package, new_resource.name) do
+ source cab_file
+ action :install
+ end
+ end
+ end
+
+ def remove_package(name, version)
+ # use cab_package provider to remove the extracted cab packages
+ @cab_files.each do |cab_file|
+ declare_resource(:cab_package, new_resource.name) do
+ source cab_file
+ action :remove
+ end
+ end
+ end
+
+ def extract_msu_contents(msu_file, destination)
+ with_os_architecture(nil) do
+ shell_out_with_timeout!("#{ENV['SYSTEMROOT']}\\system32\\expand.exe -f:* #{msu_file} #{destination}")
+ end
+ end
+
+ # msu package can contain multiple cab files
+ # Reading cab files from xml to ensure the order of installation in case of multiple cab files
+ def read_cab_files_from_xml(msu_dir)
+ # get the file with .xml extension
+ xml_files = Dir.glob("#{msu_dir}/*.xml")
+ cab_files = []
+
+ if xml_files.empty?
+ raise Chef::Exceptions::Package, "Corrupt MSU package: MSU package doesn't contain any xml file"
+ else
+ # msu package contains only single xml file. So using xml_files.first is sufficient
+ doc = ::File.open(xml_files.first.to_s) { |f| REXML::Document.new f }
+ locations = doc.elements.each("unattend/servicing/package/source") { |element| element.attributes["location"] }
+ locations.each do |loc|
+ cab_files << msu_dir + "/" + loc.attribute("location").value.split("\\")[1]
+ end
+
+ cab_files
+ end
+ cab_files
+ end
+
+ def cleanup_after_converge
+ # delete the temp directory where the contents of msu file are extracted
+ FileUtils.rm_rf(@temp_directory) if Dir.exist?(@temp_directory)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/openbsd.rb b/lib/chef/provider/package/openbsd.rb
index 2120b9aa48..f528c48f08 100644
--- a/lib/chef/provider/package/openbsd.rb
+++ b/lib/chef/provider/package/openbsd.rb
@@ -42,9 +42,9 @@ class Chef
end
def load_current_resource
- @current_resource.package_name(new_resource.package_name)
- @current_resource.version(installed_version)
- @current_resource
+ current_resource.package_name(new_resource.package_name)
+ current_resource.version(installed_version)
+ current_resource
end
def define_resource_requirements
@@ -68,12 +68,12 @@ class Chef
end
def install_package(name, version)
- unless @current_resource.version
+ unless current_resource.version
if parts = name.match(/^(.+?)--(.+)/) # use double-dash for stems with flavors, see man page for pkg_add
name = parts[1]
end
- shell_out_with_timeout!("pkg_add -r #{name}#{version_string(version)}", :env => { "PKG_PATH" => pkg_path }).status
- Chef::Log.debug("#{new_resource.package_name} installed")
+ shell_out_compact_timeout!("pkg_add", "-r", package_string(name, version), env: { "PKG_PATH" => pkg_path }).status
+ logger.trace("#{new_resource.package_name} installed")
end
end
@@ -81,35 +81,35 @@ class Chef
if parts = name.match(/^(.+?)--(.+)/)
name = parts[1]
end
- shell_out_with_timeout!("pkg_delete #{name}#{version_string(version)}", :env => nil).status
+ shell_out_compact_timeout!("pkg_delete", package_string(name, version), env: nil).status
end
private
def installed_version
- if parts = new_resource.package_name.match(/^(.+?)--(.+)/)
- name = parts[1]
- else
- name = new_resource.package_name
- end
- pkg_info = shell_out_with_timeout!("pkg_info -e \"#{name}->0\"", :env => nil, :returns => [0, 1])
+ name = if parts = new_resource.package_name.match(/^(.+?)--(.+)/)
+ parts[1]
+ else
+ new_resource.package_name
+ end
+ pkg_info = shell_out_compact_timeout!("pkg_info", "-e", "#{name}->0", env: nil, returns: [0, 1])
result = pkg_info.stdout[/^inst:#{Regexp.escape(name)}-(.+?)\s/, 1]
- Chef::Log.debug("installed_version of '#{new_resource.package_name}' is '#{result}'")
+ logger.trace("installed_version of '#{new_resource.package_name}' is '#{result}'")
result
end
def candidate_version
@candidate_version ||= begin
results = []
- shell_out_with_timeout!("pkg_info -I \"#{new_resource.package_name}#{version_string(new_resource.version)}\"", :env => nil, :returns => [0, 1]).stdout.each_line do |line|
- if parts = new_resource.package_name.match(/^(.+?)--(.+)/)
- results << line[/^#{Regexp.escape(parts[1])}-(.+?)\s/, 1]
- else
- results << line[/^#{Regexp.escape(new_resource.package_name)}-(.+?)\s/, 1]
- end
+ shell_out_compact_timeout!("pkg_info", "-I", package_string(new_resource.package_name, new_resource.version), env: nil, returns: [0, 1]).stdout.each_line do |line|
+ results << if parts = new_resource.package_name.match(/^(.+?)--(.+)/)
+ line[/^#{Regexp.escape(parts[1])}-(.+?)\s/, 1]
+ else
+ line[/^#{Regexp.escape(new_resource.package_name)}-(.+?)\s/, 1]
+ end
end
results = results.reject(&:nil?)
- Chef::Log.debug("Candidate versions of '#{new_resource.package_name}' are '#{results}'")
+ logger.trace("Candidate versions of '#{new_resource.package_name}' are '#{results}'")
case results.length
when 0
[]
@@ -121,13 +121,16 @@ class Chef
end
end
- def version_string(version)
- ver = ""
- ver += "-#{version}" if version
+ def package_string(name, version)
+ if version
+ "#{name}-#{version}"
+ else
+ name
+ end
end
def pkg_path
- ENV["PKG_PATH"] || "http://ftp.OpenBSD.org/pub/#{node.kernel.name}/#{node.kernel.release}/packages/#{node.kernel.machine}/"
+ ENV["PKG_PATH"] || "http://ftp.OpenBSD.org/pub/#{node['kernel']['name']}/#{node['kernel']['release']}/packages/#{node['kernel']['machine']}/"
end
end
diff --git a/lib/chef/provider/package/pacman.rb b/lib/chef/provider/package/pacman.rb
index bd8028d881..f6dde66219 100644
--- a/lib/chef/provider/package/pacman.rb
+++ b/lib/chef/provider/package/pacman.rb
@@ -17,7 +17,6 @@
#
require "chef/provider/package"
-require "chef/mixin/command"
require "chef/resource/package"
class Chef
@@ -26,19 +25,19 @@ class Chef
class Pacman < Chef::Provider::Package
provides :package, platform: "arch"
- provides :pacman_package, os: "linux"
+ provides :pacman_package
def load_current_resource
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
+ @current_resource = Chef::Resource::Package.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
- Chef::Log.debug("#{@new_resource} checking pacman for #{@new_resource.package_name}")
- status = shell_out_with_timeout("pacman -Qi #{@new_resource.package_name}")
+ logger.trace("#{new_resource} checking pacman for #{new_resource.package_name}")
+ status = shell_out_compact_timeout("pacman", "-Qi", new_resource.package_name)
status.stdout.each_line do |line|
case line
when /^Version(\s?)*: (.+)$/
- Chef::Log.debug("#{@new_resource} current version is #{$2}")
- @current_resource.version($2)
+ logger.trace("#{new_resource} current version is #{$2}")
+ current_resource.version($2)
end
end
@@ -46,7 +45,7 @@ class Chef
raise Chef::Exceptions::Package, "pacman failed - #{status.inspect}!"
end
- @current_resource
+ current_resource
end
def candidate_version
@@ -54,20 +53,20 @@ class Chef
repos = %w{extra core community}
- if ::File.exists?("/etc/pacman.conf")
+ if ::File.exist?("/etc/pacman.conf")
pacman = ::File.read("/etc/pacman.conf")
repos = pacman.scan(/\[(.+)\]/).flatten
end
package_repos = repos.map { |r| Regexp.escape(r) }.join("|")
- status = shell_out_with_timeout("pacman -Sl")
+ status = shell_out_compact_timeout("pacman", "-Sl")
status.stdout.each_line do |line|
case line
- when /^(#{package_repos}) #{Regexp.escape(@new_resource.package_name)} (.+)$/
- # $2 contains a string like "4.4.0-1" or "3.10-4 [installed]"
- # simply split by space and use first token
- @candidate_version = $2.split(" ").first
+ when /^(#{package_repos}) #{Regexp.escape(new_resource.package_name)} (.+)$/
+ # $2 contains a string like "4.4.0-1" or "3.10-4 [installed]"
+ # simply split by space and use first token
+ @candidate_version = $2.split(" ").first
end
end
@@ -76,14 +75,14 @@ class Chef
end
unless @candidate_version
- raise Chef::Exceptions::Package, "pacman does not have a version of package #{@new_resource.package_name}"
+ raise Chef::Exceptions::Package, "pacman does not have a version of package #{new_resource.package_name}"
end
@candidate_version
end
def install_package(name, version)
- shell_out_with_timeout!( "pacman --sync --noconfirm --noprogressbar#{expand_options(@new_resource.options)} #{name}" )
+ shell_out_compact_timeout!( "pacman", "--sync", "--noconfirm", "--noprogressbar", options, name)
end
def upgrade_package(name, version)
@@ -91,7 +90,7 @@ class Chef
end
def remove_package(name, version)
- shell_out_with_timeout!( "pacman --remove --noconfirm --noprogressbar#{expand_options(@new_resource.options)} #{name}" )
+ shell_out_compact_timeout!( "pacman", "--remove", "--noconfirm", "--noprogressbar", options, name )
end
def purge_package(name, version)
diff --git a/lib/chef/provider/package/paludis.rb b/lib/chef/provider/package/paludis.rb
index 557e7ebc22..f6274d7553 100644
--- a/lib/chef/provider/package/paludis.rb
+++ b/lib/chef/provider/package/paludis.rb
@@ -25,41 +25,40 @@ class Chef
class Paludis < Chef::Provider::Package
provides :package, platform: "exherbo"
- provides :paludis_package, os: "linux"
+ provides :paludis_package
def load_current_resource
- @current_resource = Chef::Resource::Package.new(@new_resource.package_name)
- @current_resource.package_name(@new_resource.package_name)
+ @current_resource = Chef::Resource::Package.new(new_resource.package_name)
+ current_resource.package_name(new_resource.package_name)
- Chef::Log.debug("Checking package status for #{@new_resource.package_name}")
+ logger.trace("Checking package status for #{new_resource.package_name}")
installed = false
re = Regexp.new("(.*)[[:blank:]](.*)[[:blank:]](.*)$")
- shell_out!("cave -L warning print-ids -M none -m \"#{@new_resource.package_name}\" -f \"%c/%p %v %r\n\"").stdout.each_line do |line|
+ shell_out_compact!("cave", "-L", "warning", "print-ids", "-M", "none", "-m", new_resource.package_name, "-f", "%c/%p %v %r\n").stdout.each_line do |line|
res = re.match(line)
- unless res.nil?
- case res[3]
- when "accounts", "installed-accounts"
- next
- when "installed"
- installed = true
- @current_resource.version(res[2])
- else
- @candidate_version = res[2]
- end
+ next if res.nil?
+ case res[3]
+ when "accounts", "installed-accounts"
+ next
+ when "installed"
+ installed = true
+ current_resource.version(res[2])
+ else
+ @candidate_version = res[2]
end
end
- @current_resource
+ current_resource
end
def install_package(name, version)
- if version
- pkg = "=#{name}-#{version}"
- else
- pkg = "#{@new_resource.package_name}"
- end
- shell_out!("cave -L warning resolve -x#{expand_options(@new_resource.options)} \"#{pkg}\"", :timeout => @new_resource.timeout)
+ pkg = if version
+ "=#{name}-#{version}"
+ else
+ new_resource.package_name.to_s
+ end
+ shell_out_compact_timeout!("cave", "-L", "warning", "resolve", "-x", options, pkg)
end
def upgrade_package(name, version)
@@ -67,13 +66,13 @@ class Chef
end
def remove_package(name, version)
- if version
- pkg = "=#{@new_resource.package_name}-#{version}"
- else
- pkg = "#{@new_resource.package_name}"
- end
+ pkg = if version
+ "=#{new_resource.package_name}-#{version}"
+ else
+ new_resource.package_name.to_s
+ end
- shell_out!("cave -L warning uninstall -x#{expand_options(@new_resource.options)} \"#{pkg}\"")
+ shell_out_compact!("cave", "-L", "warning", "uninstall", "-x", options, pkg)
end
def purge_package(name, version)
diff --git a/lib/chef/provider/package/portage.rb b/lib/chef/provider/package/portage.rb
index 7c52e43bff..fecbba9dc9 100644
--- a/lib/chef/provider/package/portage.rb
+++ b/lib/chef/provider/package/portage.rb
@@ -17,8 +17,7 @@
#
require "chef/provider/package"
-require "chef/mixin/command"
-require "chef/resource/package"
+require "chef/resource/portage_package"
require "chef/util/path_helper"
class Chef
@@ -32,74 +31,68 @@ class Chef
PACKAGE_NAME_PATTERN = %r{(?:([^/]+)/)?([^/]+)}
def load_current_resource
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
+ @current_resource = Chef::Resource::Package.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
- category, pkg = %r{^#{PACKAGE_NAME_PATTERN}$}.match(@new_resource.package_name)[1, 2]
+ category, pkg = /^#{PACKAGE_NAME_PATTERN}$/.match(new_resource.package_name)[1, 2]
globsafe_category = category ? Chef::Util::PathHelper.escape_glob_dir(category) : nil
globsafe_pkg = Chef::Util::PathHelper.escape_glob_dir(pkg)
- possibilities = Dir["/var/db/pkg/#{globsafe_category || "*"}/#{globsafe_pkg}-*"].map { |d| d.sub(%r{/var/db/pkg/}, "") }
+ possibilities = Dir["/var/db/pkg/#{globsafe_category || '*'}/#{globsafe_pkg}-*"].map { |d| d.sub(%r{/var/db/pkg/}, "") }
versions = possibilities.map do |entry|
- if entry =~ %r{[^/]+/#{Regexp.escape(pkg)}\-(\d[\.\d]*((_(alpha|beta|pre|rc|p)\d*)*)?(-r\d+)?)}
+ if entry =~ %r{[^/]+/#{Regexp.escape(pkg)}\-(\d[\.\d]*[a-z]?((_(alpha|beta|pre|rc|p)\d*)*)?(-r\d+)?)}
[$&, $1]
end
end.compact
if versions.size > 1
- atoms = versions.map { |v| v.first }.sort
+ atoms = versions.map(&:first).sort
categories = atoms.map { |v| v.split("/")[0] }.uniq
if !category && categories.size > 1
- raise Chef::Exceptions::Package, "Multiple packages found for #{@new_resource.package_name}: #{atoms.join(" ")}. Specify a category."
+ raise Chef::Exceptions::Package, "Multiple packages found for #{new_resource.package_name}: #{atoms.join(' ')}. Specify a category."
end
elsif versions.size == 1
- @current_resource.version(versions.first.last)
- Chef::Log.debug("#{@new_resource} current version #{$1}")
+ current_resource.version(versions.first.last)
+ logger.trace("#{new_resource} current version #{$1}")
end
- @current_resource
+ current_resource
end
- def parse_emerge(package, txt)
- availables = {}
- found_package_name = nil
+ def raise_error_for_query(msg)
+ raise Chef::Exceptions::Package, "Query for '#{new_resource.package_name}' #{msg}"
+ end
- txt.each_line do |line|
- if line =~ /\*\s+#{PACKAGE_NAME_PATTERN}/
- found_package_name = $&.delete("*").strip
- if package =~ /\// #the category is specified
- if found_package_name == package
- availables[found_package_name] = nil
- end
- else #the category is not specified
- if found_package_name.split("/").last == package
- availables[found_package_name] = nil
- end
+ def candidate_version
+ return @candidate_version if @candidate_version
+
+ pkginfo = shell_out_compact("portageq", "best_visible", "/", new_resource.package_name)
+
+ if pkginfo.exitstatus != 0
+ pkginfo.stderr.each_line do |line|
+ if line =~ /[Uu]nqualified atom .*match.* multiple/
+ raise_error_for_query("matched multiple packages (please specify a category):\n#{pkginfo.inspect}")
end
end
- if line =~ /Latest version available: (.*)/ && availables.has_key?(found_package_name)
- availables[found_package_name] = $1.strip
+ if pkginfo.stdout.strip.empty?
+ raise_error_for_query("did not find a matching package:\n#{pkginfo.inspect}")
end
- end
- if availables.size > 1
- # shouldn't happen if a category is specified so just use `package`
- raise Chef::Exceptions::Package, "Multiple emerge results found for #{package}: #{availables.keys.join(" ")}. Specify a category."
+ raise_error_for_query("resulted in an unknown error:\n#{pkginfo.inspect}")
end
- availables.values.first
- end
-
- def candidate_version
- return @candidate_version if @candidate_version
-
- status = shell_out("emerge --color n --nospinner --search #{@new_resource.package_name.split('/').last}")
- available, installed = parse_emerge(@new_resource.package_name, status.stdout)
- @candidate_version = available
+ if pkginfo.stdout.lines.count > 1
+ raise_error_for_query("produced unexpected output (multiple lines):\n#{pkginfo.inspect}")
+ end
- unless status.exitstatus == 0
- raise Chef::Exceptions::Package, "emerge --search failed - #{status.inspect}!"
+ pkginfo.stdout.chomp!
+ if pkginfo.stdout =~ /-r\d+$/
+ # Latest/Best version of the package is a revision (-rX).
+ @candidate_version = pkginfo.stdout.split(/(?<=-)/).last(2).join
+ else
+ # Latest/Best version of the package is NOT a revision (-rX).
+ @candidate_version = pkginfo.stdout.split("-").last
end
@candidate_version
@@ -113,7 +106,7 @@ class Chef
pkg = "~#{name}-#{$1}"
end
- shell_out!( "emerge -g --color n --nospinner --quiet#{expand_options(@new_resource.options)} #{pkg}" )
+ shell_out_compact!( "emerge", "-g", "--color", "n", "--nospinner", "--quiet", options, pkg )
end
def upgrade_package(name, version)
@@ -121,13 +114,13 @@ class Chef
end
def remove_package(name, version)
- if version
- pkg = "=#{@new_resource.package_name}-#{version}"
- else
- pkg = "#{@new_resource.package_name}"
- end
+ pkg = if version
+ "=#{new_resource.package_name}-#{version}"
+ else
+ new_resource.package_name.to_s
+ end
- shell_out!( "emerge --unmerge --color n --nospinner --quiet#{expand_options(@new_resource.options)} #{pkg}" )
+ shell_out_compact!( "emerge", "--unmerge", "--color", "n", "--nospinner", "--quiet", options, pkg )
end
def purge_package(name, version)
diff --git a/lib/chef/provider/package/powershell.rb b/lib/chef/provider/package/powershell.rb
new file mode 100644
index 0000000000..44b6e69a00
--- /dev/null
+++ b/lib/chef/provider/package/powershell.rb
@@ -0,0 +1,129 @@
+# Author:: Dheeraj Dubey(dheeraj.dubey@msystechnologies.com)
+# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/provider/package"
+require "chef/resource/powershell_package"
+require "chef/mixin/powershell_out"
+
+class Chef
+ class Provider
+ class Package
+ class Powershell < Chef::Provider::Package
+ include Chef::Mixin::PowershellOut
+
+ provides :powershell_package
+
+ def load_current_resource
+ @current_resource = Chef::Resource::PowershellPackage.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
+ current_resource.version(build_current_versions)
+ current_resource
+ end
+
+ def define_resource_requirements
+ super
+ if powershell_out("$PSVersionTable.PSVersion.Major").stdout.strip.to_i < 5
+ raise "Minimum installed Powershell Version required is 5"
+ end
+ requirements.assert(:install) do |a|
+ a.assertion { candidates_exist_for_all_uninstalled? }
+ a.failure_message(Chef::Exceptions::Package, "No candidate version available for #{packages_missing_candidates.join(', ')}")
+ a.whyrun("Assuming a repository that offers #{packages_missing_candidates.join(', ')} would have been configured")
+ end
+ end
+
+ def candidate_version
+ @candidate_version ||= build_candidate_versions
+ end
+
+ # Installs the package specified with the version passed else latest version will be installed
+ def install_package(names, versions)
+ names.each_with_index do |name, index|
+ powershell_out(build_powershell_package_command("Install-Package '#{name}'", versions[index]), timeout: new_resource.timeout)
+ end
+ end
+
+ # Removes the package for the version passed and if no version is passed, then all installed versions of the package are removed
+ def remove_package(names, versions)
+ names.each_with_index do |name, index|
+ if versions && !versions[index].nil?
+ powershell_out(build_powershell_package_command("Uninstall-Package '#{name}'", versions[index]), timeout: new_resource.timeout)
+ else
+ version = "0"
+ until version.empty?
+ version = powershell_out(build_powershell_package_command("Uninstall-Package '#{name}'"), timeout: new_resource.timeout).stdout.strip
+ unless version.empty?
+ logger.info("Removed package '#{name}' with version #{version}")
+ end
+ end
+ end
+ end
+ end
+
+ # Returns array of available available online
+ def build_candidate_versions
+ versions = []
+ new_resource.package_name.each_with_index do |name, index|
+ version = if new_resource.version && !new_resource.version[index].nil?
+ powershell_out(build_powershell_package_command("Find-Package '#{name}'", new_resource.version[index]), timeout: new_resource.timeout).stdout.strip
+ else
+ powershell_out(build_powershell_package_command("Find-Package '#{name}'"), timeout: new_resource.timeout).stdout.strip
+ end
+ if version.empty?
+ version = nil
+ end
+ versions.push(version)
+ end
+ versions
+ end
+
+ # Returns version array of installed version on the system
+ def build_current_versions
+ version_list = []
+ new_resource.package_name.each_with_index do |name, index|
+ version = if new_resource.version && !new_resource.version[index].nil?
+ powershell_out(build_powershell_package_command("Get-Package '#{name}'", new_resource.version[index]), timeout: new_resource.timeout).stdout.strip
+ else
+ powershell_out(build_powershell_package_command("Get-Package '#{name}'"), timeout: new_resource.timeout).stdout.strip
+ end
+ if version.empty?
+ version = nil
+ end
+ version_list.push(version)
+ end
+ version_list
+ end
+
+ def build_powershell_package_command(command, version = nil)
+ command = [command] unless command.is_a?(Array)
+ command.unshift("(")
+ %w{-Force -ForceBootstrap}.each do |arg|
+ command.push(arg)
+ end
+ command.push("-RequiredVersion #{version}") if version
+ command.push("-Source #{new_resource.source}") if new_resource.source && command[1] =~ Regexp.union(/Install-Package/, /Find-Package/)
+ command.push(").Version")
+ command.join(" ")
+ end
+
+ def check_resource_semantics!
+ # This validation method from Chef::Provider::Package does not apply here, so no-op it.
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/rpm.rb b/lib/chef/provider/package/rpm.rb
index 777cc6d209..9d4f2f3c23 100644
--- a/lib/chef/provider/package/rpm.rb
+++ b/lib/chef/provider/package/rpm.rb
@@ -16,16 +16,15 @@
# limitations under the License.
#
require "chef/provider/package"
-require "chef/mixin/command"
require "chef/resource/package"
require "chef/mixin/get_source_from_package"
+require "chef/provider/package/yum/rpm_utils"
class Chef
class Provider
class Package
class Rpm < Chef::Provider::Package
-
- provides :rpm_package, os: %w{linux aix}
+ provides :rpm_package
include Chef::Mixin::GetSourceFromPackage
@@ -34,13 +33,13 @@ class Chef
requirements.assert(:all_actions) do |a|
a.assertion { @package_source_exists }
- a.failure_message Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}"
- a.whyrun "Assuming package #{@new_resource.name} would have been made available."
+ a.failure_message Chef::Exceptions::Package, "Package #{new_resource.name} not found: #{new_resource.source}"
+ a.whyrun "Assuming package #{new_resource.name} would have been made available."
end
requirements.assert(:all_actions) do |a|
a.assertion { !@rpm_status.nil? && (@rpm_status.exitstatus == 0 || @rpm_status.exitstatus == 1) }
a.failure_message Chef::Exceptions::Package, "Unable to determine current version due to RPM failure. Detail: #{@rpm_status.inspect}"
- a.whyrun "Assuming current version would have been determined for package#{@new_resource.name}."
+ a.whyrun "Assuming current version would have been determined for package#{new_resource.name}."
end
end
@@ -48,74 +47,78 @@ class Chef
@package_source_provided = true
@package_source_exists = true
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
+ @current_resource = Chef::Resource::Package.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
- if @new_resource.source
- unless uri_scheme?(@new_resource.source) || ::File.exists?(@new_resource.source)
+ if new_resource.source
+ unless uri_scheme?(new_resource.source) || ::File.exist?(new_resource.source)
@package_source_exists = false
return
end
- Chef::Log.debug("#{@new_resource} checking rpm status")
- shell_out_with_timeout!("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@new_resource.source}").stdout.each_line do |line|
+ logger.trace("#{new_resource} checking rpm status")
+ shell_out_compact_timeout!("rpm", "-qp", "--queryformat", "%{NAME} %{VERSION}-%{RELEASE}\n", new_resource.source).stdout.each_line do |line|
case line
when /^(\S+)\s(\S+)$/
- @current_resource.package_name($1)
- @new_resource.version($2)
+ current_resource.package_name($1)
+ new_resource.version($2)
@candidate_version = $2
end
end
else
- if Array(@new_resource.action).include?(:install)
+ if Array(new_resource.action).include?(:install)
@package_source_exists = false
return
end
end
- Chef::Log.debug("#{@new_resource} checking install state")
- @rpm_status = shell_out_with_timeout("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@current_resource.package_name}")
+ logger.trace("#{new_resource} checking install state")
+ @rpm_status = shell_out_compact_timeout("rpm", "-q", "--queryformat", "%{NAME} %{VERSION}-%{RELEASE}\n", current_resource.package_name)
@rpm_status.stdout.each_line do |line|
case line
when /^(\S+)\s(\S+)$/
- Chef::Log.debug("#{@new_resource} current version is #{$2}")
- @current_resource.version($2)
+ logger.trace("#{new_resource} current version is #{$2}")
+ current_resource.version($2)
end
end
- @current_resource
+ current_resource
end
def install_package(name, version)
- unless @current_resource.version
- shell_out_with_timeout!( "rpm #{@new_resource.options} -i #{@new_resource.source}" )
- else
+ if current_resource.version
if allow_downgrade
- shell_out_with_timeout!( "rpm #{@new_resource.options} -U --oldpackage #{@new_resource.source}" )
+ shell_out_compact_timeout!("rpm", options, "-U", "--oldpackage", new_resource.source)
else
- shell_out_with_timeout!( "rpm #{@new_resource.options} -U #{@new_resource.source}" )
+ shell_out_compact_timeout!("rpm", options, "-U", new_resource.source)
end
+ else
+ shell_out_compact_timeout!("rpm", options, "-i", new_resource.source)
end
end
- alias_method :upgrade_package, :install_package
+ alias upgrade_package install_package
def remove_package(name, version)
if version
- shell_out_with_timeout!( "rpm #{@new_resource.options} -e #{name}-#{version}" )
+ shell_out_compact_timeout!("rpm", options, "-e", "#{name}-#{version}")
else
- shell_out_with_timeout!( "rpm #{@new_resource.options} -e #{name}" )
+ shell_out_compact_timeout!("rpm", options, "-e", name)
end
end
private
+ def version_compare(v1, v2)
+ Chef::Provider::Package::Yum::RPMVersion.parse(v1) <=> Chef::Provider::Package::Yum::RPMVersion.parse(v2)
+ end
+
def uri_scheme?(str)
scheme = URI.split(str).first
return false unless scheme
%w{http https ftp file}.include?(scheme.downcase)
rescue URI::InvalidURIError
- return false
+ false
end
end
end
diff --git a/lib/chef/provider/package/rubygems.rb b/lib/chef/provider/package/rubygems.rb
index a15ee1bdea..6b04af547b 100644
--- a/lib/chef/provider/package/rubygems.rb
+++ b/lib/chef/provider/package/rubygems.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2008-2016, 2010-2016 Chef Software, Inc.
+# Copyright:: Copyright 2008-2016, 2010-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,9 +19,9 @@
require "uri"
require "chef/provider/package"
-require "chef/mixin/command"
require "chef/resource/package"
require "chef/mixin/get_source_from_package"
+require "chef/mixin/which"
# Class methods on Gem are defined in rubygems
require "rubygems"
@@ -47,7 +47,7 @@ class Chef
# alternate value and overwrite it with the defaults.
Gem.configuration
- DEFAULT_UNINSTALLER_OPTS = { :ignore => true, :executables => true }
+ DEFAULT_UNINSTALLER_OPTS = { ignore: true, executables: true }.freeze
##
# The paths where rubygems should search for installed gems.
@@ -86,7 +86,22 @@ class Chef
# === Returns
# [Gem::Specification] an array of Gem::Specification objects
def installed_versions(gem_dep)
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("1.8.0")
+ rubygems_version = Gem::Version.new(Gem::VERSION)
+ if rubygems_version >= Gem::Version.new("2.7")
+ # In newer Rubygems, bundler is now a "default gem" which means
+ # even with AlternateGemEnvironment when you try to get the
+ # installed versions, you get the one from Chef's Ruby's default
+ # gems. This workaround ignores default gems entirely so we see
+ # only the installed gems.
+ stubs = gem_specification.send(:installed_stubs, gem_specification.dirs, "#{gem_dep.name}-*.gemspec")
+ # Filter down to only to only stubs we actually want. The name
+ # filter is needed in case of things like `foo-*.gemspec` also
+ # matching a gem named `foo-bar`.
+ stubs.select! { |stub| stub.name == gem_dep.name && gem_dep.requirement.satisfied_by?(stub.version) }
+ # This isn't sorting before returning beacuse the only code that
+ # uses this method calls `max_by` so it doesn't need to be sorted.
+ stubs
+ elsif rubygems_version >= Gem::Version.new("1.8.0")
gem_specification.find_all_by_name(gem_dep.name, gem_dep.requirement)
else
gem_source_index.search(gem_dep)
@@ -133,11 +148,11 @@ class Chef
def candidate_version_from_file(gem_dependency, source)
spec = spec_from_file(source)
if spec.satisfies_requirement?(gem_dependency)
- logger.debug { "#{@new_resource} found candidate gem version #{spec.version} from local gem package #{source}" }
+ logger.trace { "found candidate gem version #{spec.version} from local gem package #{source}" }
spec.version
else
# This is probably going to end badly...
- logger.warn { "#{@new_resource} gem package #{source} does not satisfy the requirements #{gem_dependency}" }
+ logger.warn { "gem package #{source} does not satisfy the requirements #{gem_dependency}" }
nil
end
end
@@ -159,27 +174,30 @@ class Chef
# Find the newest gem version available from Gem.sources that satisfies
# the constraints of +gem_dependency+
def find_newest_remote_version(gem_dependency, *sources)
- available_gems = dependency_installer.find_gems_with_sources(gem_dependency)
- spec, source = if available_gems.respond_to?(:last)
- # DependencyInstaller sorts the results such that the last one is
- # always the one it considers best.
- spec_with_source = available_gems.last
- spec_with_source && spec_with_source
- else
- # Rubygems 2.0 returns a Gem::Available set, which is a
- # collection of AvailableSet::Tuple structs
- available_gems.pick_best!
- best_gem = available_gems.set.first
- best_gem && [best_gem.spec, best_gem.source]
- end
+ spec, source =
+ if Chef::Config[:rubygems_cache_enabled]
+ # This code caches every gem on rubygems.org and uses lots of RAM
+ available_gems = dependency_installer.find_gems_with_sources(gem_dependency)
+ available_gems.pick_best!
+ best_gem = available_gems.set.first
+ best_gem && [best_gem.spec, best_gem.source]
+ else
+ # Use the API that 'gem install' calls which does not pull down the rubygems universe
+ begin
+ rs = dependency_installer.resolve_dependencies gem_dependency.name, gem_dependency.requirement
+ rs.specs.select { |s| s.name == gem_dependency.name }.first
+ rescue Gem::UnsatisfiableDependencyError
+ nil
+ end
+ end
version = spec && spec.version
if version
- logger.debug { "#{@new_resource} found gem #{spec.name} version #{version} for platform #{spec.platform} from #{source}" }
+ logger.trace { "found gem #{spec.name} version #{version} for platform #{spec.platform} from #{source}" }
version
else
source_list = sources.compact.empty? ? "[#{Gem.sources.to_a.join(', ')}]" : "[#{sources.join(', ')}]"
- logger.warn { "#{@new_resource} failed to find gem #{gem_dependency} from #{source_list}" }
+ logger.warn { "failed to find gem #{gem_dependency} from #{source_list}" }
nil
end
end
@@ -214,7 +232,7 @@ class Chef
# Set rubygems' user interaction to ConsoleUI or SilentUI depending
# on our current debug level
def with_correct_verbosity
- Gem::DefaultUserInteraction.ui = Chef::Log.debug? ? Gem::ConsoleUI.new : Gem::SilentUI.new
+ Gem::DefaultUserInteraction.ui = logger.trace? ? Gem::ConsoleUI.new : Gem::SilentUI.new
yield
end
@@ -229,7 +247,7 @@ class Chef
private
def logger
- Chef::Log.logger
+ Chef::Log.with_child({ subsytem: "gem_installer_environment" })
end
end
@@ -282,7 +300,7 @@ class Chef
# shellout! is a fork/exec which won't work on windows
shell_style_paths = shell_out!("#{@gem_binary_location} env gempath").stdout
# on windows, the path separator is (usually? always?) semicolon
- paths = shell_style_paths.split(::File::PATH_SEPARATOR).map { |path| path.strip }
+ paths = shell_style_paths.split(::File::PATH_SEPARATOR).map(&:strip)
self.class.gempath_cache[@gem_binary_location] = paths
end
end
@@ -319,11 +337,11 @@ class Chef
self.class.platform_cache[@gem_binary_location]
else
gem_environment = shell_out!("#{@gem_binary_location} env").stdout
- if jruby = gem_environment[JRUBY_PLATFORM]
- self.class.platform_cache[@gem_binary_location] = ["ruby", Gem::Platform.new(jruby)]
- else
- self.class.platform_cache[@gem_binary_location] = Gem.platforms
- end
+ self.class.platform_cache[@gem_binary_location] = if jruby = gem_environment[JRUBY_PLATFORM]
+ ["ruby", Gem::Platform.new(jruby)]
+ else
+ Gem.platforms
+ end
end
end
@@ -349,30 +367,27 @@ class Chef
attr_reader :gem_env
attr_reader :cleanup_gem_env
- def logger
- Chef::Log.logger
- end
-
provides :chef_gem
provides :gem_package
include Chef::Mixin::GetSourceFromPackage
+ include Chef::Mixin::Which
def initialize(new_resource, run_context = nil)
super
@cleanup_gem_env = true
if new_resource.gem_binary
- if new_resource.options && new_resource.options.kind_of?(Hash)
+ if new_resource.options && new_resource.options.is_a?(Hash)
msg = "options cannot be given as a hash when using an explicit gem_binary\n"
msg << "in #{new_resource} from #{new_resource.source_line}"
raise ArgumentError, msg
end
@gem_env = AlternateGemEnvironment.new(new_resource.gem_binary)
- Chef::Log.debug("#{@new_resource} using gem '#{new_resource.gem_binary}'")
- elsif is_omnibus? && (!@new_resource.instance_of? Chef::Resource::ChefGem)
+ logger.trace("#{new_resource} using gem '#{new_resource.gem_binary}'")
+ elsif is_omnibus? && (!new_resource.instance_of? Chef::Resource::ChefGem)
# Opscode Omnibus - The ruby that ships inside omnibus is only used for Chef
# Default to installing somewhere more functional
- if new_resource.options && new_resource.options.kind_of?(Hash)
+ if new_resource.options && new_resource.options.is_a?(Hash)
msg = [
"Gem options must be passed to gem_package as a string instead of a hash when",
"using this installation of Chef because it runs with its own packaged Ruby. A hash",
@@ -383,23 +398,23 @@ class Chef
raise ArgumentError, msg
end
gem_location = find_gem_by_path
- @new_resource.gem_binary gem_location
+ new_resource.gem_binary gem_location
@gem_env = AlternateGemEnvironment.new(gem_location)
- Chef::Log.debug("#{@new_resource} using gem '#{gem_location}'")
+ logger.trace("#{new_resource} using gem '#{gem_location}'")
else
@gem_env = CurrentGemEnvironment.new
@cleanup_gem_env = false
- Chef::Log.debug("#{@new_resource} using gem from running ruby environment")
+ logger.trace("#{new_resource} using gem from running ruby environment")
end
end
def is_omnibus?
if RbConfig::CONFIG["bindir"] =~ %r{/(opscode|chef|chefdk)/embedded/bin}
- Chef::Log.debug("#{@new_resource} detected omnibus installation in #{RbConfig::CONFIG['bindir']}")
+ logger.trace("#{new_resource} detected omnibus installation in #{RbConfig::CONFIG['bindir']}")
# Omnibus installs to a static path because of linking on unix, find it.
true
elsif RbConfig::CONFIG["bindir"].sub(/^[\w]:/, "") == "/opscode/chef/embedded/bin"
- Chef::Log.debug("#{@new_resource} detected omnibus installation in #{RbConfig::CONFIG['bindir']}")
+ logger.trace("#{new_resource} detected omnibus installation in #{RbConfig::CONFIG['bindir']}")
# windows, with the drive letter removed
true
else
@@ -408,44 +423,40 @@ class Chef
end
def find_gem_by_path
- Chef::Log.debug("#{@new_resource} searching for 'gem' binary in path: #{ENV['PATH']}")
- separator = ::File::ALT_SEPARATOR ? ::File::ALT_SEPARATOR : ::File::SEPARATOR
- path_to_first_gem = ENV["PATH"].split(::File::PATH_SEPARATOR).find { |path| ::File.exists?(path + separator + "gem") }
- raise Chef::Exceptions::FileNotFound, "Unable to find 'gem' binary in path: #{ENV['PATH']}" if path_to_first_gem.nil?
- path_to_first_gem + separator + "gem"
+ which("gem", extra_path: RbConfig::CONFIG["bindir"])
end
def gem_dependency
- Gem::Dependency.new(@new_resource.package_name, @new_resource.version)
+ Gem::Dependency.new(new_resource.package_name, new_resource.version)
end
def source_is_remote?
- return true if @new_resource.source.nil?
- scheme = URI.parse(@new_resource.source).scheme
+ return true if new_resource.source.nil?
+ return true if new_resource.source.is_a?(Array)
+ scheme = URI.parse(new_resource.source).scheme
# URI.parse gets confused by MS Windows paths with forward slashes.
scheme = nil if scheme =~ /^[a-z]$/
%w{http https}.include?(scheme)
rescue URI::InvalidURIError
- Chef::Log.debug("#{@new_resource} failed to parse source '#{@new_resource.source}' as a URI, assuming a local path")
+ logger.trace("#{new_resource} failed to parse source '#{new_resource.source}' as a URI, assuming a local path")
false
end
def current_version
- #raise 'todo'
# If one or more matching versions are installed, the newest of them
# is the current version
if !matching_installed_versions.empty?
- gemspec = matching_installed_versions.last
- logger.debug { "#{@new_resource} found installed gem #{gemspec.name} version #{gemspec.version} matching #{gem_dependency}" }
+ gemspec = matching_installed_versions.max_by(&:version)
+ logger.trace { "#{new_resource} found installed gem #{gemspec.name} version #{gemspec.version} matching #{gem_dependency}" }
gemspec
# If no version matching the requirements exists, the latest installed
# version is the current version.
elsif !all_installed_versions.empty?
- gemspec = all_installed_versions.last
- logger.debug { "#{@new_resource} newest installed version of gem #{gemspec.name} is #{gemspec.version}" }
+ gemspec = all_installed_versions.max_by(&:version)
+ logger.trace { "#{new_resource} newest installed version of gem #{gemspec.name} is #{gemspec.version}" }
gemspec
else
- logger.debug { "#{@new_resource} no installed version found for #{gem_dependency}" }
+ logger.trace { "#{new_resource} no installed version found for #{gem_dependency}" }
nil
end
end
@@ -461,41 +472,39 @@ class Chef
end
def gem_sources
- @new_resource.source ? Array(@new_resource.source) : nil
+ srcs = [ new_resource.source ]
+ srcs << Chef::Config[:rubygems_url] if new_resource.include_default_source
+ srcs.flatten.compact
end
def load_current_resource
- @current_resource = Chef::Resource::Package::GemPackage.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
+ @current_resource = Chef::Resource::Package::GemPackage.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
if current_spec = current_version
- @current_resource.version(current_spec.version.to_s)
+ current_resource.version(current_spec.version.to_s)
end
- @current_resource
+ current_resource
end
def cleanup_after_converge
if @cleanup_gem_env
- logger.debug { "#{@new_resource} resetting gem environment to default" }
+ logger.trace { "#{new_resource} resetting gem environment to default" }
Gem.clear_paths
end
end
def candidate_version
@candidate_version ||= begin
- if target_version_already_installed?(@current_resource.version, @new_resource.version)
- nil
- elsif source_is_remote?
- @gem_env.candidate_version_from_remote(gem_dependency, *gem_sources).to_s
- else
- @gem_env.candidate_version_from_file(gem_dependency, @new_resource.source).to_s
- end
- end
+ if source_is_remote?
+ @gem_env.candidate_version_from_remote(gem_dependency, *gem_sources).to_s
+ else
+ @gem_env.candidate_version_from_file(gem_dependency, new_resource.source).to_s
+ end
+ end
end
- def target_version_already_installed?(current_version, new_version)
- return false unless current_version
- return false if new_version.nil?
-
+ def version_requirement_satisfied?(current_version, new_version)
+ return false unless current_version && new_version
Gem::Requirement.new(new_version).satisfied_by?(Gem::Version.new(current_version))
end
@@ -506,20 +515,18 @@ class Chef
# 2. shell out to `gem install` when a String of options is given
# 3. use gems API with options if a hash of options is given
def install_package(name, version)
- if source_is_remote? && @new_resource.gem_binary.nil?
- if @new_resource.options.nil?
- @gem_env.install(gem_dependency, :sources => gem_sources)
- elsif @new_resource.options.kind_of?(Hash)
- options = @new_resource.options
+ if source_is_remote? && new_resource.gem_binary.nil?
+ if new_resource.options.nil?
+ @gem_env.install(gem_dependency, sources: gem_sources)
+ elsif new_resource.options.is_a?(Hash)
+ options = new_resource.options
options[:sources] = gem_sources
@gem_env.install(gem_dependency, options)
else
install_via_gem_command(name, version)
end
- elsif @new_resource.gem_binary.nil?
- # domain is used by Gem::DependencyInstaller rather than by Chef code
- # domain can be :local, :remote or :both
- @gem_env.install(@new_resource.source, domain: :local)
+ elsif new_resource.gem_binary.nil?
+ @gem_env.install(new_resource.source)
else
install_via_gem_command(name, version)
end
@@ -527,23 +534,22 @@ class Chef
end
def gem_binary_path
- @new_resource.gem_binary || "gem"
+ new_resource.gem_binary || "gem"
end
def install_via_gem_command(name, version)
- if @new_resource.source =~ /\.gem$/i
- name = @new_resource.source
- src = " --local" unless source_is_remote?
- elsif @new_resource.clear_sources
- src = " --clear-sources"
- src << (@new_resource.source && " --source=#{@new_resource.source}" || "")
+ src = []
+ if new_resource.source.is_a?(String) && new_resource.source =~ /\.gem$/i
+ name = new_resource.source
else
- src = @new_resource.source && " --source=#{@new_resource.source} --source=#{Chef::Config[:rubygems_url]}"
+ src << "--clear-sources" if new_resource.clear_sources
+ src += gem_sources.map { |s| "--source=#{s}" }
end
- if !version.nil? && version.length > 0
- shell_out_with_timeout!("#{gem_binary_path} install #{name} -q --no-rdoc --no-ri -v \"#{version}\"#{src}#{opts}", :env => nil)
+ src_str = src.empty? ? "" : " #{src.join(" ")}"
+ if !version.nil? && !version.empty?
+ shell_out_with_timeout!("#{gem_binary_path} install #{name} -q --no-rdoc --no-ri -v \"#{version}\"#{src_str}#{opts}", env: nil)
else
- shell_out_with_timeout!("#{gem_binary_path} install \"#{name}\" -q --no-rdoc --no-ri #{src}#{opts}", :env => nil)
+ shell_out_with_timeout!("#{gem_binary_path} install \"#{name}\" -q --no-rdoc --no-ri #{src_str}#{opts}", env: nil)
end
end
@@ -552,11 +558,11 @@ class Chef
end
def remove_package(name, version)
- if @new_resource.gem_binary.nil?
- if @new_resource.options.nil?
+ if new_resource.gem_binary.nil?
+ if new_resource.options.nil?
@gem_env.uninstall(name, version)
- elsif @new_resource.options.kind_of?(Hash)
- @gem_env.uninstall(name, version, @new_resource.options)
+ elsif new_resource.options.is_a?(Hash)
+ @gem_env.uninstall(name, version, new_resource.options)
else
uninstall_via_gem_command(name, version)
end
@@ -567,9 +573,9 @@ class Chef
def uninstall_via_gem_command(name, version)
if version
- shell_out_with_timeout!("#{gem_binary_path} uninstall #{name} -q -x -I -v \"#{version}\"#{opts}", :env => nil)
+ shell_out_with_timeout!("#{gem_binary_path} uninstall #{name} -q -x -I -v \"#{version}\"#{opts}", env: nil)
else
- shell_out_with_timeout!("#{gem_binary_path} uninstall #{name} -q -x -I -a#{opts}", :env => nil)
+ shell_out_with_timeout!("#{gem_binary_path} uninstall #{name} -q -x -I -a#{opts}", env: nil)
end
end
@@ -580,7 +586,7 @@ class Chef
private
def opts
- expand_options(@new_resource.options)
+ expand_options(new_resource.options)
end
end
diff --git a/lib/chef/provider/package/smartos.rb b/lib/chef/provider/package/smartos.rb
index 3f09bef212..5c637814a6 100644
--- a/lib/chef/provider/package/smartos.rb
+++ b/lib/chef/provider/package/smartos.rb
@@ -3,7 +3,7 @@
# Bryan McLellan (btm@loftninjas.org)
# Matthew Landauer (matthew@openaustralia.org)
# Ben Rockwood (benr@joyent.com)
-# Copyright:: Copyright 2009-2016, Bryan McLellan, Matthew Landauer
+# Copyright:: Copyright 2009-2018, Bryan McLellan, Matthew Landauer
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,27 +30,27 @@ class Chef
attr_accessor :is_virtual_package
provides :package, platform: "smartos"
- provides :smartos_package, os: "solaris2", platform_family: "smartos"
+ provides :smartos_package
def load_current_resource
- Chef::Log.debug("#{@new_resource} loading current resource")
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
- check_package_state(@new_resource.package_name)
- @current_resource # modified by check_package_state
+ logger.trace("#{new_resource} loading current resource")
+ @current_resource = Chef::Resource::Package.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
+ check_package_state(new_resource.package_name)
+ current_resource # modified by check_package_state
end
def check_package_state(name)
- Chef::Log.debug("#{@new_resource} checking package #{name}")
+ logger.trace("#{new_resource} checking package #{name}")
version = nil
- info = shell_out_with_timeout!("/opt/local/sbin/pkg_info", "-E", "#{name}*", :env => nil, :returns => [0, 1])
+ info = shell_out_compact_timeout!("/opt/local/sbin/pkg_info", "-E", "#{name}*", env: nil, returns: [0, 1])
if info.stdout
- version = info.stdout[/^#{@new_resource.package_name}-(.+)/, 1]
+ version = info.stdout[/^#{new_resource.package_name}-(.+)/, 1]
end
if version
- @current_resource.version(version)
+ current_resource.version(version)
end
end
@@ -58,7 +58,7 @@ class Chef
return @candidate_version if @candidate_version
name = nil
version = nil
- pkg = shell_out_with_timeout!("/opt/local/bin/pkgin", "se", new_resource.package_name, :env => nil, :returns => [0, 1])
+ pkg = shell_out_compact_timeout!("/opt/local/bin/pkgin", "se", new_resource.package_name, env: nil, returns: [0, 1])
pkg.stdout.each_line do |line|
case line
when /^#{new_resource.package_name}/
@@ -70,20 +70,20 @@ class Chef
end
def install_package(name, version)
- Chef::Log.debug("#{@new_resource} installing package #{name} version #{version}")
+ logger.trace("#{new_resource} installing package #{name} version #{version}")
package = "#{name}-#{version}"
- out = shell_out_with_timeout!("/opt/local/bin/pkgin", "-y", "install", package, :env => nil)
+ out = shell_out_compact_timeout!("/opt/local/bin/pkgin", "-y", "install", package, env: nil)
end
def upgrade_package(name, version)
- Chef::Log.debug("#{@new_resource} upgrading package #{name} version #{version}")
+ logger.trace("#{new_resource} upgrading package #{name} version #{version}")
install_package(name, version)
end
def remove_package(name, version)
- Chef::Log.debug("#{@new_resource} removing package #{name} version #{version}")
- package = "#{name}"
- out = shell_out_with_timeout!("/opt/local/bin/pkgin", "-y", "remove", package, :env => nil)
+ logger.trace("#{new_resource} removing package #{name} version #{version}")
+ package = name.to_s
+ out = shell_out_compact_timeout!("/opt/local/bin/pkgin", "-y", "remove", package, env: nil)
end
end
diff --git a/lib/chef/provider/package/solaris.rb b/lib/chef/provider/package/solaris.rb
index 1c393e6a20..9c75c76929 100644
--- a/lib/chef/provider/package/solaris.rb
+++ b/lib/chef/provider/package/solaris.rb
@@ -16,7 +16,6 @@
# limitations under the License.
#
require "chef/provider/package"
-require "chef/mixin/command"
require "chef/resource/package"
require "chef/mixin/get_source_from_package"
@@ -29,49 +28,49 @@ class Chef
provides :package, platform: "nexentacore"
provides :package, platform: "solaris2", platform_version: "< 5.11"
- provides :solaris_package, os: "solaris2"
+ provides :solaris_package
# def initialize(*args)
# super
- # @current_resource = Chef::Resource::Package.new(@new_resource.name)
+ # @current_resource = Chef::Resource::Package.new(new_resource.name)
# end
def define_resource_requirements
super
requirements.assert(:install) do |a|
- a.assertion { @new_resource.source }
- a.failure_message Chef::Exceptions::Package, "Source for package #{@new_resource.name} required for action install"
+ a.assertion { new_resource.source }
+ a.failure_message Chef::Exceptions::Package, "Source for package #{new_resource.name} required for action install"
end
requirements.assert(:all_actions) do |a|
- a.assertion { !@new_resource.source || @package_source_found }
- a.failure_message Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}"
- a.whyrun "would assume #{@new_resource.source} would be have previously been made available"
+ a.assertion { !new_resource.source || @package_source_found }
+ a.failure_message Chef::Exceptions::Package, "Package #{new_resource.name} not found: #{new_resource.source}"
+ a.whyrun "would assume #{new_resource.source} would be have previously been made available"
end
end
def load_current_resource
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
+ @current_resource = Chef::Resource::Package.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
- if @new_resource.source
- @package_source_found = ::File.exists?(@new_resource.source)
+ if new_resource.source
+ @package_source_found = ::File.exist?(new_resource.source)
if @package_source_found
- Chef::Log.debug("#{@new_resource} checking pkg status")
- shell_out_with_timeout("pkginfo -l -d #{@new_resource.source} #{@new_resource.package_name}").stdout.each_line do |line|
+ logger.trace("#{new_resource} checking pkg status")
+ shell_out_compact_timeout("pkginfo", "-l", "-d", new_resource.source, new_resource.package_name).stdout.each_line do |line|
case line
when /VERSION:\s+(.+)/
- @new_resource.version($1)
+ new_resource.version($1)
end
end
end
end
- Chef::Log.debug("#{@new_resource} checking install state")
- status = shell_out_with_timeout("pkginfo -l #{@current_resource.package_name}")
+ logger.trace("#{new_resource} checking install state")
+ status = shell_out_compact_timeout("pkginfo", "-l", current_resource.package_name)
status.stdout.each_line do |line|
case line
when /VERSION:\s+(.+)/
- Chef::Log.debug("#{@new_resource} version #{$1} is already installed")
- @current_resource.version($1)
+ logger.trace("#{new_resource} version #{$1} is already installed")
+ current_resource.version($1)
end
end
@@ -79,56 +78,56 @@ class Chef
raise Chef::Exceptions::Package, "pkginfo failed - #{status.inspect}!"
end
- @current_resource
+ current_resource
end
def candidate_version
return @candidate_version if @candidate_version
- status = shell_out_with_timeout("pkginfo -l -d #{@new_resource.source} #{new_resource.package_name}")
+ status = shell_out_compact_timeout("pkginfo", "-l", "-d", new_resource.source, new_resource.package_name)
status.stdout.each_line do |line|
case line
when /VERSION:\s+(.+)/
@candidate_version = $1
- @new_resource.version($1)
- Chef::Log.debug("#{@new_resource} setting install candidate version to #{@candidate_version}")
+ new_resource.version($1)
+ logger.trace("#{new_resource} setting install candidate version to #{@candidate_version}")
end
end
unless status.exitstatus == 0
- raise Chef::Exceptions::Package, "pkginfo -l -d #{@new_resource.source} - #{status.inspect}!"
+ raise Chef::Exceptions::Package, "pkginfo -l -d #{new_resource.source} - #{status.inspect}!"
end
@candidate_version
end
def install_package(name, version)
- Chef::Log.debug("#{@new_resource} package install options: #{@new_resource.options}")
- if @new_resource.options.nil?
- if ::File.directory?(@new_resource.source) # CHEF-4469
- command = "pkgadd -n -d #{@new_resource.source} #{@new_resource.package_name}"
- else
- command = "pkgadd -n -d #{@new_resource.source} all"
- end
- shell_out_with_timeout!(command)
- Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}")
+ logger.trace("#{new_resource} package install options: #{options}")
+ if options.nil?
+ command = if ::File.directory?(new_resource.source) # CHEF-4469
+ [ "pkgadd", "-n", "-d", new_resource.source, new_resource.package_name ]
+ else
+ [ "pkgadd", "-n", "-d", new_resource.source, "all" ]
+ end
+ shell_out_compact_timeout!(command)
+ logger.trace("#{new_resource} installed version #{new_resource.version} from: #{new_resource.source}")
else
- if ::File.directory?(@new_resource.source) # CHEF-4469
- command = "pkgadd -n#{expand_options(@new_resource.options)} -d #{@new_resource.source} #{@new_resource.package_name}"
- else
- command = "pkgadd -n#{expand_options(@new_resource.options)} -d #{@new_resource.source} all"
- end
- shell_out_with_timeout!(command)
- Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}")
+ command = if ::File.directory?(new_resource.source) # CHEF-4469
+ [ "pkgadd", "-n", options, "-d", new_resource.source, new_resource.package_name ]
+ else
+ [ "pkgadd", "-n", options, "-d", new_resource.source, "all" ]
+ end
+ shell_out_compact_timeout!(*command)
+ logger.trace("#{new_resource} installed version #{new_resource.version} from: #{new_resource.source}")
end
end
- alias_method :upgrade_package, :install_package
+ alias upgrade_package install_package
def remove_package(name, version)
- if @new_resource.options.nil?
- shell_out_with_timeout!( "pkgrm -n #{name}" )
- Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}")
+ if options.nil?
+ shell_out_compact_timeout!( "pkgrm", "-n", name )
+ logger.trace("#{new_resource} removed version #{new_resource.version}")
else
- shell_out_with_timeout!( "pkgrm -n#{expand_options(@new_resource.options)} #{name}" )
- Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}")
+ shell_out_compact_timeout!( "pkgrm", "-n", options, name )
+ logger.trace("#{new_resource} removed version #{new_resource.version}")
end
end
diff --git a/lib/chef/provider/package/windows.rb b/lib/chef/provider/package/windows.rb
index a5f3ff7191..0fea32dcf3 100644
--- a/lib/chef/provider/package/windows.rb
+++ b/lib/chef/provider/package/windows.rb
@@ -30,7 +30,7 @@ class Chef
include Chef::Mixin::Checksum
provides :package, os: "windows"
- provides :windows_package, os: "windows"
+ provides :windows_package
require "chef/provider/package/windows/registry_uninstall_entry.rb"
@@ -45,7 +45,7 @@ class Chef
def load_current_resource
@current_resource = Chef::Resource::WindowsPackage.new(new_resource.name)
if downloadable_file_missing?
- Chef::Log.debug("We do not know the version of #{new_resource.source} because the file is not downloaded")
+ logger.trace("We do not know the version of #{new_resource.source} because the file is not downloaded")
current_resource.version(:unknown.to_s)
else
current_resource.version(package_provider.installed_version)
@@ -59,11 +59,11 @@ class Chef
@package_provider ||= begin
case installer_type
when :msi
- Chef::Log.debug("#{new_resource} is MSI")
+ logger.trace("#{new_resource} is MSI")
require "chef/provider/package/windows/msi"
Chef::Provider::Package::Windows::MSI.new(resource_for_provider, uninstall_registry_entries)
else
- Chef::Log.debug("#{new_resource} is EXE with type '#{installer_type}'")
+ logger.trace("#{new_resource} is EXE with type '#{installer_type}'")
require "chef/provider/package/windows/exe"
Chef::Provider::Package::Windows::Exe.new(resource_for_provider, installer_type, uninstall_registry_entries)
end
@@ -104,8 +104,8 @@ class Chef
return :nsis
end
- if io.tell() < filesize
- io.seek(io.tell() - overlap)
+ if io.tell < filesize
+ io.seek(io.tell - overlap)
end
end
@@ -165,7 +165,11 @@ class Chef
#
# @return [Boolean] true if new_version is equal to or included in current_version
def target_version_already_installed?(current_version, new_version)
- Chef::Log.debug("Checking if #{new_resource} version '#{new_version}' is already installed. #{current_version} is currently installed")
+ version_equals?(current_version, new_version)
+ end
+
+ def version_equals?(current_version, new_version)
+ logger.trace("Checking if #{new_resource} version '#{new_version}' is already installed. #{current_version} is currently installed")
if current_version.is_a?(Array)
current_version.include?(new_version)
else
@@ -179,6 +183,17 @@ class Chef
private
+ def version_compare(v1, v2)
+ if v1 == "latest" || v2 == "latest"
+ return 0
+ end
+
+ gem_v1 = Gem::Version.new(v1)
+ gem_v2 = Gem::Version.new(v2)
+
+ gem_v1 <=> gem_v2
+ end
+
def uninstall_registry_entries
@uninstall_registry_entries ||= Chef::Provider::Package::Windows::RegistryUninstallEntry.find_entries(new_resource.package_name)
end
@@ -195,12 +210,13 @@ class Chef
end
def downloadable_file_missing?
- !new_resource.source.nil? && uri_scheme?(new_resource.source) && !::File.exists?(source_location)
+ !new_resource.source.nil? && uri_scheme?(new_resource.source) && !::File.exist?(source_location)
end
def resource_for_provider
@resource_for_provider = Chef::Resource::WindowsPackage.new(new_resource.name).tap do |r|
r.source(Chef::Util::PathHelper.validate_path(source_location)) unless source_location.nil?
+ r.cookbook_name = new_resource.cookbook_name
r.version(new_resource.version)
r.timeout(new_resource.timeout)
r.returns(new_resource.returns)
@@ -210,12 +226,13 @@ class Chef
def download_source_file
source_resource.run_action(:create)
- Chef::Log.debug("#{new_resource} fetched source file to #{source_resource.path}")
+ logger.trace("#{new_resource} fetched source file to #{source_resource.path}")
end
def source_resource
@source_resource ||= Chef::Resource::RemoteFile.new(default_download_cache_path, run_context).tap do |r|
r.source(new_resource.source)
+ r.cookbook_name = new_resource.cookbook_name
r.checksum(new_resource.checksum)
r.backup(false)
@@ -248,7 +265,7 @@ class Chef
def validate_content!
if new_resource.checksum
source_checksum = checksum(source_location)
- if new_resource.checksum != source_checksum
+ if new_resource.checksum.downcase != source_checksum
raise Chef::Exceptions::ChecksumMismatch.new(short_cksum(new_resource.checksum), short_cksum(source_checksum))
end
end
@@ -260,7 +277,7 @@ class Chef
if source_location.nil?
inferred_registry_type == :msi
else
- ::File.extname(source_location).casecmp(".msi").zero?
+ ::File.extname(source_location).casecmp(".msi") == 0
end
end
end
diff --git a/lib/chef/provider/package/windows/exe.rb b/lib/chef/provider/package/windows/exe.rb
index 70c9879845..6499d0cfeb 100644
--- a/lib/chef/provider/package/windows/exe.rb
+++ b/lib/chef/provider/package/windows/exe.rb
@@ -28,11 +28,13 @@ class Chef
def initialize(resource, installer_type, uninstall_entries)
@new_resource = resource
+ @logger = new_resource.logger
@installer_type = installer_type
@uninstall_entries = uninstall_entries
end
attr_reader :new_resource
+ attr_reader :logger
attr_reader :installer_type
attr_reader :uninstall_entries
@@ -43,7 +45,7 @@ class Chef
# Returns a version if the package is installed or nil if it is not.
def installed_version
- Chef::Log.debug("#{new_resource} checking package version")
+ logger.trace("#{new_resource} checking package version")
current_installed_version
end
@@ -52,7 +54,7 @@ class Chef
end
def install_package
- Chef::Log.debug("#{new_resource} installing #{new_resource.installer_type} package '#{new_resource.source}'")
+ logger.trace("#{new_resource} installing #{new_resource.installer_type} package '#{new_resource.source}'")
shell_out!(
[
"start",
@@ -69,32 +71,30 @@ class Chef
def remove_package
uninstall_version = new_resource.version || current_installed_version
uninstall_entries.select { |entry| [uninstall_version].flatten.include?(entry.display_version) }
- .map { |version| version.uninstall_string }.uniq.each do |uninstall_string|
- Chef::Log.debug("Registry provided uninstall string for #{new_resource} is '#{uninstall_string}'")
- shell_out!(uninstall_command(uninstall_string), { returns: new_resource.returns })
- end
+ .map(&:uninstall_string).uniq.each do |uninstall_string|
+ logger.trace("Registry provided uninstall string for #{new_resource} is '#{uninstall_string}'")
+ shell_out!(uninstall_command(uninstall_string), timeout: new_resource.timeout, returns: new_resource.returns)
+ end
end
private
def uninstall_command(uninstall_string)
- uninstall_string.delete!('"')
+ uninstall_string = "\"#{uninstall_string}\"" if ::File.exist?(uninstall_string)
uninstall_string = [
- %q{/d"},
- ::File.dirname(uninstall_string),
- %q{" },
- ::File.basename(uninstall_string),
+ uninstall_string,
expand_options(new_resource.options),
" ",
unattended_flags,
].join
- %Q{start "" /wait #{uninstall_string} & exit %%%%ERRORLEVEL%%%%}
+ %{start "" /wait #{uninstall_string} & exit %%%%ERRORLEVEL%%%%}
end
def current_installed_version
- @current_installed_version ||= uninstall_entries.count == 0 ? nil : begin
- uninstall_entries.map { |entry| entry.display_version }.uniq
- end
+ @current_installed_version ||=
+ if uninstall_entries.count != 0
+ uninstall_entries.map(&:display_version).uniq
+ end
end
# http://unattended.sourceforge.net/installers.php
diff --git a/lib/chef/provider/package/windows/msi.rb b/lib/chef/provider/package/windows/msi.rb
index ac771688e7..51afcab2a2 100644
--- a/lib/chef/provider/package/windows/msi.rb
+++ b/lib/chef/provider/package/windows/msi.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,7 @@
# limitations under the License.
#
-# TODO: Allow @new_resource.source to be a Product Code as a GUID for uninstall / network install
+# TODO: Allow new_resource.source to be a Product Code as a GUID for uninstall / network install
require "chef/win32/api/installer" if (RUBY_PLATFORM =~ /mswin|mingw32|windows/) && Chef::Platform.supports_msi?
require "chef/mixin/shell_out"
@@ -31,10 +31,12 @@ class Chef
def initialize(resource, uninstall_entries)
@new_resource = resource
+ @logger = new_resource.logger
@uninstall_entries = uninstall_entries
end
attr_reader :new_resource
+ attr_reader :logger
attr_reader :uninstall_entries
# From Chef::Provider::Package
@@ -45,13 +47,13 @@ class Chef
# Returns a version if the package is installed or nil if it is not.
def installed_version
if !new_resource.source.nil? && ::File.exist?(new_resource.source)
- Chef::Log.debug("#{new_resource} getting product code for package at #{new_resource.source}")
+ logger.trace("#{new_resource} getting product code for package at #{new_resource.source}")
product_code = get_product_property(new_resource.source, "ProductCode")
- Chef::Log.debug("#{new_resource} checking package status and version for #{product_code}")
+ logger.trace("#{new_resource} checking package status and version for #{product_code}")
get_installed_version(product_code)
else
- uninstall_entries.count == 0 ? nil : begin
- uninstall_entries.map { |entry| entry.display_version }.uniq
+ if uninstall_entries.count != 0
+ uninstall_entries.map(&:display_version).uniq
end
end
end
@@ -59,30 +61,31 @@ class Chef
def package_version
return new_resource.version if new_resource.version
if !new_resource.source.nil? && ::File.exist?(new_resource.source)
- Chef::Log.debug("#{new_resource} getting product version for package at #{new_resource.source}")
+ logger.trace("#{new_resource} getting product version for package at #{new_resource.source}")
get_product_property(new_resource.source, "ProductVersion")
end
end
def install_package
# We could use MsiConfigureProduct here, but we'll start off with msiexec
- Chef::Log.debug("#{new_resource} installing MSI package '#{new_resource.source}'")
- shell_out!("msiexec /qn /i \"#{new_resource.source}\" #{expand_options(new_resource.options)}", { :timeout => new_resource.timeout, :returns => new_resource.returns })
+ logger.trace("#{new_resource} installing MSI package '#{new_resource.source}'")
+ shell_out!("msiexec /qn /i \"#{new_resource.source}\" #{expand_options(new_resource.options)}", timeout: new_resource.timeout, returns: new_resource.returns)
end
def remove_package
# We could use MsiConfigureProduct here, but we'll start off with msiexec
if !new_resource.source.nil? && ::File.exist?(new_resource.source)
- Chef::Log.debug("#{new_resource} removing MSI package '#{new_resource.source}'")
- shell_out!("msiexec /qn /x \"#{new_resource.source}\" #{expand_options(new_resource.options)}", { :timeout => new_resource.timeout, :returns => new_resource.returns })
+ logger.trace("#{new_resource} removing MSI package '#{new_resource.source}'")
+ shell_out!("msiexec /qn /x \"#{new_resource.source}\" #{expand_options(new_resource.options)}", timeout: new_resource.timeout, returns: new_resource.returns)
else
uninstall_version = new_resource.version || installed_version
uninstall_entries.select { |entry| [uninstall_version].flatten.include?(entry.display_version) }
- .map { |version| version.uninstall_string }.uniq.each do |uninstall_string|
- Chef::Log.debug("#{new_resource} removing MSI package version using '#{uninstall_string}'")
+ .map(&:uninstall_string).uniq.each do |uninstall_string|
+ uninstall_string = "msiexec /x #{uninstall_string.match(/{.*}/)}"
uninstall_string += expand_options(new_resource.options)
- uninstall_string += " /Q" unless uninstall_string =~ / \/Q\b/
- shell_out!(uninstall_string, { :timeout => new_resource.timeout, :returns => new_resource.returns })
+ uninstall_string += " /q" unless uninstall_string.downcase =~ / \/q/
+ logger.trace("#{new_resource} removing MSI package version using '#{uninstall_string}'")
+ shell_out!(uninstall_string, timeout: new_resource.timeout, returns: new_resource.returns)
end
end
end
diff --git a/lib/chef/provider/package/windows/registry_uninstall_entry.rb b/lib/chef/provider/package/windows/registry_uninstall_entry.rb
index 3fa00b6081..d57f700799 100644
--- a/lib/chef/provider/package/windows/registry_uninstall_entry.rb
+++ b/lib/chef/provider/package/windows/registry_uninstall_entry.rb
@@ -26,7 +26,7 @@ class Chef
class RegistryUninstallEntry
def self.find_entries(package_name)
- Chef::Log.debug("Finding uninstall entries for #{package_name}")
+ logger.trace("Finding uninstall entries for #{package_name}")
entries = []
[
[::Win32::Registry::HKEY_LOCAL_MACHINE, (::Win32::Registry::Constants::KEY_READ | 0x0100)],
@@ -40,36 +40,47 @@ class Chef
begin
entry = reg.open(key, desired)
display_name = read_registry_property(entry, "DisplayName")
- if display_name == package_name
- entries.push(RegistryUninstallEntry.new(hkey, key, entry))
+ if display_name.to_s.rstrip == package_name
+ quiet_uninstall_string = RegistryUninstallEntry.read_registry_property(entry, "QuietUninstallString")
+ entries.push(quiet_uninstall_string_key?(quiet_uninstall_string, hkey, key, entry))
end
rescue ::Win32::Registry::Error => ex
- Chef::Log.debug("Registry error opening key '#{key}' on node #{desired}: #{ex}")
+ logger.trace("Registry error opening key '#{key}' on node #{desired}: #{ex}")
end
end
end
rescue ::Win32::Registry::Error => ex
- Chef::Log.debug("Registry error opening hive '#{hkey[0]}' :: #{desired}: #{ex}")
+ logger.trace("Registry error opening hive '#{hkey[0]}' :: #{desired}: #{ex}")
end
end
entries
end
+ def self.quiet_uninstall_string_key?(quiet_uninstall_string, hkey, key, entry)
+ return RegistryUninstallEntry.new(hkey, key, entry) if quiet_uninstall_string.nil?
+ RegistryUninstallEntry.new(hkey, key, entry, "QuietUninstallString")
+ end
+
def self.read_registry_property(data, property)
data[property]
- rescue ::Win32::Registry::Error => ex
- Chef::Log.debug("Failure to read property '#{property}'")
+ rescue ::Win32::Registry::Error
+ logger.trace("Failure to read property '#{property}'")
nil
end
- def initialize(hive, key, registry_data)
- Chef::Log.debug("Creating uninstall entry for #{hive}::#{key}")
+ def self.logger
+ Chef::Log
+ end
+
+ def initialize(hive, key, registry_data, uninstall_key = "UninstallString")
+ @logger = Chef::Log.with_child({ subsystem: "registry_uninstall_entry" })
+ logger.trace("Creating uninstall entry for #{hive}::#{key}")
@hive = hive
@key = key
@data = registry_data
@display_name = RegistryUninstallEntry.read_registry_property(registry_data, "DisplayName")
@display_version = RegistryUninstallEntry.read_registry_property(registry_data, "DisplayVersion")
- @uninstall_string = RegistryUninstallEntry.read_registry_property(registry_data, "UninstallString")
+ @uninstall_string = RegistryUninstallEntry.read_registry_property(registry_data, uninstall_key)
end
attr_reader :hive
@@ -78,8 +89,7 @@ class Chef
attr_reader :display_version
attr_reader :uninstall_string
attr_reader :data
-
- private
+ attr_reader :logger
UNINSTALL_SUBKEY = 'Software\Microsoft\Windows\CurrentVersion\Uninstall'.freeze
end
diff --git a/lib/chef/provider/package/yum-dump.py b/lib/chef/provider/package/yum-dump.py
deleted file mode 100644
index 6183460195..0000000000
--- a/lib/chef/provider/package/yum-dump.py
+++ /dev/null
@@ -1,307 +0,0 @@
-#
-# Author:: Matthew Kent (<mkent@magoazul.com>)
-# Copyright:: Copyright 2009-2016, Matthew Kent
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# yum-dump.py
-# Inspired by yumhelper.py by David Lutterkort
-#
-# Produce a list of installed, available and re-installable packages using yum
-# and dump the results to stdout.
-#
-# yum-dump invokes yum similarly to the command line interface which makes it
-# subject to most of the configuration parameters in yum.conf. yum-dump will
-# also load yum plugins in the same manor as yum - these can affect the output.
-#
-# Can be run as non root, but that won't update the cache.
-#
-# Intended to support yum 2.x and 3.x
-
-import os
-import sys
-import time
-import yum
-import re
-import errno
-
-from yum import Errors
-from optparse import OptionParser
-from distutils import version
-
-YUM_PID_FILE='/var/run/yum.pid'
-
-YUM_VER = version.StrictVersion(yum.__version__)
-YUM_MAJOR = YUM_VER.version[0]
-
-if YUM_MAJOR > 3 or YUM_MAJOR < 2:
- print >> sys.stderr, "yum-dump Error: Can't match supported yum version" \
- " (%s)" % yum.__version__
- sys.exit(1)
-
-# Required for Provides output
-if YUM_MAJOR == 2:
- import rpm
- import rpmUtils.miscutils
-
-def setup(yb, options):
- # Only want our output
- #
- if YUM_MAJOR == 3:
- try:
- if YUM_VER >= version.StrictVersion("3.2.22"):
- yb.preconf.errorlevel=0
- yb.preconf.debuglevel=0
-
- # initialize the config
- yb.conf
- else:
- yb.doConfigSetup(errorlevel=0, debuglevel=0)
- except yum.Errors.ConfigError, e:
- # suppresses an ignored exception at exit
- yb.preconf = None
- print >> sys.stderr, "yum-dump Config Error: %s" % e
- return 1
- except ValueError, e:
- yb.preconf = None
- print >> sys.stderr, "yum-dump Options Error: %s" % e
- return 1
- elif YUM_MAJOR == 2:
- yb.doConfigSetup()
-
- def __log(a,b): pass
-
- yb.log = __log
- yb.errorlog = __log
-
- # Give Chef every possible package version, it can decide what to do with them
- if YUM_MAJOR == 3:
- yb.conf.showdupesfromrepos = True
- elif YUM_MAJOR == 2:
- yb.conf.setConfigOption('showdupesfromrepos', True)
-
- # Optionally run only on cached repositories, but non root must use the cache
- if os.geteuid() != 0:
- if YUM_MAJOR == 3:
- yb.conf.cache = True
- elif YUM_MAJOR == 2:
- yb.conf.setConfigOption('cache', True)
- else:
- if YUM_MAJOR == 3:
- yb.conf.cache = options.cache
- elif YUM_MAJOR == 2:
- yb.conf.setConfigOption('cache', options.cache)
-
- # Handle repo toggle via id or glob exactly like yum
- for opt, repos in options.repo_control:
- for repo in repos:
- if opt == '--enablerepo':
- yb.repos.enableRepo(repo)
- elif opt == '--disablerepo':
- yb.repos.disableRepo(repo)
-
- return 0
-
-def dump_packages(yb, list, output_provides):
- packages = {}
-
- if YUM_MAJOR == 2:
- yb.doTsSetup()
- yb.doRepoSetup()
- yb.doSackSetup()
-
- db = yb.doPackageLists(list)
-
- for pkg in db.installed:
- pkg.type = 'i'
- packages[str(pkg)] = pkg
-
- if YUM_VER >= version.StrictVersion("3.2.21"):
- for pkg in db.available:
- pkg.type = 'a'
- packages[str(pkg)] = pkg
-
- # These are both installed and available
- for pkg in db.reinstall_available:
- pkg.type = 'r'
- packages[str(pkg)] = pkg
- else:
- # Old style method - no reinstall list
- for pkg in yb.pkgSack.returnPackages():
-
- if str(pkg) in packages:
- if packages[str(pkg)].type == "i":
- packages[str(pkg)].type = 'r'
- continue
-
- pkg.type = 'a'
- packages[str(pkg)] = pkg
-
- unique_packages = packages.values()
-
- unique_packages.sort(lambda x, y: cmp(x.name, y.name))
-
- for pkg in unique_packages:
- if output_provides == "all" or \
- (output_provides == "installed" and (pkg.type == "i" or pkg.type == "r")):
-
- # yum 2 doesn't have provides_print, implement it ourselves using methods
- # based on requires gathering in packages.py
- if YUM_MAJOR == 2:
- provlist = []
-
- # Installed and available are gathered in different ways
- if pkg.type == 'i' or pkg.type == 'r':
- names = pkg.hdr[rpm.RPMTAG_PROVIDENAME]
- flags = pkg.hdr[rpm.RPMTAG_PROVIDEFLAGS]
- ver = pkg.hdr[rpm.RPMTAG_PROVIDEVERSION]
- if names is not None:
- tmplst = zip(names, flags, ver)
-
- for (n, f, v) in tmplst:
- prov = rpmUtils.miscutils.formatRequire(n, v, f)
- provlist.append(prov)
- # This is slow :(
- elif pkg.type == 'a':
- for prcoTuple in pkg.returnPrco('provides'):
- prcostr = pkg.prcoPrintable(prcoTuple)
- provlist.append(prcostr)
-
- provides = provlist
- else:
- provides = pkg.provides_print
- else:
- provides = "[]"
-
- print '%s %s %s %s %s %s %s %s' % (
- pkg.name,
- pkg.epoch,
- pkg.version,
- pkg.release,
- pkg.arch,
- provides,
- pkg.type,
- pkg.repoid )
-
- return 0
-
-def yum_dump(options):
- lock_obtained = False
-
- yb = yum.YumBase()
-
- status = setup(yb, options)
- if status != 0:
- return status
-
- if options.output_options:
- print "[option installonlypkgs] %s" % " ".join(yb.conf.installonlypkgs)
-
- # Non root can't handle locking on rhel/centos 4
- if os.geteuid() != 0:
- return dump_packages(yb, options.package_list, options.output_provides)
-
- # Wrap the collection and output of packages in yum's global lock to prevent
- # any inconsistencies.
- try:
- # Spin up to --yum-lock-timeout option
- countdown = options.yum_lock_timeout
- while True:
- try:
- yb.doLock(YUM_PID_FILE)
- lock_obtained = True
- except Errors.LockError, e:
- time.sleep(1)
- countdown -= 1
- if countdown == 0:
- print >> sys.stderr, "yum-dump Locking Error! Couldn't obtain an " \
- "exclusive yum lock in %d seconds. Giving up." % options.yum_lock_timeout
- return 200
- else:
- break
-
- return dump_packages(yb, options.package_list, options.output_provides)
-
- # Ensure we clear the lock and cleanup any resources
- finally:
- try:
- yb.closeRpmDB()
- if lock_obtained == True:
- yb.doUnlock(YUM_PID_FILE)
- except Errors.LockError, e:
- print >> sys.stderr, "yum-dump Unlock Error: %s" % e
- return 200
-
-# Preserve order of enable/disable repo args like yum does
-def gather_repo_opts(option, opt, value, parser):
- if getattr(parser.values, option.dest, None) is None:
- setattr(parser.values, option.dest, [])
- getattr(parser.values, option.dest).append((opt, value.split(',')))
-
-def main():
- usage = "Usage: %prog [options]\n" + \
- "Output a list of installed, available and re-installable packages via yum"
- parser = OptionParser(usage=usage)
- parser.add_option("-C", "--cache",
- action="store_true", dest="cache", default=False,
- help="run entirely from cache, don't update cache")
- parser.add_option("-o", "--options",
- action="store_true", dest="output_options", default=False,
- help="output select yum options useful to Chef")
- parser.add_option("-p", "--installed-provides",
- action="store_const", const="installed", dest="output_provides", default="none",
- help="output Provides for installed packages, big/wide output")
- parser.add_option("-P", "--all-provides",
- action="store_const", const="all", dest="output_provides", default="none",
- help="output Provides for all package, slow, big/wide output")
- parser.add_option("-i", "--installed",
- action="store_const", const="installed", dest="package_list", default="all",
- help="output only installed packages")
- parser.add_option("-a", "--available",
- action="store_const", const="available", dest="package_list", default="all",
- help="output only available and re-installable packages")
- parser.add_option("--enablerepo",
- action="callback", callback=gather_repo_opts, type="string", dest="repo_control", default=[],
- help="enable disabled repositories by id or glob")
- parser.add_option("--disablerepo",
- action="callback", callback=gather_repo_opts, type="string", dest="repo_control", default=[],
- help="disable repositories by id or glob")
- parser.add_option("--yum-lock-timeout",
- action="store", type="int", dest="yum_lock_timeout", default=30,
- help="Time in seconds to wait for yum process lock")
-
- (options, args) = parser.parse_args()
-
- try:
- return yum_dump(options)
-
- except yum.Errors.RepoError, e:
- print >> sys.stderr, "yum-dump Repository Error: %s" % e
- return 1
-
- except yum.Errors.YumBaseError, e:
- print >> sys.stderr, "yum-dump General Error: %s" % e
- return 1
-
-try:
- status = main()
-# Suppress a nasty broken pipe error when output is piped to utilities like 'head'
-except IOError, e:
- if e.errno == errno.EPIPE:
- sys.exit(1)
- else:
- raise
-
-sys.exit(status)
diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb
index dfd32fde55..805a74d013 100644
--- a/lib/chef/provider/package/yum.rb
+++ b/lib/chef/provider/package/yum.rb
@@ -1,6 +1,5 @@
-
-# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+#
+# Copyright:: Copyright 2016-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,1402 +15,271 @@
# limitations under the License.
#
-require "chef/config"
require "chef/provider/package"
+require "chef/resource/yum_package"
require "chef/mixin/which"
-require "chef/resource/package"
-require "singleton"
+require "chef/mixin/shell_out"
require "chef/mixin/get_source_from_package"
+require "chef/provider/package/yum/python_helper"
+require "chef/provider/package/yum/version"
+# the stubs in the YumCache class are still an external API
+require "chef/provider/package/yum/yum_cache"
class Chef
class Provider
class Package
class Yum < Chef::Provider::Package
+ extend Chef::Mixin::Which
+ extend Chef::Mixin::ShellOut
+ include Chef::Mixin::GetSourceFromPackage
- provides :package, platform_family: %w{rhel fedora}
- provides :yum_package, os: "linux"
-
- class RPMUtils
- class << self
-
- # RPM::Version version_parse equivalent
- def version_parse(evr)
- return if evr.nil?
-
- epoch = nil
- # assume this is a version
- version = evr
- release = nil
-
- lead = 0
- tail = evr.size
-
- if evr =~ %r{^([\d]+):}
- epoch = $1.to_i
- lead = $1.length + 1
- elsif evr[0].ord == ":".ord
- epoch = 0
- lead = 1
- end
-
- if evr =~ %r{:?.*-(.*)$}
- release = $1
- tail = evr.length - release.length - lead - 1
-
- if release.empty?
- release = nil
- end
- end
-
- version = evr[lead, tail]
- if version.empty?
- version = nil
- end
-
- [ epoch, version, release ]
- end
-
- # verify
- def isalnum(x)
- isalpha(x) || isdigit(x)
- end
-
- def isalpha(x)
- v = x.ord
- (v >= 65 && v <= 90) || (v >= 97 && v <= 122)
- end
-
- def isdigit(x)
- v = x.ord
- v >= 48 && v <= 57
- end
-
- # based on the reference spec in lib/rpmvercmp.c in rpm 4.9.0
- def rpmvercmp(x, y)
- # easy! :)
- return 0 if x == y
-
- if x.nil?
- x = ""
- end
-
- if y.nil?
- y = ""
- end
-
- # not so easy :(
- #
- # takes 2 strings like
- #
- # x = "1.20.b18.el5"
- # y = "1.20.b17.el5"
- #
- # breaks into purely alpha and numeric segments and compares them using
- # some rules
- #
- # * 10 > 1
- # * 1 > a
- # * z > a
- # * Z > A
- # * z > Z
- # * leading zeros are ignored
- # * separators (periods, commas) are ignored
- # * "1.20.b18.el5.extrastuff" > "1.20.b18.el5"
-
- x_pos = 0 # overall string element reference position
- x_pos_max = x.length - 1 # number of elements in string, starting from 0
- x_seg_pos = 0 # segment string element reference position
- x_comp = nil # segment to compare
-
- y_pos = 0
- y_seg_pos = 0
- y_pos_max = y.length - 1
- y_comp = nil
-
- while x_pos <= x_pos_max && y_pos <= y_pos_max
- # first we skip over anything non alphanumeric
- while (x_pos <= x_pos_max) && (isalnum(x[x_pos]) == false)
- x_pos += 1 # +1 over pos_max if end of string
- end
- while (y_pos <= y_pos_max) && (isalnum(y[y_pos]) == false)
- y_pos += 1
- end
-
- # if we hit the end of either we are done matching segments
- if (x_pos == x_pos_max + 1) || (y_pos == y_pos_max + 1)
- break
- end
-
- # we are now at the start of a alpha or numeric segment
- x_seg_pos = x_pos
- y_seg_pos = y_pos
-
- # grab segment so we can compare them
- if isdigit(x[x_seg_pos].ord)
- x_seg_is_num = true
-
- # already know it's a digit
- x_seg_pos += 1
-
- # gather up our digits
- while (x_seg_pos <= x_pos_max) && isdigit(x[x_seg_pos])
- x_seg_pos += 1
- end
- # copy the segment but not the unmatched character that x_seg_pos will
- # refer to
- x_comp = x[x_pos, x_seg_pos - x_pos]
-
- while (y_seg_pos <= y_pos_max) && isdigit(y[y_seg_pos])
- y_seg_pos += 1
- end
- y_comp = y[y_pos, y_seg_pos - y_pos]
- else
- # we are comparing strings
- x_seg_is_num = false
-
- while (x_seg_pos <= x_pos_max) && isalpha(x[x_seg_pos])
- x_seg_pos += 1
- end
- x_comp = x[x_pos, x_seg_pos - x_pos]
-
- while (y_seg_pos <= y_pos_max) && isalpha(y[y_seg_pos])
- y_seg_pos += 1
- end
- y_comp = y[y_pos, y_seg_pos - y_pos]
- end
-
- # if y_seg_pos didn't advance in the above loop it means the segments are
- # different types
- if y_pos == y_seg_pos
- # numbers always win over letters
- return x_seg_is_num ? 1 : -1
- end
-
- # move the ball forward before we mess with the segments
- x_pos += x_comp.length # +1 over pos_max if end of string
- y_pos += y_comp.length
-
- # we are comparing numbers - simply convert them
- if x_seg_is_num
- x_comp = x_comp.to_i
- y_comp = y_comp.to_i
- end
-
- # compares ints or strings
- # don't return if equal - try the next segment
- if x_comp > y_comp
- return 1
- elsif x_comp < y_comp
- return -1
- end
-
- # if we've reached here than the segments are the same - try again
- end
-
- # we must have reached the end of one or both of the strings and they
- # matched up until this point
-
- # segments matched completely but the segment separators were different -
- # rpm reference code treats these as equal.
- if (x_pos == x_pos_max + 1) && (y_pos == y_pos_max + 1)
- return 0
- end
-
- # the most unprocessed characters left wins
- if (x_pos_max - x_pos) > (y_pos_max - y_pos)
- return 1
- else
- return -1
- end
- end
-
- end # self
- end # RPMUtils
-
- class RPMVersion
- include Comparable
+ allow_nils
+ use_multipackage_api
+ use_package_name_for_source
- def initialize(*args)
- if args.size == 1
- @e, @v, @r = RPMUtils.version_parse(args[0])
- elsif args.size == 3
- @e = args[0].to_i
- @v = args[1]
- @r = args[2]
- else
- raise ArgumentError, "Expecting either 'epoch-version-release' or 'epoch, " +
- "version, release'"
- end
- end
- attr_reader :e, :v, :r
- alias :epoch :e
- alias :version :v
- alias :release :r
+ provides :package, platform_family: %w{fedora amazon rhel}
- def self.parse(*args)
- self.new(*args)
- end
+ provides :yum_package
- def <=>(y)
- compare_versions(y)
- end
+ #
+ # Most of the magic in this class happens in the python helper script. The ruby side of this
+ # provider knows only enough to translate Chef-style new_resource name+package+version into
+ # a request to the python side. The python side is then responsible for knowing everything
+ # about RPMs and what is installed and what is available. The ruby side of this class should
+ # remain a lightweight translation layer to translate Chef requests into RPC requests to
+ # python. This class knows nothing about how to compare RPM versions, and does not maintain
+ # any cached state of installed/available versions and should be kept that way.
+ #
+ def python_helper
+ @python_helper ||= PythonHelper.instance
+ end
- def compare(y)
- compare_versions(y, false)
- end
+ def load_current_resource
+ flushcache if new_resource.flush_cache[:before]
- def partial_compare(y)
- compare_versions(y, true)
- end
+ @current_resource = Chef::Resource::YumPackage.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
+ current_resource.version(get_current_versions)
- # RPM::Version rpm_version_to_s equivalent
- def to_s
- if @r.nil?
- @v
- else
- "#{@v}-#{@r}"
- end
- end
+ current_resource
+ end
- def evr
- "#{@e}:#{@v}-#{@r}"
+ def define_resource_requirements
+ requirements.assert(:install, :upgrade, :remove, :purge) do |a|
+ a.assertion { !new_resource.source || ::File.exist?(new_resource.source) }
+ a.failure_message Chef::Exceptions::Package, "Package #{new_resource.package_name} not found: #{new_resource.source}"
+ a.whyrun "assuming #{new_resource.source} would have previously been created"
end
- private
-
- # Rough RPM::Version rpm_version_cmp equivalent - except much slower :)
- #
- # partial lets epoch and version segment equality be good enough to return equal, eg:
- #
- # 2:1.2-1 == 2:1.2
- # 2:1.2-1 == 2:
- #
- def compare_versions(y, partial = false)
- x = self
-
- # compare epoch
- if (x.e.nil? == false && x.e > 0) && y.e.nil?
- return 1
- elsif x.e.nil? && (y.e.nil? == false && y.e > 0)
- return -1
- elsif x.e.nil? == false && y.e.nil? == false
- if x.e < y.e
- return -1
- elsif x.e > y.e
- return 1
- end
- end
-
- # compare version
- if partial && (x.v.nil? || y.v.nil?)
- return 0
- elsif x.v.nil? == false && y.v.nil?
- return 1
- elsif x.v.nil? && y.v.nil? == false
- return -1
- elsif x.v.nil? == false && y.v.nil? == false
- cmp = RPMUtils.rpmvercmp(x.v, y.v)
- return cmp if cmp != 0
- end
-
- # compare release
- if partial && (x.r.nil? || y.r.nil?)
- return 0
- elsif x.r.nil? == false && y.r.nil?
- return 1
- elsif x.r.nil? && y.r.nil? == false
- return -1
- elsif x.r.nil? == false && y.r.nil? == false
- cmp = RPMUtils.rpmvercmp(x.r, y.r)
- return cmp
- end
+ super
+ end
- return 0
+ def candidate_version
+ package_name_array.each_with_index.map do |pkg, i|
+ available_version(i).version_with_arch
end
end
- class RPMPackage
- include Comparable
-
- def initialize(*args)
- if args.size == 4
- @n = args[0]
- @version = RPMVersion.new(args[1])
- @a = args[2]
- @provides = args[3]
- elsif args.size == 6
- @n = args[0]
- e = args[1].to_i
- v = args[2]
- r = args[3]
- @version = RPMVersion.new(e, v, r)
- @a = args[4]
- @provides = args[5]
- else
- raise ArgumentError, "Expecting either 'name, epoch-version-release, arch, provides' " +
- "or 'name, epoch, version, release, arch, provides'"
- end
-
- # We always have one, ourselves!
- if @provides.empty?
- @provides = [ RPMProvide.new(@n, @version.evr, :==) ]
- end
+ def get_current_versions
+ package_name_array.each_with_index.map do |pkg, i|
+ installed_version(i).version_with_arch
end
- attr_reader :n, :a, :version, :provides
- alias :name :n
- alias :arch :a
+ end
- def <=>(y)
- compare(y)
- end
+ def install_package(names, versions)
+ method = nil
+ methods = []
+ names.each_with_index do |n, i|
+ next if n.nil?
- def compare(y)
- x = self
+ av = available_version(i)
- # easy! :)
- return 0 if x.nevra == y.nevra
+ name = av.name # resolve the name via the available/candidate version
- # compare name
- if x.n.nil? == false && y.n.nil?
- return 1
- elsif x.n.nil? && y.n.nil? == false
- return -1
- elsif x.n.nil? == false && y.n.nil? == false
- if x.n < y.n
- return -1
- elsif x.n > y.n
- return 1
- end
- end
+ iv = python_helper.package_query(:whatinstalled, name)
- # compare version
- if x.version > y.version
- return 1
- elsif x.version < y.version
- return -1
- end
+ method = "install"
- # compare arch
- if x.a.nil? == false && y.a.nil?
- return 1
- elsif x.a.nil? && y.a.nil? == false
- return -1
- elsif x.a.nil? == false && y.a.nil? == false
- if x.a < y.a
- return -1
- elsif x.a > y.a
- return 1
- end
+ # If this is a package like the kernel that can be installed multiple times, we'll skip over this logic
+ if new_resource.allow_downgrade && version_gt?(iv.version_with_arch, av.version_with_arch) && !python_helper.install_only_packages(name)
+ # We allow downgrading only in the evenit of single-package
+ # rules where the user explicitly allowed it
+ method = "downgrade"
end
- return 0
+ methods << method
end
- def to_s
- nevra
+ # We could split this up into two commands if we wanted to, but
+ # for now, just don't support this.
+ if methods.uniq.length > 1
+ raise Chef::Exceptions::Package, "Multipackage rule has a mix of upgrade and downgrade packages. Cannot proceed."
end
- def nevra
- "#{@n}-#{@version.evr}.#{@a}"
+ if new_resource.source
+ yum(options, "-y #{method}", new_resource.source)
+ else
+ resolved_names = names.each_with_index.map { |name, i| available_version(i).to_s unless name.nil? }
+ yum(options, "-y #{method}", resolved_names)
end
+ flushcache
end
- # Simple implementation from rpm and ruby-rpm reference code
- class RPMDependency
- def initialize(*args)
- if args.size == 3
- @name = args[0]
- @version = RPMVersion.new(args[1])
- # Our requirement to other dependencies
- @flag = args[2] || :==
- elsif args.size == 5
- @name = args[0]
- e = args[1].to_i
- v = args[2]
- r = args[3]
- @version = RPMVersion.new(e, v, r)
- @flag = args[4] || :==
- else
- raise ArgumentError, "Expecting either 'name, epoch-version-release, flag' or " +
- "'name, epoch, version, release, flag'"
- end
- end
- attr_reader :name, :version, :flag
-
- # Parses 2 forms:
- #
- # "mtr >= 2:0.71-3.0"
- # "mta"
- def self.parse(string)
- if string =~ %r{^(\S+)\s+(>|>=|=|==|<=|<)\s+(\S+)$}
- name = $1
- if $2 == "="
- flag = :==
- else
- flag = :"#{$2}"
- end
- version = $3
-
- return self.new(name, version, flag)
- else
- name = string
- return self.new(name, nil, nil)
- end
- end
-
- # Test if another RPMDependency satisfies our requirements
- def satisfy?(y)
- unless y.kind_of?(RPMDependency)
- raise ArgumentError, "Expecting an RPMDependency object"
- end
-
- x = self
-
- # Easy!
- if x.name != y.name
- return false
- end
-
- # Partial compare
- #
- # eg: x.version 2.3 == y.version 2.3-1
- sense = x.version.partial_compare(y.version)
+ # yum upgrade does not work on uninstalled packaged, while install will upgrade
+ alias upgrade_package install_package
- # Thanks to rpmdsCompare() rpmds.c
- if (sense < 0) && ((x.flag == :> || x.flag == :>=) || (y.flag == :<= || y.flag == :<))
- return true
- elsif (sense > 0) && ((x.flag == :< || x.flag == :<=) || (y.flag == :>= || y.flag == :>))
- return true
- elsif sense == 0 && (
- ((x.flag == :== || x.flag == :<= || x.flag == :>=) && (y.flag == :== || y.flag == :<= || y.flag == :>=)) ||
- (x.flag == :< && y.flag == :<) ||
- (x.flag == :> && y.flag == :>)
- )
- return true
- end
-
- return false
- end
+ def remove_package(names, versions)
+ resolved_names = names.each_with_index.map { |name, i| installed_version(i).to_s unless name.nil? }
+ yum(options, "-y remove", resolved_names)
+ flushcache
end
- class RPMProvide < RPMDependency; end
- class RPMRequire < RPMDependency; end
+ alias purge_package remove_package
- class RPMDbPackage < RPMPackage
- # <rpm parts>, installed, available
- def initialize(*args)
- @repoid = args.pop
- # state
- @available = args.pop
- @installed = args.pop
- super(*args)
- end
- attr_reader :repoid, :available, :installed
+ action :flush_cache do
+ flushcache
end
- # Simple storage for RPMPackage objects - keeps them unique and sorted
- class RPMDb
- def initialize
- # package name => [ RPMPackage, RPMPackage ] of different versions
- @rpms = Hash.new
- # package nevra => RPMPackage for lookups
- @index = Hash.new
- # provide name (aka feature) => [RPMPackage, RPMPackage] each providing this feature
- @provides = Hash.new
- # RPMPackages listed as available
- @available = Set.new
- # RPMPackages listed as installed
- @installed = Set.new
- end
-
- def [](package_name)
- self.lookup(package_name)
- end
-
- # Lookup package_name and return a descending array of package objects
- def lookup(package_name)
- pkgs = @rpms[package_name]
- if pkgs
- return pkgs.sort.reverse
- else
- return nil
- end
- end
-
- def lookup_provides(provide_name)
- @provides[provide_name]
- end
+ # NB: the yum_package provider manages individual single packages, please do not submit issues or PRs to try to add wildcard
+ # support to lock / unlock. The best solution is to write an execute resource which does a not_if `yum versionlock | grep '^pattern`` kind of approach
+ def lock_package(names, versions)
+ yum("-d0 -e0 -y", options, "versionlock add", resolved_package_lock_names(names))
+ end
- # Using the package name as a key, and nevra for an index, keep a unique list of packages.
- # The available/installed state can be overwritten for existing packages.
- def push(*args)
- args.flatten.each do |new_rpm|
- unless new_rpm.kind_of?(RPMDbPackage)
- raise ArgumentError, "Expecting an RPMDbPackage object"
- end
+ # NB: the yum_package provider manages individual single packages, please do not submit issues or PRs to try to add wildcard
+ # support to lock / unlock. The best solution is to write an execute resource which does a only_if `yum versionlock | grep '^pattern`` kind of approach
+ def unlock_package(names, versions)
+ # yum versionlock delete on rhel6 needs the glob nonsense in the following command
+ yum("-d0 -e0 -y", options, "versionlock delete", resolved_package_lock_names(names).map { |n| "'*:#{n}-*'" })
+ end
- @rpms[new_rpm.n] ||= Array.new
+ private
- # we may already have this one, like when the installed list is refreshed
- idx = @index[new_rpm.nevra]
- if idx
- # grab the existing package if it's not
- curr_rpm = idx
+ # this will resolve things like `/usr/bin/perl` or virtual packages like `mysql` -- it will not work (well? at all?) with globs that match multiple packages
+ def resolved_package_lock_names(names)
+ names.each_with_index.map do |name, i|
+ if !name.nil?
+ if installed_version(i).version.nil?
+ available_version(i).name
else
- @rpms[new_rpm.n] << new_rpm
-
- new_rpm.provides.each do |provide|
- @provides[provide.name] ||= Array.new
- @provides[provide.name] << new_rpm
- end
-
- curr_rpm = new_rpm
- end
-
- # Track the nevra -> RPMPackage association to avoid having to compare versions
- # with @rpms[new_rpm.n] on the next round
- @index[new_rpm.nevra] = curr_rpm
-
- # these are overwritten for existing packages
- if new_rpm.available
- @available << curr_rpm
- end
- if new_rpm.installed
- @installed << curr_rpm
+ installed_version(i).name
end
end
end
-
- def <<(*args)
- self.push(args)
- end
-
- def clear
- @rpms.clear
- @index.clear
- @provides.clear
- clear_available
- clear_installed
- end
-
- def clear_available
- @available.clear
- end
-
- def clear_installed
- @installed.clear
- end
-
- def size
- @rpms.size
- end
- alias :length :size
-
- def available_size
- @available.size
- end
-
- def installed_size
- @installed.size
- end
-
- def available?(package)
- @available.include?(package)
- end
-
- def installed?(package)
- @installed.include?(package)
- end
-
- def whatprovides(rpmdep)
- unless rpmdep.kind_of?(RPMDependency)
- raise ArgumentError, "Expecting an RPMDependency object"
- end
-
- what = []
-
- packages = lookup_provides(rpmdep.name)
- if packages
- packages.each do |pkg|
- pkg.provides.each do |provide|
- if provide.satisfy?(rpmdep)
- what << pkg
- end
- end
- end
- end
-
- return what
- end
end
- # Cache for our installed and available packages, pulled in from yum-dump.py
- class YumCache
- include Chef::Mixin::Which
- include Chef::Mixin::ShellOut
- include Singleton
-
- attr_accessor :yum_binary
-
- def initialize
- @rpmdb = RPMDb.new
-
- # Next time @rpmdb is accessed:
- # :all - Trigger a run of "yum-dump.py --options --installed-provides", updates
- # yum's cache and parses options from /etc/yum.conf. Pulls in Provides
- # dependency data for installed packages only - this data is slow to
- # gather.
- # :provides - Same as :all but pulls in Provides data for available packages as well.
- # Used as a last resort when we can't find a Provides match.
- # :installed - Trigger a run of "yum-dump.py --installed", only reads the local rpm
- # db. Used between client runs for a quick refresh.
- # :none - Do nothing, a call to one of the reload methods is required.
- @next_refresh = :all
-
- @allow_multi_install = []
-
- @extra_repo_control = nil
-
- # these are for subsequent runs if we are on an interval
- Chef::Client.when_run_starts do
- YumCache.instance.reload
- end
- end
-
- attr_reader :extra_repo_control
-
- # Cache management
- #
-
- def refresh
- case @next_refresh
- when :none
- return nil
- when :installed
- reset_installed
- # fast
- opts = " --installed"
- when :all
- reset
- # medium
- opts = " --options --installed-provides"
- when :provides
- reset
- # slow!
- opts = " --options --all-provides"
- else
- raise ArgumentError, "Unexpected value in next_refresh: #{@next_refresh}"
- end
-
- if @extra_repo_control
- opts << " #{@extra_repo_control}"
- end
-
- opts << " --yum-lock-timeout #{Chef::Config[:yum_lock_timeout]}"
-
- one_line = false
- error = nil
-
- helper = ::File.join(::File.dirname(__FILE__), "yum-dump.py")
- status = nil
-
+ def locked_packages
+ @locked_packages ||=
begin
- status = shell_out!("#{python_bin} #{helper}#{opts}", :timeout => Chef::Config[:yum_timeout])
- status.stdout.each_line do |line|
- one_line = true
-
- line.chomp!
- if line =~ %r{\[option (.*)\] (.*)}
- if $1 == "installonlypkgs"
- @allow_multi_install = $2.split
- else
- raise Chef::Exceptions::Package, "Strange, unknown option line '#{line}' from yum-dump.py"
- end
- next
- end
-
- if line =~ %r{^(\S+) ([0-9]+) (\S+) (\S+) (\S+) \[(.*)\] ([i,a,r]) (\S+)$}
- name = $1
- epoch = $2
- version = $3
- release = $4
- arch = $5
- provides = parse_provides($6)
- type = $7
- repoid = $8
- else
- Chef::Log.warn("Problem parsing line '#{line}' from yum-dump.py! " +
- "Please check your yum configuration.")
- next
- end
-
- case type
- when "i"
- # if yum-dump was called with --installed this may not be true, but it's okay
- # since we don't touch the @available Set in reload_installed
- available = false
- installed = true
- when "a"
- available = true
- installed = false
- when "r"
- available = true
- installed = true
- end
-
- pkg = RPMDbPackage.new(name, epoch, version, release, arch, provides, installed, available, repoid)
- @rpmdb << pkg
- end
-
- error = status.stderr
- rescue Mixlib::ShellOut::CommandTimeout => e
- Chef::Log.error("#{helper} exceeded timeout #{Chef::Config[:yum_timeout]}")
- raise(e)
- end
-
- if status.exitstatus != 0
- raise Chef::Exceptions::Package, "Yum failed - #{status.inspect} - returns: #{error}"
- else
- unless one_line
- Chef::Log.warn("Odd, no output from yum-dump.py. Please check " +
- "your yum configuration.")
- end
- end
-
- # A reload method must be called before the cache is altered
- @next_refresh = :none
- end
-
- def python_bin
- yum_executable = which(yum_binary)
- if yum_executable && shabang?(yum_executable)
- shabang_or_fallback(extract_interpreter(yum_executable))
- else
- Chef::Log.warn("Yum executable not found or doesn't start with #!. Using default python.")
- "/usr/bin/python"
- end
- rescue StandardError => e
- Chef::Log.warn("An error occurred attempting to determine correct python executable. Using default.")
- Chef::Log.debug(e)
- "/usr/bin/python"
- end
-
- def extract_interpreter(file)
- ::File.open(file, "r", &:readline)[2..-1].strip
- end
-
- # dnf based systems have a yum shim that has /bin/bash as the interpreter. Don't use this.
- def shabang_or_fallback(interpreter)
- if interpreter == "/bin/bash"
- Chef::Log.warn("Yum executable interpreter is /bin/bash. Falling back to default python.")
- "/usr/bin/python"
- else
- interpreter
- end
- end
-
- def shabang?(file)
- ::File.open(file, "r") do |f|
- f.read(2) == '#!'
- end
- rescue Errno::ENOENT
- false
- end
-
- def reload
- @next_refresh = :all
- end
-
- def reload_installed
- @next_refresh = :installed
- end
-
- def reload_provides
- @next_refresh = :provides
- end
-
- def reset
- @rpmdb.clear
- end
-
- def reset_installed
- @rpmdb.clear_installed
- end
-
- # Querying the cache
- #
-
- # Check for package by name or name+arch
- def package_available?(package_name)
- refresh
-
- if @rpmdb.lookup(package_name)
- return true
- else
- if package_name =~ %r{^(.*)\.(.*)$}
- pkg_name = $1
- pkg_arch = $2
-
- if matches = @rpmdb.lookup(pkg_name)
- matches.each do |m|
- return true if m.arch == pkg_arch
- end
- end
+ locked = shell_out_with_timeout!("yum versionlock list")
+ locked.stdout.each_line.map do |line|
+ line.sub(/-[^-]*-[^-]*$/, "").split(":").last.strip
end
end
-
- return false
- end
-
- # Returns a array of packages satisfying an RPMDependency
- def packages_from_require(rpmdep)
- refresh
- @rpmdb.whatprovides(rpmdep)
- end
-
- # Check if a package-version.arch is available to install
- def version_available?(package_name, desired_version, arch = nil)
- version(package_name, arch, true, false) do |v|
- return true if desired_version == v
- end
-
- return false
- end
-
- # Return the source repository for a package-version.arch
- def package_repository(package_name, desired_version, arch = nil)
- package(package_name, arch, true, false) do |pkg|
- return pkg.repoid if desired_version == pkg.version.to_s
- end
-
- return nil
- end
-
- # Return the latest available version for a package.arch
- def available_version(package_name, arch = nil)
- version(package_name, arch, true, false)
- end
- alias :candidate_version :available_version
-
- # Return the currently installed version for a package.arch
- def installed_version(package_name, arch = nil)
- version(package_name, arch, false, true)
- end
-
- # Return an array of packages allowed to be installed multiple times, such as the kernel
- def allow_multi_install
- refresh
- @allow_multi_install
- end
-
- def enable_extra_repo_control(arg)
- # Don't touch cache if it's the same repos as the last load
- unless @extra_repo_control == arg
- @extra_repo_control = arg
- reload
- end
- end
-
- def disable_extra_repo_control
- # Only force reload when set
- if @extra_repo_control
- @extra_repo_control = nil
- reload
- end
- end
-
- private
-
- def version(package_name, arch = nil, is_available = false, is_installed = false)
- package(package_name, arch, is_available, is_installed) do |pkg|
- if block_given?
- yield pkg.version.to_s
- else
- # first match is latest version
- return pkg.version.to_s
- end
- end
-
- if block_given?
- return self
- else
- return nil
- end
- end
-
- def package(package_name, arch = nil, is_available = false, is_installed = false)
- refresh
- packages = @rpmdb[package_name]
- if packages
- packages.each do |pkg|
- if is_available
- next unless @rpmdb.available?(pkg)
- end
- if is_installed
- next unless @rpmdb.installed?(pkg)
- end
- if arch
- next unless pkg.arch == arch
- end
-
- if block_given?
- yield pkg
- else
- # first match is latest version
- return pkg
- end
- end
- end
-
- if block_given?
- return self
- else
- return nil
- end
- end
-
- # Parse provides from yum-dump.py output
- def parse_provides(string)
- ret = []
- # ['atk = 1.12.2-1.fc6', 'libatk-1.0.so.0']
- string.split(", ").each do |seg|
- # 'atk = 1.12.2-1.fc6'
- if seg =~ %r{^'(.*)'$}
- ret << RPMProvide.parse($1)
- end
- end
-
- return ret
- end
-
- end # YumCache
-
- include Chef::Mixin::GetSourceFromPackage
-
- def initialize(new_resource, run_context)
- super
-
- @yum = YumCache.instance
- @yum.yum_binary = yum_binary
end
- def yum_binary
- @yum_binary ||=
- begin
- yum_binary = new_resource.yum_binary if new_resource.is_a?(Chef::Resource::YumPackage)
- yum_binary ||= ::File.exist?("/usr/bin/yum-deprecated") ? "yum-deprecated" : "yum"
- end
+ def packages_all_locked?(names, versions)
+ resolved_package_lock_names(names).all? { |n| locked_packages.include? n }
end
- # Extra attributes
- #
-
- def arch_for_name(n)
- if @new_resource.respond_to?("arch")
- @new_resource.arch
- elsif @arch
- idx = package_name_array.index(n)
- as_array(@arch)[idx]
- else
- nil
- end
+ def packages_all_unlocked?(names, versions)
+ !resolved_package_lock_names(names).any? { |n| locked_packages.include? n }
end
- def arch
- if @new_resource.respond_to?("arch")
- @new_resource.arch
- else
- nil
- end
+ def version_gt?(v1, v2)
+ return false if v1.nil? || v2.nil?
+ python_helper.compare_versions(v1, v2) == 1
end
- def set_arch(arch)
- if @new_resource.respond_to?("arch")
- @new_resource.arch(arch)
- end
+ def version_equals?(v1, v2)
+ return false if v1.nil? || v2.nil?
+ python_helper.compare_versions(v1, v2) == 0
end
- def flush_cache
- if @new_resource.respond_to?("flush_cache")
- @new_resource.flush_cache
- else
- { :before => false, :after => false }
- end
+ def version_compare(v1, v2)
+ return false if v1.nil? || v2.nil?
+ python_helper.compare_versions(v1, v2)
end
- # Helpers
- #
-
- def yum_arch(arch)
- arch ? ".#{arch}" : nil
+ # Generate the yum syntax for the package
+ def yum_syntax(name, version, arch)
+ s = name
+ s += "-#{version}" if version
+ s += ".#{arch}" if arch
+ s
end
- def yum_command(command)
- command = "#{yum_binary} #{command}"
- Chef::Log.debug("#{@new_resource}: yum command: \"#{command}\"")
- status = shell_out_with_timeout(command, { :timeout => Chef::Config[:yum_timeout] })
-
- # This is fun: rpm can encounter errors in the %post/%postun scripts which aren't
- # considered fatal - meaning the rpm is still successfully installed. These issue
- # cause yum to emit a non fatal warning but still exit(1). As there's currently no
- # way to suppress this behavior and an exit(1) will break a Chef run we make an
- # effort to trap these and re-run the same install command - it will either fail a
- # second time or succeed.
- #
- # A cleaner solution would have to be done in python and better hook into
- # yum/rpm to handle exceptions as we see fit.
- if status.exitstatus == 1
- status.stdout.each_line do |l|
- # rpm-4.4.2.3 lib/psm.c line 2182
- if l =~ %r{^error: %(post|postun)\(.*\) scriptlet failed, exit status \d+$}
- Chef::Log.warn("#{@new_resource} caught non-fatal scriptlet issue: \"#{l}\". Can't trust yum exit status " +
- "so running install again to verify.")
- status = shell_out_with_timeout(command, { :timeout => Chef::Config[:yum_timeout] })
- break
- end
+ def resolve_source_to_version_obj
+ shell_out_with_timeout!("rpm -qp --queryformat '%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n' #{new_resource.source}").stdout.each_line do |line|
+ # this is another case of committing the sin of doing some lightweight mangling of RPM versions in ruby -- but the output of the rpm command
+ # does not match what the yum library accepts.
+ case line
+ when /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$/
+ return Version.new($1, "#{$2 == '(none)' ? '0' : $2}:#{$3}-#{$4}", $5)
end
end
-
- if status.exitstatus > 0
- command_output = "STDOUT: #{status.stdout}\nSTDERR: #{status.stderr}"
- raise Chef::Exceptions::Exec, "#{command} returned #{status.exitstatus}:\n#{command_output}"
- end
end
- # Standard Provider methods for Parent
- #
-
- def load_current_resource
- if flush_cache[:before]
- @yum.reload
- end
-
- if @new_resource.options
- repo_control = []
- @new_resource.options.split.each do |opt|
- if opt =~ %r{--(enable|disable)repo=.+}
- repo_control << opt
- end
- end
-
- if repo_control.size > 0
- @yum.enable_extra_repo_control(repo_control.join(" "))
- else
- @yum.disable_extra_repo_control
- end
- else
- @yum.disable_extra_repo_control
- end
-
- # At this point package_name could be:
- #
- # 1) a package name, eg: "foo"
- # 2) a package name.arch, eg: "foo.i386"
- # 3) or a dependency, eg: "foo >= 1.1"
-
- # Check if we have name or name+arch which has a priority over a dependency
- package_name_array.each_with_index do |n, index|
- unless @yum.package_available?(n)
- # If they aren't in the installed packages they could be a dependency
- dep = parse_dependency(n, new_version_array[index])
- if dep
- if @new_resource.package_name.is_a?(Array)
- @new_resource.package_name(package_name_array - [n] + [dep.first])
- @new_resource.version(new_version_array - [new_version_array[index]] + [dep.last]) if dep.last
- else
- @new_resource.package_name(dep.first)
- @new_resource.version(dep.last) if dep.last
- end
- end
- end
- end
-
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
-
- installed_version = []
- @candidate_version = []
- @arch = []
- if @new_resource.source
- unless ::File.exists?(@new_resource.source)
- raise Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}"
- end
-
- Chef::Log.debug("#{@new_resource} checking rpm status")
- shell_out_with_timeout!("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@new_resource.source}", :timeout => Chef::Config[:yum_timeout]).stdout.each_line do |line|
- case line
- when /([\w\d_.-]+)\s([\w\d_.-]+)/
- @current_resource.package_name($1)
- @new_resource.version($2)
- end
- end
- @candidate_version << @new_resource.version
- installed_version << @yum.installed_version(@current_resource.package_name, arch)
- else
-
- package_name_array.each_with_index do |pkg, idx|
- # Don't overwrite an existing arch
- if arch
- name, parch = pkg, arch
- else
- name, parch = parse_arch(pkg)
- # if we parsed an arch from the name, update the name
- # to be just the package name.
- if parch
- if @new_resource.package_name.is_a?(Array)
- @new_resource.package_name[idx] = name
- else
- @new_resource.package_name(name)
- # only set the arch if it's a single package
- set_arch(parch)
- end
- end
- end
+ # @returns Array<Version>
+ def available_version(index)
+ @available_version ||= []
- if @new_resource.version
- new_resource =
- "#{@new_resource.package_name}-#{@new_resource.version}#{yum_arch(parch)}"
- else
- new_resource = "#{@new_resource.package_name}#{yum_arch(parch)}"
- end
- Chef::Log.debug("#{@new_resource} checking yum info for #{new_resource}")
- installed_version << @yum.installed_version(name, parch)
- @candidate_version << @yum.candidate_version(name, parch)
- @arch << parch
- end
- end
+ @available_version[index] ||= if new_resource.source
+ resolve_source_to_version_obj
+ else
+ python_helper.package_query(:whatavailable, package_name_array[index], version: safe_version_array[index], arch: safe_arch_array[index], options: options)
+ end
- if installed_version.size == 1
- @current_resource.version(installed_version[0])
- @candidate_version = @candidate_version[0]
- @arch = @arch[0]
- else
- @current_resource.version(installed_version)
- end
-
- Chef::Log.debug("#{@new_resource} installed version: #{installed_version || "(none)"} candidate version: " +
- "#{@candidate_version || "(none)"}")
-
- @current_resource
+ @available_version[index]
end
- def install_remote_package(name, version)
- # Work around yum not exiting with an error if a package doesn't exist
- # for CHEF-2062
- all_avail = as_array(name).zip(as_array(version)).any? do |n, v|
- @yum.version_available?(n, v, arch_for_name(n))
- end
- method = log_method = nil
- methods = []
- if all_avail
- # More Yum fun:
- #
- # yum install of an old name+version will exit(1)
- # yum install of an old name+version+arch will exit(0) for some reason
- #
- # Some packages can be installed multiple times like the kernel
- as_array(name).zip(as_array(version)).each do |n, v|
- method = "install"
- log_method = "installing"
- idx = package_name_array.index(n)
- unless @yum.allow_multi_install.include?(n)
- if RPMVersion.parse(current_version_array[idx]) > RPMVersion.parse(v)
- # We allow downgrading only in the evenit of single-package
- # rules where the user explicitly allowed it
- if allow_downgrade
- method = "downgrade"
- log_method = "downgrading"
- else
- # we bail like yum when the package is older
- raise Chef::Exceptions::Package, "Installed package #{n}-#{current_version_array[idx]} is newer " +
- "than candidate package #{n}-#{v}"
- end
- end
- end
- # methods don't count for packages we won't be touching
- next if RPMVersion.parse(current_version_array[idx]) == RPMVersion.parse(v)
- methods << method
- end
-
- # We could split this up into two commands if we wanted to, but
- # for now, just don't support this.
- if methods.uniq.length > 1
- raise Chef::Exceptions::Package, "Multipackage rule #{name} has a mix of upgrade and downgrade packages. Cannot proceed."
- end
-
- repos = []
- pkg_string_bits = []
- as_array(name).zip(as_array(version)).each do |n, v|
- idx = package_name_array.index(n)
- a = arch_for_name(n)
- s = ""
- unless v == current_version_array[idx]
- s = "#{n}-#{v}#{yum_arch(a)}"
- repo = @yum.package_repository(n, v, a)
- repos << "#{s} from #{repo} repository"
- pkg_string_bits << s
- end
- end
- pkg_string = pkg_string_bits.join(" ")
- Chef::Log.info("#{@new_resource} #{log_method} #{repos.join(' ')}")
- yum_command("-d0 -e0 -y#{expand_options(@new_resource.options)} #{method} #{pkg_string}")
- else
- raise Chef::Exceptions::Package, "Version #{version} of #{name} not found. Did you specify both version " +
- "and release? (version-release, e.g. 1.84-10.fc6)"
- end
+ # @returns Array<Version>
+ def installed_version(index)
+ @installed_version ||= []
+ @installed_version[index] ||= if new_resource.source
+ python_helper.package_query(:whatinstalled, available_version(index).name, version: safe_version_array[index], arch: safe_arch_array[index])
+ else
+ python_helper.package_query(:whatinstalled, package_name_array[index], version: safe_version_array[index], arch: safe_arch_array[index])
+ end
+ @installed_version[index]
end
- def install_package(name, version)
- if @new_resource.source
- yum_command("-d0 -e0 -y#{expand_options(@new_resource.options)} localinstall #{@new_resource.source}")
- else
- install_remote_package(name, version)
- end
-
- if flush_cache[:after]
- @yum.reload
- else
- @yum.reload_installed
- end
+ # cache flushing is accomplished by simply restarting the python helper. this produces a roughly
+ # 15% hit to the runtime of installing/removing/upgrading packages. correctly using multipackage
+ # array installs (and the multipackage cookbook) can produce 600% improvements in runtime.
+ def flushcache
+ python_helper.restart
end
- # Keep upgrades from trying to install an older candidate version. Can happen when a new
- # version is installed then removed from a repository, now the older available version
- # shows up as a viable install candidate.
- #
- # Can be done in upgrade_package but an upgraded from->to log message slips out
- #
- # Hacky - better overall solution? Custom compare in Package provider?
- def action_upgrade
- # Could be uninstalled or have no candidate
- if @current_resource.version.nil? || !candidate_version_array.any?
- super
- elsif candidate_version_array.zip(current_version_array).any? do |c, i|
- RPMVersion.parse(c) > RPMVersion.parse(i)
- end
- super
- else
- Chef::Log.debug("#{@new_resource} is at the latest version - nothing to do")
- end
+ def yum_binary
+ @yum_binary ||=
+ begin
+ yum_binary = new_resource.yum_binary if new_resource.is_a?(Chef::Resource::YumPackage)
+ yum_binary ||= ::File.exist?("/usr/bin/yum-deprecated") ? "yum-deprecated" : "yum"
+ end
end
- def upgrade_package(name, version)
- install_package(name, version)
+ def yum(*args)
+ shell_out_with_timeout!(a_to_s(yum_binary, *args))
end
- def remove_package(name, version)
- if version
- remove_str = as_array(name).zip(as_array(version)).map do |n, v|
- a = arch_for_name(n)
- "#{[n, v].join('-')}#{yum_arch(a)}"
- end.join(" ")
+ def safe_version_array
+ if new_resource.version.is_a?(Array)
+ new_resource.version
+ elsif new_resource.version.nil?
+ package_name_array.map { nil }
else
- remove_str = as_array(name).map do |n|
- a = arch_for_name(n)
- "#{n}#{yum_arch(a)}"
- end.join(" ")
+ [ new_resource.version ]
end
- yum_command("-d0 -e0 -y#{expand_options(@new_resource.options)} remove #{remove_str}")
-
- if flush_cache[:after]
- @yum.reload
- else
- @yum.reload_installed
- end
- end
-
- def purge_package(name, version)
- remove_package(name, version)
end
- private
-
- def parse_arch(package_name)
- # Allow for foo.x86_64 style package_name like yum uses in it's output
- #
- if package_name =~ %r{^(.*)\.(.*)$}
- new_package_name = $1
- new_arch = $2
- # foo.i386 and foo.beta1 are both valid package names or expressions of an arch.
- # Ensure we don't have an existing package matching package_name, then ensure we at
- # least have a match for the new_package+new_arch before we overwrite. If neither
- # then fall through to standard package handling.
- old_installed = @yum.installed_version(package_name)
- old_candidate = @yum.candidate_version(package_name)
- new_installed = @yum.installed_version(new_package_name, new_arch)
- new_candidate = @yum.candidate_version(new_package_name, new_arch)
- if (old_installed.nil? && old_candidate.nil?) && (new_installed || new_candidate)
- Chef::Log.debug("Parsed out arch #{new_arch}, new package name is #{new_package_name}")
- return new_package_name, new_arch
- end
- end
- return package_name, nil
- end
-
- # If we don't have the package we could have been passed a 'whatprovides' feature
- #
- # eg: yum install "perl(Config)"
- # yum install "mtr = 2:0.71-3.1"
- # yum install "mtr > 2:0.71"
- #
- # We support resolving these out of the Provides data imported from yum-dump.py and
- # matching them up with an actual package so the standard resource handling can apply.
- #
- # There is currently no support for filename matching.
- def parse_dependency(name, version)
- # Transform the package_name into a requirement
-
- # If we are passed a version or a version constraint we have to assume it's a requirement first. If it can't be
- # parsed only yum_require.name will be set and @new_resource.version will be left intact
- if version
- require_string = "#{name} #{version}"
+ def safe_arch_array
+ if new_resource.arch.is_a?(Array)
+ new_resource.arch
+ elsif new_resource.arch.nil?
+ package_name_array.map { nil }
else
- # Transform the package_name into a requirement, might contain a version, could just be
- # a match for virtual provides
- require_string = name
- end
- yum_require = RPMRequire.parse(require_string)
- # and gather all the packages that have a Provides feature satisfying the requirement.
- # It could be multiple be we can only manage one
- packages = @yum.packages_from_require(yum_require)
-
- if packages.empty?
- # Don't bother if we are just ensuring a package is removed - we don't need Provides data
- actions = Array(@new_resource.action)
- unless actions.size == 1 && (actions[0] == :remove || actions[0] == :purge)
- Chef::Log.debug("#{@new_resource} couldn't match #{@new_resource.package_name} in " +
- "installed Provides, loading available Provides - this may take a moment")
- @yum.reload_provides
- packages = @yum.packages_from_require(yum_require)
- end
- end
-
- unless packages.empty?
- new_package_name = packages.first.name
- new_package_version = packages.first.version.to_s
- debug_msg = "#{name}: Unable to match package '#{name}' but matched #{packages.size} "
- debug_msg << (packages.size == 1 ? "package" : "packages")
- debug_msg << ", selected '#{new_package_name}' version '#{new_package_version}'"
- Chef::Log.debug(debug_msg)
-
- # Ensure it's not the same package under a different architecture
- unique_names = []
- packages.each do |pkg|
- unique_names << "#{pkg.name}-#{pkg.version.evr}"
- end
- unique_names.uniq!
-
- if unique_names.size > 1
- Chef::Log.warn("#{@new_resource} matched multiple Provides for #{@new_resource.package_name} " +
- "but we can only use the first match: #{new_package_name}. Please use a more " +
- "specific version.")
- end
-
- if yum_require.version.to_s.nil?
- new_package_version = nil
- end
-
- [new_package_name, new_package_version]
+ [ new_resource.arch ]
end
end
diff --git a/lib/chef/provider/package/yum/python_helper.rb b/lib/chef/provider/package/yum/python_helper.rb
new file mode 100644
index 0000000000..0a09e12c3d
--- /dev/null
+++ b/lib/chef/provider/package/yum/python_helper.rb
@@ -0,0 +1,221 @@
+#
+# Copyright:: Copyright 2016-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/mixin/which"
+require "chef/mixin/shell_out"
+require "chef/provider/package/yum/version"
+require "singleton"
+require "timeout"
+
+class Chef
+ class Provider
+ class Package
+ class Yum < Chef::Provider::Package
+ class PythonHelper
+ include Singleton
+ include Chef::Mixin::Which
+ include Chef::Mixin::ShellOut
+
+ attr_accessor :stdin
+ attr_accessor :stdout
+ attr_accessor :stderr
+ attr_accessor :inpipe
+ attr_accessor :outpipe
+ attr_accessor :wait_thr
+
+ YUM_HELPER = ::File.expand_path(::File.join(::File.dirname(__FILE__), "yum_helper.py")).freeze
+
+ def yum_command
+ @yum_command ||= which("python", "python2", "python2.7") do |f|
+ shell_out("#{f} -c 'import yum'").exitstatus == 0
+ end + " #{YUM_HELPER}"
+ end
+
+ def start
+ ENV["PYTHONUNBUFFERED"] = "1"
+ @inpipe, inpipe_write = IO.pipe
+ outpipe_read, @outpipe = IO.pipe
+ @stdin, @stdout, @stderr, @wait_thr = Open3.popen3("#{yum_command} #{outpipe_read.fileno} #{inpipe_write.fileno}", outpipe_read.fileno => outpipe_read, inpipe_write.fileno => inpipe_write, close_others: false)
+ outpipe_read.close
+ inpipe_write.close
+ end
+
+ def reap
+ unless wait_thr.nil?
+ Process.kill("INT", wait_thr.pid) rescue nil
+ begin
+ Timeout.timeout(3) do
+ wait_thr.value # this calls waitpid()
+ end
+ rescue Timeout::Error
+ Process.kill("KILL", wait_thr.pid) rescue nil
+ end
+ stdin.close unless stdin.nil?
+ stdout.close unless stdout.nil?
+ stderr.close unless stderr.nil?
+ inpipe.close unless inpipe.nil?
+ outpipe.close unless outpipe.nil?
+ end
+ end
+
+ def check
+ start if stdin.nil?
+ end
+
+ def compare_versions(version1, version2)
+ query("versioncompare", { "versions" => [version1, version2] }).to_i
+ end
+
+ def install_only_packages(name)
+ query_output = query("installonlypkgs", { "package" => name })
+ if query_output == "False"
+ return false
+ elsif query_output == "True"
+ return true
+ end
+ end
+
+ def options_params(options)
+ options.each_with_object({}) do |opt, h|
+ if opt =~ /--enablerepo=(.+)/
+ $1.split(",").each do |repo|
+ h["repos"] ||= []
+ h["repos"].push( { "enable" => repo } )
+ end
+ end
+ if opt =~ /--disablerepo=(.+)/
+ $1.split(",").each do |repo|
+ h["repos"] ||= []
+ h["repos"].push( { "disable" => repo } )
+ end
+ end
+ end
+ end
+
+ # @returns Array<Version>
+ # NB: "options" here is the yum_package options hash and is deliberately not **opts
+ def package_query(action, provides, version: nil, arch: nil, options: {})
+ parameters = { "provides" => provides, "version" => version, "arch" => arch }
+ repo_opts = options_params(options || {})
+ parameters.merge!(repo_opts)
+ query_output = query(action, parameters)
+ version = parse_response(query_output.lines.last)
+ Chef::Log.trace "parsed #{version} from python helper"
+ # XXX: for now we restart after every query with an enablerepo/disablerepo to clean the helpers internal state
+ restart unless repo_opts.empty?
+ version
+ end
+
+ def restart
+ reap
+ start
+ end
+
+ private
+
+ # i couldn't figure out how to decompose an evr on the python side, it seems reasonably
+ # painless to do it in ruby (generally massaging nevras in the ruby side is HIGHLY
+ # discouraged -- this is an "every rule has an exception" exception -- any additional
+ # functionality should probably trigger moving this regexp logic into python)
+ def add_version(hash, version)
+ epoch = nil
+ if version =~ /(\S+):(\S+)/
+ epoch = $1
+ version = $2
+ end
+ if version =~ /(\S+)-(\S+)/
+ version = $1
+ release = $2
+ end
+ hash["epoch"] = epoch unless epoch.nil?
+ hash["release"] = release unless release.nil?
+ hash["version"] = version
+ end
+
+ def query(action, parameters)
+ with_helper do
+ json = build_query(action, parameters)
+ Chef::Log.trace "sending '#{json}' to python helper"
+ outpipe.syswrite json + "\n"
+ output = inpipe.sysread(4096).chomp
+ Chef::Log.trace "got '#{output}' from python helper"
+ return output
+ end
+ end
+
+ def build_query(action, parameters)
+ hash = { "action" => action }
+ parameters.each do |param_name, param_value|
+ hash[param_name] = param_value unless param_value.nil?
+ end
+
+ # Special handling for certain action / param combos
+ if [:whatinstalled, :whatavailable].include?(action)
+ add_version(hash, parameters["version"]) unless parameters["version"].nil?
+ end
+
+ FFI_Yajl::Encoder.encode(hash)
+ end
+
+ def parse_response(output)
+ array = output.split.map { |x| x == "nil" ? nil : x }
+ array.each_slice(3).map { |x| Version.new(*x) }.first
+ end
+
+ def drain_fds
+ output = ""
+ fds, = IO.select([stderr, stdout, inpipe], nil, nil, 0)
+ unless fds.nil?
+ fds.each do |fd|
+ output += fd.sysread(4096) rescue ""
+ end
+ end
+ output
+ rescue => e
+ output
+ end
+
+ def with_helper
+ max_retries ||= 5
+ ret = nil
+ Timeout.timeout(600) do
+ check
+ ret = yield
+ end
+ output = drain_fds
+ unless output.empty?
+ Chef::Log.trace "discarding output on stderr/stdout from python helper: #{output}"
+ end
+ ret
+ rescue EOFError, Errno::EPIPE, Timeout::Error, Errno::ESRCH => e
+ output = drain_fds
+ if ( max_retries -= 1 ) > 0
+ unless output.empty?
+ Chef::Log.trace "discarding output on stderr/stdout from python helper: #{output}"
+ end
+ restart
+ retry
+ else
+ raise e if output.empty?
+ raise "yum-helper.py had stderr/stdout output:\n\n#{output}"
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/yum/rpm_utils.rb b/lib/chef/provider/package/yum/rpm_utils.rb
new file mode 100644
index 0000000000..eefc0b95b2
--- /dev/null
+++ b/lib/chef/provider/package/yum/rpm_utils.rb
@@ -0,0 +1,651 @@
+
+# Author:: Adam Jacob (<adam@chef.io>)
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/provider/package"
+
+#
+# BUGGY AND DEPRECATED: This ruby code is known to not match the python implementation for version comparisons.
+# The APIs here should probably be converted to talk to the PythonHelper or just abandonded completely.
+#
+# e.g. this should just use Chef::Provider::Package::Yum::PythonHelper.instance.compare_versions(x,y)
+#
+# The python_helper could be extended to support additional APIs in here to remove the ruby code entirely.
+#
+
+class Chef
+ class Provider
+ class Package
+ class Yum < Chef::Provider::Package
+ class RPMUtils
+ class << self
+
+ # RPM::Version version_parse equivalent
+ def version_parse(evr)
+ return if evr.nil?
+
+ epoch = nil
+ # assume this is a version
+ version = evr
+ release = nil
+
+ lead = 0
+ tail = evr.size
+
+ if /^([\d]+):/.match(evr) # rubocop:disable Performance/RedundantMatch
+ epoch = $1.to_i
+ lead = $1.length + 1
+ elsif evr[0].ord == ":".ord
+ epoch = 0
+ lead = 1
+ end
+
+ if /:?.*-(.*)$/.match(evr) # rubocop:disable Performance/RedundantMatch
+ release = $1
+ tail = evr.length - release.length - lead - 1
+
+ if release.empty?
+ release = nil
+ end
+ end
+
+ version = evr[lead, tail]
+ if version.empty?
+ version = nil
+ end
+
+ [ epoch, version, release ]
+ end
+
+ # verify
+ def isalnum(x)
+ isalpha(x) || isdigit(x)
+ end
+
+ def isalpha(x)
+ v = x.ord
+ (v >= 65 && v <= 90) || (v >= 97 && v <= 122)
+ end
+
+ def isdigit(x)
+ v = x.ord
+ v >= 48 && v <= 57
+ end
+
+ # based on the reference spec in lib/rpmvercmp.c in rpm 4.9.0
+ def rpmvercmp(x, y)
+ # easy! :)
+ return 0 if x == y
+
+ if x.nil?
+ x = ""
+ end
+
+ if y.nil?
+ y = ""
+ end
+
+ # not so easy :(
+ #
+ # takes 2 strings like
+ #
+ # x = "1.20.b18.el5"
+ # y = "1.20.b17.el5"
+ #
+ # breaks into purely alpha and numeric segments and compares them using
+ # some rules
+ #
+ # * 10 > 1
+ # * 1 > a
+ # * z > a
+ # * Z > A
+ # * z > Z
+ # * leading zeros are ignored
+ # * separators (periods, commas) are ignored
+ # * "1.20.b18.el5.extrastuff" > "1.20.b18.el5"
+
+ x_pos = 0 # overall string element reference position
+ x_pos_max = x.length - 1 # number of elements in string, starting from 0
+ x_seg_pos = 0 # segment string element reference position
+ x_comp = nil # segment to compare
+
+ y_pos = 0
+ y_seg_pos = 0
+ y_pos_max = y.length - 1
+ y_comp = nil
+
+ while x_pos <= x_pos_max && y_pos <= y_pos_max
+ # first we skip over anything non alphanumeric
+ while (x_pos <= x_pos_max) && (isalnum(x[x_pos]) == false)
+ x_pos += 1 # +1 over pos_max if end of string
+ end
+ while (y_pos <= y_pos_max) && (isalnum(y[y_pos]) == false)
+ y_pos += 1
+ end
+
+ # if we hit the end of either we are done matching segments
+ if (x_pos == x_pos_max + 1) || (y_pos == y_pos_max + 1)
+ break
+ end
+
+ # we are now at the start of a alpha or numeric segment
+ x_seg_pos = x_pos
+ y_seg_pos = y_pos
+
+ # grab segment so we can compare them
+ if isdigit(x[x_seg_pos].ord)
+ x_seg_is_num = true
+
+ # already know it's a digit
+ x_seg_pos += 1
+
+ # gather up our digits
+ while (x_seg_pos <= x_pos_max) && isdigit(x[x_seg_pos])
+ x_seg_pos += 1
+ end
+ # copy the segment but not the unmatched character that x_seg_pos will
+ # refer to
+ x_comp = x[x_pos, x_seg_pos - x_pos]
+
+ while (y_seg_pos <= y_pos_max) && isdigit(y[y_seg_pos])
+ y_seg_pos += 1
+ end
+ y_comp = y[y_pos, y_seg_pos - y_pos]
+ else
+ # we are comparing strings
+ x_seg_is_num = false
+
+ while (x_seg_pos <= x_pos_max) && isalpha(x[x_seg_pos])
+ x_seg_pos += 1
+ end
+ x_comp = x[x_pos, x_seg_pos - x_pos]
+
+ while (y_seg_pos <= y_pos_max) && isalpha(y[y_seg_pos])
+ y_seg_pos += 1
+ end
+ y_comp = y[y_pos, y_seg_pos - y_pos]
+ end
+
+ # if y_seg_pos didn't advance in the above loop it means the segments are
+ # different types
+ if y_pos == y_seg_pos
+ # numbers always win over letters
+ return x_seg_is_num ? 1 : -1
+ end
+
+ # move the ball forward before we mess with the segments
+ x_pos += x_comp.length # +1 over pos_max if end of string
+ y_pos += y_comp.length
+
+ # we are comparing numbers - simply convert them
+ if x_seg_is_num
+ x_comp = x_comp.to_i
+ y_comp = y_comp.to_i
+ end
+
+ # compares ints or strings
+ # don't return if equal - try the next segment
+ if x_comp > y_comp
+ return 1
+ elsif x_comp < y_comp
+ return -1
+ end
+
+ # if we've reached here than the segments are the same - try again
+ end
+
+ # we must have reached the end of one or both of the strings and they
+ # matched up until this point
+
+ # segments matched completely but the segment separators were different -
+ # rpm reference code treats these as equal.
+ if (x_pos == x_pos_max + 1) && (y_pos == y_pos_max + 1)
+ return 0
+ end
+
+ # the most unprocessed characters left wins
+ if (x_pos_max - x_pos) > (y_pos_max - y_pos)
+ return 1
+ else
+ return -1
+ end
+ end
+
+ end # self
+ end # RPMUtils
+
+ class RPMVersion
+ include Comparable
+
+ def initialize(*args)
+ if args.size == 1
+ @e, @v, @r = RPMUtils.version_parse(args[0])
+ elsif args.size == 3
+ @e = args[0].to_i
+ @v = args[1]
+ @r = args[2]
+ else
+ raise ArgumentError, "Expecting either 'epoch-version-release' or 'epoch, " \
+ "version, release'"
+ end
+ end
+ attr_reader :e, :v, :r
+ alias epoch e
+ alias version v
+ alias release r
+
+ def self.parse(*args)
+ new(*args)
+ end
+
+ def <=>(other)
+ compare_versions(other)
+ end
+
+ def compare(other)
+ compare_versions(other, false)
+ end
+
+ def partial_compare(other)
+ compare_versions(other, true)
+ end
+
+ # RPM::Version rpm_version_to_s equivalent
+ def to_s
+ if @r.nil?
+ @v
+ else
+ "#{@v}-#{@r}"
+ end
+ end
+
+ def evr
+ "#{@e}:#{@v}-#{@r}"
+ end
+
+ private
+
+ # Rough RPM::Version rpm_version_cmp equivalent - except much slower :)
+ #
+ # partial lets epoch and version segment equality be good enough to return equal, eg:
+ #
+ # 2:1.2-1 == 2:1.2
+ # 2:1.2-1 == 2:
+ #
+ def compare_versions(y, partial = false)
+ x = self
+
+ # compare epoch
+ if (x.e.nil? == false && x.e > 0) && y.e.nil?
+ return 1
+ elsif x.e.nil? && (y.e.nil? == false && y.e > 0)
+ return -1
+ elsif x.e.nil? == false && y.e.nil? == false
+ if x.e < y.e
+ return -1
+ elsif x.e > y.e
+ return 1
+ end
+ end
+
+ # compare version
+ if partial && (x.v.nil? || y.v.nil?)
+ return 0
+ elsif x.v.nil? == false && y.v.nil?
+ return 1
+ elsif x.v.nil? && y.v.nil? == false
+ return -1
+ elsif x.v.nil? == false && y.v.nil? == false
+ cmp = RPMUtils.rpmvercmp(x.v, y.v)
+ return cmp if cmp != 0
+ end
+
+ # compare release
+ if partial && (x.r.nil? || y.r.nil?)
+ return 0
+ elsif x.r.nil? == false && y.r.nil?
+ return 1
+ elsif x.r.nil? && y.r.nil? == false
+ return -1
+ elsif x.r.nil? == false && y.r.nil? == false
+ cmp = RPMUtils.rpmvercmp(x.r, y.r)
+ return cmp
+ end
+
+ 0
+ end
+ end
+
+ class RPMPackage
+ include Comparable
+
+ def initialize(*args)
+ if args.size == 4
+ @n = args[0]
+ @version = RPMVersion.new(args[1])
+ @a = args[2]
+ @provides = args[3]
+ elsif args.size == 6
+ @n = args[0]
+ e = args[1].to_i
+ v = args[2]
+ r = args[3]
+ @version = RPMVersion.new(e, v, r)
+ @a = args[4]
+ @provides = args[5]
+ else
+ raise ArgumentError, "Expecting either 'name, epoch-version-release, arch, provides' " \
+ "or 'name, epoch, version, release, arch, provides'"
+ end
+
+ # We always have one, ourselves!
+ if @provides.empty?
+ @provides = [ RPMProvide.new(@n, @version.evr, :==) ]
+ end
+ end
+ attr_reader :n, :a, :version, :provides
+ alias name n
+ alias arch a
+
+ def <=>(other)
+ compare(other)
+ end
+
+ def compare(y)
+ x = self
+
+ # easy! :)
+ return 0 if x.nevra == y.nevra
+
+ # compare name
+ if x.n.nil? == false && y.n.nil?
+ return 1
+ elsif x.n.nil? && y.n.nil? == false
+ return -1
+ elsif x.n.nil? == false && y.n.nil? == false
+ if x.n < y.n
+ return -1
+ elsif x.n > y.n
+ return 1
+ end
+ end
+
+ # compare version
+ if x.version > y.version
+ return 1
+ elsif x.version < y.version
+ return -1
+ end
+
+ # compare arch
+ if x.a.nil? == false && y.a.nil?
+ return 1
+ elsif x.a.nil? && y.a.nil? == false
+ return -1
+ elsif x.a.nil? == false && y.a.nil? == false
+ if x.a < y.a
+ return -1
+ elsif x.a > y.a
+ return 1
+ end
+ end
+
+ 0
+ end
+
+ def to_s
+ nevra
+ end
+
+ def nevra
+ "#{@n}-#{@version.evr}.#{@a}"
+ end
+ end
+
+ # Simple implementation from rpm and ruby-rpm reference code
+ class RPMDependency
+ def initialize(*args)
+ if args.size == 3
+ @name = args[0]
+ @version = RPMVersion.new(args[1])
+ # Our requirement to other dependencies
+ @flag = args[2] || :==
+ elsif args.size == 5
+ @name = args[0]
+ e = args[1].to_i
+ v = args[2]
+ r = args[3]
+ @version = RPMVersion.new(e, v, r)
+ @flag = args[4] || :==
+ else
+ raise ArgumentError, "Expecting either 'name, epoch-version-release, flag' or " \
+ "'name, epoch, version, release, flag'"
+ end
+ end
+ attr_reader :name, :version, :flag
+
+ # Parses 2 forms:
+ #
+ # "mtr >= 2:0.71-3.0"
+ # "mta"
+ def self.parse(string)
+ if /^(\S+)\s+(>|>=|=|==|<=|<)\s+(\S+)$/.match(string) # rubocop:disable Performance/RedundantMatch
+ name = $1
+ flag = if $2 == "="
+ :==
+ else
+ :"#{$2}"
+ end
+ version = $3
+
+ new(name, version, flag)
+ else
+ name = string
+ new(name, nil, nil)
+ end
+ end
+
+ # Test if another RPMDependency satisfies our requirements
+ def satisfy?(y)
+ unless y.is_a?(RPMDependency)
+ raise ArgumentError, "Expecting an RPMDependency object"
+ end
+
+ x = self
+
+ # Easy!
+ if x.name != y.name
+ return false
+ end
+
+ # Partial compare
+ #
+ # eg: x.version 2.3 == y.version 2.3-1
+ sense = x.version.partial_compare(y.version)
+
+ # Thanks to rpmdsCompare() rpmds.c
+ if (sense < 0) && ((x.flag == :> || x.flag == :>=) || (y.flag == :<= || y.flag == :<))
+ return true
+ elsif (sense > 0) && ((x.flag == :< || x.flag == :<=) || (y.flag == :>= || y.flag == :>))
+ return true
+ elsif sense == 0 && (
+ ((x.flag == :== || x.flag == :<= || x.flag == :>=) && (y.flag == :== || y.flag == :<= || y.flag == :>=)) ||
+ (x.flag == :< && y.flag == :<) ||
+ (x.flag == :> && y.flag == :>)
+ )
+ return true
+ end
+
+ false
+ end
+ end
+
+ class RPMProvide < RPMDependency; end
+ class RPMRequire < RPMDependency; end
+
+ class RPMDbPackage < RPMPackage
+ # <rpm parts>, installed, available
+ def initialize(*args)
+ @repoid = args.pop
+ # state
+ @available = args.pop
+ @installed = args.pop
+ super(*args)
+ end
+ attr_reader :repoid, :available, :installed
+ end
+
+ # Simple storage for RPMPackage objects - keeps them unique and sorted
+ class RPMDb
+ def initialize
+ # package name => [ RPMPackage, RPMPackage ] of different versions
+ @rpms = {}
+ # package nevra => RPMPackage for lookups
+ @index = {}
+ # provide name (aka feature) => [RPMPackage, RPMPackage] each providing this feature
+ @provides = {}
+ # RPMPackages listed as available
+ @available = Set.new
+ # RPMPackages listed as installed
+ @installed = Set.new
+ end
+
+ def [](package_name)
+ lookup(package_name)
+ end
+
+ # Lookup package_name and return a descending array of package objects
+ def lookup(package_name)
+ pkgs = @rpms[package_name]
+ if pkgs
+ return pkgs.sort.reverse
+ else
+ return nil
+ end
+ end
+
+ def lookup_provides(provide_name)
+ @provides[provide_name]
+ end
+
+ # Using the package name as a key, and nevra for an index, keep a unique list of packages.
+ # The available/installed state can be overwritten for existing packages.
+ def push(*args)
+ args.flatten.each do |new_rpm|
+ unless new_rpm.is_a?(RPMDbPackage)
+ raise ArgumentError, "Expecting an RPMDbPackage object"
+ end
+
+ @rpms[new_rpm.n] ||= []
+
+ # we may already have this one, like when the installed list is refreshed
+ idx = @index[new_rpm.nevra]
+ if idx
+ # grab the existing package if it's not
+ curr_rpm = idx
+ else
+ @rpms[new_rpm.n] << new_rpm
+
+ new_rpm.provides.each do |provide|
+ @provides[provide.name] ||= []
+ @provides[provide.name] << new_rpm
+ end
+
+ curr_rpm = new_rpm
+ end
+
+ # Track the nevra -> RPMPackage association to avoid having to compare versions
+ # with @rpms[new_rpm.n] on the next round
+ @index[new_rpm.nevra] = curr_rpm
+
+ # these are overwritten for existing packages
+ if new_rpm.available
+ @available << curr_rpm
+ end
+ if new_rpm.installed
+ @installed << curr_rpm
+ end
+ end
+ end
+
+ def <<(*args)
+ push(args)
+ end
+
+ def clear
+ @rpms.clear
+ @index.clear
+ @provides.clear
+ clear_available
+ clear_installed
+ end
+
+ def clear_available
+ @available.clear
+ end
+
+ def clear_installed
+ @installed.clear
+ end
+
+ def size
+ @rpms.size
+ end
+ alias length size
+
+ def available_size
+ @available.size
+ end
+
+ def installed_size
+ @installed.size
+ end
+
+ def available?(package)
+ @available.include?(package)
+ end
+
+ def installed?(package)
+ @installed.include?(package)
+ end
+
+ def whatprovides(rpmdep)
+ unless rpmdep.is_a?(RPMDependency)
+ raise ArgumentError, "Expecting an RPMDependency object"
+ end
+
+ what = []
+
+ packages = lookup_provides(rpmdep.name)
+ if packages
+ packages.each do |pkg|
+ pkg.provides.each do |provide|
+ if provide.satisfy?(rpmdep)
+ what << pkg
+ end
+ end
+ end
+ end
+
+ what
+ end
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/yum/simplejson/LICENSE.txt b/lib/chef/provider/package/yum/simplejson/LICENSE.txt
new file mode 100644
index 0000000000..e05f49c3fd
--- /dev/null
+++ b/lib/chef/provider/package/yum/simplejson/LICENSE.txt
@@ -0,0 +1,79 @@
+simplejson is dual-licensed software. It is available under the terms
+of the MIT license, or the Academic Free License version 2.1. The full
+text of each license agreement is included below. This code is also
+licensed to the Python Software Foundation (PSF) under a Contributor
+Agreement.
+
+MIT License
+===========
+
+Copyright (c) 2006 Bob Ippolito
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+Academic Free License v. 2.1
+============================
+
+Copyright (c) 2006 Bob Ippolito. All rights reserved.
+
+This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work:
+
+Licensed under the Academic Free License version 2.1
+
+1) Grant of Copyright License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license to do the following:
+
+a) to reproduce the Original Work in copies;
+
+b) to prepare derivative works ("Derivative Works") based upon the Original Work;
+
+c) to distribute copies of the Original Work and Derivative Works to the public;
+
+d) to perform the Original Work publicly; and
+
+e) to display the Original Work publicly.
+
+2) Grant of Patent License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, to make, use, sell and offer for sale the Original Work and Derivative Works.
+
+3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor hereby agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work, and by publishing the address of that information repository in a notice immediately following the copyright notice that applies to the Original Work.
+
+4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior written permission of the Licensor. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor except as expressly stated herein. No patent license is granted to make, use, sell or offer to sell embodiments of any patent claims other than the licensed claims defined in Section 2. No right is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any Original Work that Licensor otherwise would have a right to license.
+
+5) This section intentionally omitted.
+
+6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
+
+7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately proceeding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to Original Work is granted hereunder except under this disclaimer.
+
+8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to any person for any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to liability for death or personal injury resulting from Licensor's negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.
+
+9) Acceptance and Termination. If You distribute copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. Nothing else but this License (or another written agreement between Licensor and You) grants You permission to create Derivative Works based upon the Original Work or to exercise any of the rights granted in Section 1 herein, and any attempt to do so except under the terms of this License (or another written agreement between Licensor and You) is expressly prohibited by U.S. copyright law, the equivalent laws of other countries, and by international treaty. Therefore, by exercising any of the rights granted to You in Section 1 herein, You indicate Your acceptance of this License and all of its terms and conditions.
+
+10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
+
+11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et seq., the equivalent laws of other countries, and international treaty. This section shall survive the termination of this License.
+
+12) Attorneys Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
+
+13) Miscellaneous. This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
+
+14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
+
+This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved. Permission is hereby granted to copy and distribute this license without modification. This license may not be modified without the express written permission of its copyright owner.
diff --git a/lib/chef/provider/package/yum/simplejson/__init__.py b/lib/chef/provider/package/yum/simplejson/__init__.py
new file mode 100644
index 0000000000..d5b4d39913
--- /dev/null
+++ b/lib/chef/provider/package/yum/simplejson/__init__.py
@@ -0,0 +1,318 @@
+r"""JSON (JavaScript Object Notation) <http://json.org> is a subset of
+JavaScript syntax (ECMA-262 3rd edition) used as a lightweight data
+interchange format.
+
+:mod:`simplejson` exposes an API familiar to users of the standard library
+:mod:`marshal` and :mod:`pickle` modules. It is the externally maintained
+version of the :mod:`json` library contained in Python 2.6, but maintains
+compatibility with Python 2.4 and Python 2.5 and (currently) has
+significant performance advantages, even without using the optional C
+extension for speedups.
+
+Encoding basic Python object hierarchies::
+
+ >>> import simplejson as json
+ >>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
+ '["foo", {"bar": ["baz", null, 1.0, 2]}]'
+ >>> print json.dumps("\"foo\bar")
+ "\"foo\bar"
+ >>> print json.dumps(u'\u1234')
+ "\u1234"
+ >>> print json.dumps('\\')
+ "\\"
+ >>> print json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True)
+ {"a": 0, "b": 0, "c": 0}
+ >>> from StringIO import StringIO
+ >>> io = StringIO()
+ >>> json.dump(['streaming API'], io)
+ >>> io.getvalue()
+ '["streaming API"]'
+
+Compact encoding::
+
+ >>> import simplejson as json
+ >>> json.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
+ '[1,2,3,{"4":5,"6":7}]'
+
+Pretty printing::
+
+ >>> import simplejson as json
+ >>> s = json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4)
+ >>> print '\n'.join([l.rstrip() for l in s.splitlines()])
+ {
+ "4": 5,
+ "6": 7
+ }
+
+Decoding JSON::
+
+ >>> import simplejson as json
+ >>> obj = [u'foo', {u'bar': [u'baz', None, 1.0, 2]}]
+ >>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]') == obj
+ True
+ >>> json.loads('"\\"foo\\bar"') == u'"foo\x08ar'
+ True
+ >>> from StringIO import StringIO
+ >>> io = StringIO('["streaming API"]')
+ >>> json.load(io)[0] == 'streaming API'
+ True
+
+Specializing JSON object decoding::
+
+ >>> import simplejson as json
+ >>> def as_complex(dct):
+ ... if '__complex__' in dct:
+ ... return complex(dct['real'], dct['imag'])
+ ... return dct
+ ...
+ >>> json.loads('{"__complex__": true, "real": 1, "imag": 2}',
+ ... object_hook=as_complex)
+ (1+2j)
+ >>> import decimal
+ >>> json.loads('1.1', parse_float=decimal.Decimal) == decimal.Decimal('1.1')
+ True
+
+Specializing JSON object encoding::
+
+ >>> import simplejson as json
+ >>> def encode_complex(obj):
+ ... if isinstance(obj, complex):
+ ... return [obj.real, obj.imag]
+ ... raise TypeError(repr(o) + " is not JSON serializable")
+ ...
+ >>> json.dumps(2 + 1j, default=encode_complex)
+ '[2.0, 1.0]'
+ >>> json.JSONEncoder(default=encode_complex).encode(2 + 1j)
+ '[2.0, 1.0]'
+ >>> ''.join(json.JSONEncoder(default=encode_complex).iterencode(2 + 1j))
+ '[2.0, 1.0]'
+
+
+Using simplejson.tool from the shell to validate and pretty-print::
+
+ $ echo '{"json":"obj"}' | python -m simplejson.tool
+ {
+ "json": "obj"
+ }
+ $ echo '{ 1.2:3.4}' | python -m simplejson.tool
+ Expecting property name: line 1 column 2 (char 2)
+"""
+__version__ = '2.0.9'
+__all__ = [
+ 'dump', 'dumps', 'load', 'loads',
+ 'JSONDecoder', 'JSONEncoder',
+]
+
+__author__ = 'Bob Ippolito <bob@redivi.com>'
+
+from decoder import JSONDecoder
+from encoder import JSONEncoder
+
+_default_encoder = JSONEncoder(
+ skipkeys=False,
+ ensure_ascii=True,
+ check_circular=True,
+ allow_nan=True,
+ indent=None,
+ separators=None,
+ encoding='utf-8',
+ default=None,
+)
+
+def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
+ allow_nan=True, cls=None, indent=None, separators=None,
+ encoding='utf-8', default=None, **kw):
+ """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
+ ``.write()``-supporting file-like object).
+
+ If ``skipkeys`` is true then ``dict`` keys that are not basic types
+ (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
+ will be skipped instead of raising a ``TypeError``.
+
+ If ``ensure_ascii`` is false, then the some chunks written to ``fp``
+ may be ``unicode`` instances, subject to normal Python ``str`` to
+ ``unicode`` coercion rules. Unless ``fp.write()`` explicitly
+ understands ``unicode`` (as in ``codecs.getwriter()``) this is likely
+ to cause an error.
+
+ If ``check_circular`` is false, then the circular reference check
+ for container types will be skipped and a circular reference will
+ result in an ``OverflowError`` (or worse).
+
+ If ``allow_nan`` is false, then it will be a ``ValueError`` to
+ serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
+ in strict compliance of the JSON specification, instead of using the
+ JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
+
+ If ``indent`` is a non-negative integer, then JSON array elements and object
+ members will be pretty-printed with that indent level. An indent level
+ of 0 will only insert newlines. ``None`` is the most compact representation.
+
+ If ``separators`` is an ``(item_separator, dict_separator)`` tuple
+ then it will be used instead of the default ``(', ', ': ')`` separators.
+ ``(',', ':')`` is the most compact JSON representation.
+
+ ``encoding`` is the character encoding for str instances, default is UTF-8.
+
+ ``default(obj)`` is a function that should return a serializable version
+ of obj or raise TypeError. The default simply raises TypeError.
+
+ To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
+ ``.default()`` method to serialize additional types), specify it with
+ the ``cls`` kwarg.
+
+ """
+ # cached encoder
+ if (not skipkeys and ensure_ascii and
+ check_circular and allow_nan and
+ cls is None and indent is None and separators is None and
+ encoding == 'utf-8' and default is None and not kw):
+ iterable = _default_encoder.iterencode(obj)
+ else:
+ if cls is None:
+ cls = JSONEncoder
+ iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
+ check_circular=check_circular, allow_nan=allow_nan, indent=indent,
+ separators=separators, encoding=encoding,
+ default=default, **kw).iterencode(obj)
+ # could accelerate with writelines in some versions of Python, at
+ # a debuggability cost
+ for chunk in iterable:
+ fp.write(chunk)
+
+
+def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
+ allow_nan=True, cls=None, indent=None, separators=None,
+ encoding='utf-8', default=None, **kw):
+ """Serialize ``obj`` to a JSON formatted ``str``.
+
+ If ``skipkeys`` is false then ``dict`` keys that are not basic types
+ (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
+ will be skipped instead of raising a ``TypeError``.
+
+ If ``ensure_ascii`` is false, then the return value will be a
+ ``unicode`` instance subject to normal Python ``str`` to ``unicode``
+ coercion rules instead of being escaped to an ASCII ``str``.
+
+ If ``check_circular`` is false, then the circular reference check
+ for container types will be skipped and a circular reference will
+ result in an ``OverflowError`` (or worse).
+
+ If ``allow_nan`` is false, then it will be a ``ValueError`` to
+ serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
+ strict compliance of the JSON specification, instead of using the
+ JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
+
+ If ``indent`` is a non-negative integer, then JSON array elements and
+ object members will be pretty-printed with that indent level. An indent
+ level of 0 will only insert newlines. ``None`` is the most compact
+ representation.
+
+ If ``separators`` is an ``(item_separator, dict_separator)`` tuple
+ then it will be used instead of the default ``(', ', ': ')`` separators.
+ ``(',', ':')`` is the most compact JSON representation.
+
+ ``encoding`` is the character encoding for str instances, default is UTF-8.
+
+ ``default(obj)`` is a function that should return a serializable version
+ of obj or raise TypeError. The default simply raises TypeError.
+
+ To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
+ ``.default()`` method to serialize additional types), specify it with
+ the ``cls`` kwarg.
+
+ """
+ # cached encoder
+ if (not skipkeys and ensure_ascii and
+ check_circular and allow_nan and
+ cls is None and indent is None and separators is None and
+ encoding == 'utf-8' and default is None and not kw):
+ return _default_encoder.encode(obj)
+ if cls is None:
+ cls = JSONEncoder
+ return cls(
+ skipkeys=skipkeys, ensure_ascii=ensure_ascii,
+ check_circular=check_circular, allow_nan=allow_nan, indent=indent,
+ separators=separators, encoding=encoding, default=default,
+ **kw).encode(obj)
+
+
+_default_decoder = JSONDecoder(encoding=None, object_hook=None)
+
+
+def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None,
+ parse_int=None, parse_constant=None, **kw):
+ """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
+ a JSON document) to a Python object.
+
+ If the contents of ``fp`` is encoded with an ASCII based encoding other
+ than utf-8 (e.g. latin-1), then an appropriate ``encoding`` name must
+ be specified. Encodings that are not ASCII based (such as UCS-2) are
+ not allowed, and should be wrapped with
+ ``codecs.getreader(fp)(encoding)``, or simply decoded to a ``unicode``
+ object and passed to ``loads()``
+
+ ``object_hook`` is an optional function that will be called with the
+ result of any object literal decode (a ``dict``). The return value of
+ ``object_hook`` will be used instead of the ``dict``. This feature
+ can be used to implement custom decoders (e.g. JSON-RPC class hinting).
+
+ To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
+ kwarg.
+
+ """
+ return loads(fp.read(),
+ encoding=encoding, cls=cls, object_hook=object_hook,
+ parse_float=parse_float, parse_int=parse_int,
+ parse_constant=parse_constant, **kw)
+
+
+def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None,
+ parse_int=None, parse_constant=None, **kw):
+ """Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON
+ document) to a Python object.
+
+ If ``s`` is a ``str`` instance and is encoded with an ASCII based encoding
+ other than utf-8 (e.g. latin-1) then an appropriate ``encoding`` name
+ must be specified. Encodings that are not ASCII based (such as UCS-2)
+ are not allowed and should be decoded to ``unicode`` first.
+
+ ``object_hook`` is an optional function that will be called with the
+ result of any object literal decode (a ``dict``). The return value of
+ ``object_hook`` will be used instead of the ``dict``. This feature
+ can be used to implement custom decoders (e.g. JSON-RPC class hinting).
+
+ ``parse_float``, if specified, will be called with the string
+ of every JSON float to be decoded. By default this is equivalent to
+ float(num_str). This can be used to use another datatype or parser
+ for JSON floats (e.g. decimal.Decimal).
+
+ ``parse_int``, if specified, will be called with the string
+ of every JSON int to be decoded. By default this is equivalent to
+ int(num_str). This can be used to use another datatype or parser
+ for JSON integers (e.g. float).
+
+ ``parse_constant``, if specified, will be called with one of the
+ following strings: -Infinity, Infinity, NaN, null, true, false.
+ This can be used to raise an exception if invalid JSON numbers
+ are encountered.
+
+ To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
+ kwarg.
+
+ """
+ if (cls is None and encoding is None and object_hook is None and
+ parse_int is None and parse_float is None and
+ parse_constant is None and not kw):
+ return _default_decoder.decode(s)
+ if cls is None:
+ cls = JSONDecoder
+ if object_hook is not None:
+ kw['object_hook'] = object_hook
+ if parse_float is not None:
+ kw['parse_float'] = parse_float
+ if parse_int is not None:
+ kw['parse_int'] = parse_int
+ if parse_constant is not None:
+ kw['parse_constant'] = parse_constant
+ return cls(encoding=encoding, **kw).decode(s)
diff --git a/lib/chef/provider/package/yum/simplejson/__init__.pyc b/lib/chef/provider/package/yum/simplejson/__init__.pyc
new file mode 100644
index 0000000000..10679d3b04
--- /dev/null
+++ b/lib/chef/provider/package/yum/simplejson/__init__.pyc
Binary files differ
diff --git a/lib/chef/provider/package/yum/simplejson/decoder.py b/lib/chef/provider/package/yum/simplejson/decoder.py
new file mode 100644
index 0000000000..d921ce0b97
--- /dev/null
+++ b/lib/chef/provider/package/yum/simplejson/decoder.py
@@ -0,0 +1,354 @@
+"""Implementation of JSONDecoder
+"""
+import re
+import sys
+import struct
+
+from simplejson.scanner import make_scanner
+try:
+ from simplejson._speedups import scanstring as c_scanstring
+except ImportError:
+ c_scanstring = None
+
+__all__ = ['JSONDecoder']
+
+FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
+
+def _floatconstants():
+ _BYTES = '7FF80000000000007FF0000000000000'.decode('hex')
+ if sys.byteorder != 'big':
+ _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1]
+ nan, inf = struct.unpack('dd', _BYTES)
+ return nan, inf, -inf
+
+NaN, PosInf, NegInf = _floatconstants()
+
+
+def linecol(doc, pos):
+ lineno = doc.count('\n', 0, pos) + 1
+ if lineno == 1:
+ colno = pos
+ else:
+ colno = pos - doc.rindex('\n', 0, pos)
+ return lineno, colno
+
+
+def errmsg(msg, doc, pos, end=None):
+ # Note that this function is called from _speedups
+ lineno, colno = linecol(doc, pos)
+ if end is None:
+ #fmt = '{0}: line {1} column {2} (char {3})'
+ #return fmt.format(msg, lineno, colno, pos)
+ fmt = '%s: line %d column %d (char %d)'
+ return fmt % (msg, lineno, colno, pos)
+ endlineno, endcolno = linecol(doc, end)
+ #fmt = '{0}: line {1} column {2} - line {3} column {4} (char {5} - {6})'
+ #return fmt.format(msg, lineno, colno, endlineno, endcolno, pos, end)
+ fmt = '%s: line %d column %d - line %d column %d (char %d - %d)'
+ return fmt % (msg, lineno, colno, endlineno, endcolno, pos, end)
+
+
+_CONSTANTS = {
+ '-Infinity': NegInf,
+ 'Infinity': PosInf,
+ 'NaN': NaN,
+}
+
+STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)
+BACKSLASH = {
+ '"': u'"', '\\': u'\\', '/': u'/',
+ 'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t',
+}
+
+DEFAULT_ENCODING = "utf-8"
+
+def py_scanstring(s, end, encoding=None, strict=True, _b=BACKSLASH, _m=STRINGCHUNK.match):
+ """Scan the string s for a JSON string. End is the index of the
+ character in s after the quote that started the JSON string.
+ Unescapes all valid JSON string escape sequences and raises ValueError
+ on attempt to decode an invalid string. If strict is False then literal
+ control characters are allowed in the string.
+
+ Returns a tuple of the decoded string and the index of the character in s
+ after the end quote."""
+ if encoding is None:
+ encoding = DEFAULT_ENCODING
+ chunks = []
+ _append = chunks.append
+ begin = end - 1
+ while 1:
+ chunk = _m(s, end)
+ if chunk is None:
+ raise ValueError(
+ errmsg("Unterminated string starting at", s, begin))
+ end = chunk.end()
+ content, terminator = chunk.groups()
+ # Content is contains zero or more unescaped string characters
+ if content:
+ if not isinstance(content, unicode):
+ content = unicode(content, encoding)
+ _append(content)
+ # Terminator is the end of string, a literal control character,
+ # or a backslash denoting that an escape sequence follows
+ if terminator == '"':
+ break
+ elif terminator != '\\':
+ if strict:
+ msg = "Invalid control character %r at" % (terminator,)
+ #msg = "Invalid control character {0!r} at".format(terminator)
+ raise ValueError(errmsg(msg, s, end))
+ else:
+ _append(terminator)
+ continue
+ try:
+ esc = s[end]
+ except IndexError:
+ raise ValueError(
+ errmsg("Unterminated string starting at", s, begin))
+ # If not a unicode escape sequence, must be in the lookup table
+ if esc != 'u':
+ try:
+ char = _b[esc]
+ except KeyError:
+ msg = "Invalid \\escape: " + repr(esc)
+ raise ValueError(errmsg(msg, s, end))
+ end += 1
+ else:
+ # Unicode escape sequence
+ esc = s[end + 1:end + 5]
+ next_end = end + 5
+ if len(esc) != 4:
+ msg = "Invalid \\uXXXX escape"
+ raise ValueError(errmsg(msg, s, end))
+ uni = int(esc, 16)
+ # Check for surrogate pair on UCS-4 systems
+ if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535:
+ msg = "Invalid \\uXXXX\\uXXXX surrogate pair"
+ if not s[end + 5:end + 7] == '\\u':
+ raise ValueError(errmsg(msg, s, end))
+ esc2 = s[end + 7:end + 11]
+ if len(esc2) != 4:
+ raise ValueError(errmsg(msg, s, end))
+ uni2 = int(esc2, 16)
+ uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
+ next_end += 6
+ char = unichr(uni)
+ end = next_end
+ # Append the unescaped character
+ _append(char)
+ return u''.join(chunks), end
+
+
+# Use speedup if available
+scanstring = c_scanstring or py_scanstring
+
+WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS)
+WHITESPACE_STR = ' \t\n\r'
+
+def JSONObject((s, end), encoding, strict, scan_once, object_hook, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
+ pairs = {}
+ # Use a slice to prevent IndexError from being raised, the following
+ # check will raise a more specific ValueError if the string is empty
+ nextchar = s[end:end + 1]
+ # Normally we expect nextchar == '"'
+ if nextchar != '"':
+ if nextchar in _ws:
+ end = _w(s, end).end()
+ nextchar = s[end:end + 1]
+ # Trivial empty object
+ if nextchar == '}':
+ return pairs, end + 1
+ elif nextchar != '"':
+ raise ValueError(errmsg("Expecting property name", s, end))
+ end += 1
+ while True:
+ key, end = scanstring(s, end, encoding, strict)
+
+ # To skip some function call overhead we optimize the fast paths where
+ # the JSON key separator is ": " or just ":".
+ if s[end:end + 1] != ':':
+ end = _w(s, end).end()
+ if s[end:end + 1] != ':':
+ raise ValueError(errmsg("Expecting : delimiter", s, end))
+
+ end += 1
+
+ try:
+ if s[end] in _ws:
+ end += 1
+ if s[end] in _ws:
+ end = _w(s, end + 1).end()
+ except IndexError:
+ pass
+
+ try:
+ value, end = scan_once(s, end)
+ except StopIteration:
+ raise ValueError(errmsg("Expecting object", s, end))
+ pairs[key] = value
+
+ try:
+ nextchar = s[end]
+ if nextchar in _ws:
+ end = _w(s, end + 1).end()
+ nextchar = s[end]
+ except IndexError:
+ nextchar = ''
+ end += 1
+
+ if nextchar == '}':
+ break
+ elif nextchar != ',':
+ raise ValueError(errmsg("Expecting , delimiter", s, end - 1))
+
+ try:
+ nextchar = s[end]
+ if nextchar in _ws:
+ end += 1
+ nextchar = s[end]
+ if nextchar in _ws:
+ end = _w(s, end + 1).end()
+ nextchar = s[end]
+ except IndexError:
+ nextchar = ''
+
+ end += 1
+ if nextchar != '"':
+ raise ValueError(errmsg("Expecting property name", s, end - 1))
+
+ if object_hook is not None:
+ pairs = object_hook(pairs)
+ return pairs, end
+
+def JSONArray((s, end), scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
+ values = []
+ nextchar = s[end:end + 1]
+ if nextchar in _ws:
+ end = _w(s, end + 1).end()
+ nextchar = s[end:end + 1]
+ # Look-ahead for trivial empty array
+ if nextchar == ']':
+ return values, end + 1
+ _append = values.append
+ while True:
+ try:
+ value, end = scan_once(s, end)
+ except StopIteration:
+ raise ValueError(errmsg("Expecting object", s, end))
+ _append(value)
+ nextchar = s[end:end + 1]
+ if nextchar in _ws:
+ end = _w(s, end + 1).end()
+ nextchar = s[end:end + 1]
+ end += 1
+ if nextchar == ']':
+ break
+ elif nextchar != ',':
+ raise ValueError(errmsg("Expecting , delimiter", s, end))
+
+ try:
+ if s[end] in _ws:
+ end += 1
+ if s[end] in _ws:
+ end = _w(s, end + 1).end()
+ except IndexError:
+ pass
+
+ return values, end
+
+class JSONDecoder(object):
+ """Simple JSON <http://json.org> decoder
+
+ Performs the following translations in decoding by default:
+
+ +---------------+-------------------+
+ | JSON | Python |
+ +===============+===================+
+ | object | dict |
+ +---------------+-------------------+
+ | array | list |
+ +---------------+-------------------+
+ | string | unicode |
+ +---------------+-------------------+
+ | number (int) | int, long |
+ +---------------+-------------------+
+ | number (real) | float |
+ +---------------+-------------------+
+ | true | True |
+ +---------------+-------------------+
+ | false | False |
+ +---------------+-------------------+
+ | null | None |
+ +---------------+-------------------+
+
+ It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as
+ their corresponding ``float`` values, which is outside the JSON spec.
+
+ """
+
+ def __init__(self, encoding=None, object_hook=None, parse_float=None,
+ parse_int=None, parse_constant=None, strict=True):
+ """``encoding`` determines the encoding used to interpret any ``str``
+ objects decoded by this instance (utf-8 by default). It has no
+ effect when decoding ``unicode`` objects.
+
+ Note that currently only encodings that are a superset of ASCII work,
+ strings of other encodings should be passed in as ``unicode``.
+
+ ``object_hook``, if specified, will be called with the result
+ of every JSON object decoded and its return value will be used in
+ place of the given ``dict``. This can be used to provide custom
+ deserializations (e.g. to support JSON-RPC class hinting).
+
+ ``parse_float``, if specified, will be called with the string
+ of every JSON float to be decoded. By default this is equivalent to
+ float(num_str). This can be used to use another datatype or parser
+ for JSON floats (e.g. decimal.Decimal).
+
+ ``parse_int``, if specified, will be called with the string
+ of every JSON int to be decoded. By default this is equivalent to
+ int(num_str). This can be used to use another datatype or parser
+ for JSON integers (e.g. float).
+
+ ``parse_constant``, if specified, will be called with one of the
+ following strings: -Infinity, Infinity, NaN.
+ This can be used to raise an exception if invalid JSON numbers
+ are encountered.
+
+ """
+ self.encoding = encoding
+ self.object_hook = object_hook
+ self.parse_float = parse_float or float
+ self.parse_int = parse_int or int
+ self.parse_constant = parse_constant or _CONSTANTS.__getitem__
+ self.strict = strict
+ self.parse_object = JSONObject
+ self.parse_array = JSONArray
+ self.parse_string = scanstring
+ self.scan_once = make_scanner(self)
+
+ def decode(self, s, _w=WHITESPACE.match):
+ """Return the Python representation of ``s`` (a ``str`` or ``unicode``
+ instance containing a JSON document)
+
+ """
+ obj, end = self.raw_decode(s, idx=_w(s, 0).end())
+ end = _w(s, end).end()
+ if end != len(s):
+ raise ValueError(errmsg("Extra data", s, end, len(s)))
+ return obj
+
+ def raw_decode(self, s, idx=0):
+ """Decode a JSON document from ``s`` (a ``str`` or ``unicode`` beginning
+ with a JSON document) and return a 2-tuple of the Python
+ representation and the index in ``s`` where the document ended.
+
+ This can be used to decode a JSON document from a string that may
+ have extraneous data at the end.
+
+ """
+ try:
+ obj, end = self.scan_once(s, idx)
+ except StopIteration:
+ raise ValueError("No JSON object could be decoded")
+ return obj, end
diff --git a/lib/chef/provider/package/yum/simplejson/decoder.pyc b/lib/chef/provider/package/yum/simplejson/decoder.pyc
new file mode 100644
index 0000000000..d402901870
--- /dev/null
+++ b/lib/chef/provider/package/yum/simplejson/decoder.pyc
Binary files differ
diff --git a/lib/chef/provider/package/yum/simplejson/encoder.py b/lib/chef/provider/package/yum/simplejson/encoder.py
new file mode 100644
index 0000000000..cf58290366
--- /dev/null
+++ b/lib/chef/provider/package/yum/simplejson/encoder.py
@@ -0,0 +1,440 @@
+"""Implementation of JSONEncoder
+"""
+import re
+
+try:
+ from simplejson._speedups import encode_basestring_ascii as c_encode_basestring_ascii
+except ImportError:
+ c_encode_basestring_ascii = None
+try:
+ from simplejson._speedups import make_encoder as c_make_encoder
+except ImportError:
+ c_make_encoder = None
+
+ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]')
+ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
+HAS_UTF8 = re.compile(r'[\x80-\xff]')
+ESCAPE_DCT = {
+ '\\': '\\\\',
+ '"': '\\"',
+ '\b': '\\b',
+ '\f': '\\f',
+ '\n': '\\n',
+ '\r': '\\r',
+ '\t': '\\t',
+}
+for i in range(0x20):
+ #ESCAPE_DCT.setdefault(chr(i), '\\u{0:04x}'.format(i))
+ ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
+
+# Assume this produces an infinity on all machines (probably not guaranteed)
+INFINITY = float('1e66666')
+FLOAT_REPR = repr
+
+def encode_basestring(s):
+ """Return a JSON representation of a Python string
+
+ """
+ def replace(match):
+ return ESCAPE_DCT[match.group(0)]
+ return '"' + ESCAPE.sub(replace, s) + '"'
+
+
+def py_encode_basestring_ascii(s):
+ """Return an ASCII-only JSON representation of a Python string
+
+ """
+ if isinstance(s, str) and HAS_UTF8.search(s) is not None:
+ s = s.decode('utf-8')
+ def replace(match):
+ s = match.group(0)
+ try:
+ return ESCAPE_DCT[s]
+ except KeyError:
+ n = ord(s)
+ if n < 0x10000:
+ #return '\\u{0:04x}'.format(n)
+ return '\\u%04x' % (n,)
+ else:
+ # surrogate pair
+ n -= 0x10000
+ s1 = 0xd800 | ((n >> 10) & 0x3ff)
+ s2 = 0xdc00 | (n & 0x3ff)
+ #return '\\u{0:04x}\\u{1:04x}'.format(s1, s2)
+ return '\\u%04x\\u%04x' % (s1, s2)
+ return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
+
+
+encode_basestring_ascii = c_encode_basestring_ascii or py_encode_basestring_ascii
+
+class JSONEncoder(object):
+ """Extensible JSON <http://json.org> encoder for Python data structures.
+
+ Supports the following objects and types by default:
+
+ +-------------------+---------------+
+ | Python | JSON |
+ +===================+===============+
+ | dict | object |
+ +-------------------+---------------+
+ | list, tuple | array |
+ +-------------------+---------------+
+ | str, unicode | string |
+ +-------------------+---------------+
+ | int, long, float | number |
+ +-------------------+---------------+
+ | True | true |
+ +-------------------+---------------+
+ | False | false |
+ +-------------------+---------------+
+ | None | null |
+ +-------------------+---------------+
+
+ To extend this to recognize other objects, subclass and implement a
+ ``.default()`` method with another method that returns a serializable
+ object for ``o`` if possible, otherwise it should call the superclass
+ implementation (to raise ``TypeError``).
+
+ """
+ item_separator = ', '
+ key_separator = ': '
+ def __init__(self, skipkeys=False, ensure_ascii=True,
+ check_circular=True, allow_nan=True, sort_keys=False,
+ indent=None, separators=None, encoding='utf-8', default=None):
+ """Constructor for JSONEncoder, with sensible defaults.
+
+ If skipkeys is false, then it is a TypeError to attempt
+ encoding of keys that are not str, int, long, float or None. If
+ skipkeys is True, such items are simply skipped.
+
+ If ensure_ascii is true, the output is guaranteed to be str
+ objects with all incoming unicode characters escaped. If
+ ensure_ascii is false, the output will be unicode object.
+
+ If check_circular is true, then lists, dicts, and custom encoded
+ objects will be checked for circular references during encoding to
+ prevent an infinite recursion (which would cause an OverflowError).
+ Otherwise, no such check takes place.
+
+ If allow_nan is true, then NaN, Infinity, and -Infinity will be
+ encoded as such. This behavior is not JSON specification compliant,
+ but is consistent with most JavaScript based encoders and decoders.
+ Otherwise, it will be a ValueError to encode such floats.
+
+ If sort_keys is true, then the output of dictionaries will be
+ sorted by key; this is useful for regression tests to ensure
+ that JSON serializations can be compared on a day-to-day basis.
+
+ If indent is a non-negative integer, then JSON array
+ elements and object members will be pretty-printed with that
+ indent level. An indent level of 0 will only insert newlines.
+ None is the most compact representation.
+
+ If specified, separators should be a (item_separator, key_separator)
+ tuple. The default is (', ', ': '). To get the most compact JSON
+ representation you should specify (',', ':') to eliminate whitespace.
+
+ If specified, default is a function that gets called for objects
+ that can't otherwise be serialized. It should return a JSON encodable
+ version of the object or raise a ``TypeError``.
+
+ If encoding is not None, then all input strings will be
+ transformed into unicode using that encoding prior to JSON-encoding.
+ The default is UTF-8.
+
+ """
+
+ self.skipkeys = skipkeys
+ self.ensure_ascii = ensure_ascii
+ self.check_circular = check_circular
+ self.allow_nan = allow_nan
+ self.sort_keys = sort_keys
+ self.indent = indent
+ if separators is not None:
+ self.item_separator, self.key_separator = separators
+ if default is not None:
+ self.default = default
+ self.encoding = encoding
+
+ def default(self, o):
+ """Implement this method in a subclass such that it returns
+ a serializable object for ``o``, or calls the base implementation
+ (to raise a ``TypeError``).
+
+ For example, to support arbitrary iterators, you could
+ implement default like this::
+
+ def default(self, o):
+ try:
+ iterable = iter(o)
+ except TypeError:
+ pass
+ else:
+ return list(iterable)
+ return JSONEncoder.default(self, o)
+
+ """
+ raise TypeError(repr(o) + " is not JSON serializable")
+
+ def encode(self, o):
+ """Return a JSON string representation of a Python data structure.
+
+ >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
+ '{"foo": ["bar", "baz"]}'
+
+ """
+ # This is for extremely simple cases and benchmarks.
+ if isinstance(o, basestring):
+ if isinstance(o, str):
+ _encoding = self.encoding
+ if (_encoding is not None
+ and not (_encoding == 'utf-8')):
+ o = o.decode(_encoding)
+ if self.ensure_ascii:
+ return encode_basestring_ascii(o)
+ else:
+ return encode_basestring(o)
+ # This doesn't pass the iterator directly to ''.join() because the
+ # exceptions aren't as detailed. The list call should be roughly
+ # equivalent to the PySequence_Fast that ''.join() would do.
+ chunks = self.iterencode(o, _one_shot=True)
+ if not isinstance(chunks, (list, tuple)):
+ chunks = list(chunks)
+ return ''.join(chunks)
+
+ def iterencode(self, o, _one_shot=False):
+ """Encode the given object and yield each string
+ representation as available.
+
+ For example::
+
+ for chunk in JSONEncoder().iterencode(bigobject):
+ mysocket.write(chunk)
+
+ """
+ if self.check_circular:
+ markers = {}
+ else:
+ markers = None
+ if self.ensure_ascii:
+ _encoder = encode_basestring_ascii
+ else:
+ _encoder = encode_basestring
+ if self.encoding != 'utf-8':
+ def _encoder(o, _orig_encoder=_encoder, _encoding=self.encoding):
+ if isinstance(o, str):
+ o = o.decode(_encoding)
+ return _orig_encoder(o)
+
+ def floatstr(o, allow_nan=self.allow_nan, _repr=FLOAT_REPR, _inf=INFINITY, _neginf=-INFINITY):
+ # Check for specials. Note that this type of test is processor- and/or
+ # platform-specific, so do tests which don't depend on the internals.
+
+ if o != o:
+ text = 'NaN'
+ elif o == _inf:
+ text = 'Infinity'
+ elif o == _neginf:
+ text = '-Infinity'
+ else:
+ return _repr(o)
+
+ if not allow_nan:
+ raise ValueError(
+ "Out of range float values are not JSON compliant: " +
+ repr(o))
+
+ return text
+
+
+ if _one_shot and c_make_encoder is not None and not self.indent and not self.sort_keys:
+ _iterencode = c_make_encoder(
+ markers, self.default, _encoder, self.indent,
+ self.key_separator, self.item_separator, self.sort_keys,
+ self.skipkeys, self.allow_nan)
+ else:
+ _iterencode = _make_iterencode(
+ markers, self.default, _encoder, self.indent, floatstr,
+ self.key_separator, self.item_separator, self.sort_keys,
+ self.skipkeys, _one_shot)
+ return _iterencode(o, 0)
+
+def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot,
+ ## HACK: hand-optimized bytecode; turn globals into locals
+ False=False,
+ True=True,
+ ValueError=ValueError,
+ basestring=basestring,
+ dict=dict,
+ float=float,
+ id=id,
+ int=int,
+ isinstance=isinstance,
+ list=list,
+ long=long,
+ str=str,
+ tuple=tuple,
+ ):
+
+ def _iterencode_list(lst, _current_indent_level):
+ if not lst:
+ yield '[]'
+ return
+ if markers is not None:
+ markerid = id(lst)
+ if markerid in markers:
+ raise ValueError("Circular reference detected")
+ markers[markerid] = lst
+ buf = '['
+ if _indent is not None:
+ _current_indent_level += 1
+ newline_indent = '\n' + (' ' * (_indent * _current_indent_level))
+ separator = _item_separator + newline_indent
+ buf += newline_indent
+ else:
+ newline_indent = None
+ separator = _item_separator
+ first = True
+ for value in lst:
+ if first:
+ first = False
+ else:
+ buf = separator
+ if isinstance(value, basestring):
+ yield buf + _encoder(value)
+ elif value is None:
+ yield buf + 'null'
+ elif value is True:
+ yield buf + 'true'
+ elif value is False:
+ yield buf + 'false'
+ elif isinstance(value, (int, long)):
+ yield buf + str(value)
+ elif isinstance(value, float):
+ yield buf + _floatstr(value)
+ else:
+ yield buf
+ if isinstance(value, (list, tuple)):
+ chunks = _iterencode_list(value, _current_indent_level)
+ elif isinstance(value, dict):
+ chunks = _iterencode_dict(value, _current_indent_level)
+ else:
+ chunks = _iterencode(value, _current_indent_level)
+ for chunk in chunks:
+ yield chunk
+ if newline_indent is not None:
+ _current_indent_level -= 1
+ yield '\n' + (' ' * (_indent * _current_indent_level))
+ yield ']'
+ if markers is not None:
+ del markers[markerid]
+
+ def _iterencode_dict(dct, _current_indent_level):
+ if not dct:
+ yield '{}'
+ return
+ if markers is not None:
+ markerid = id(dct)
+ if markerid in markers:
+ raise ValueError("Circular reference detected")
+ markers[markerid] = dct
+ yield '{'
+ if _indent is not None:
+ _current_indent_level += 1
+ newline_indent = '\n' + (' ' * (_indent * _current_indent_level))
+ item_separator = _item_separator + newline_indent
+ yield newline_indent
+ else:
+ newline_indent = None
+ item_separator = _item_separator
+ first = True
+ if _sort_keys:
+ items = dct.items()
+ items.sort(key=lambda kv: kv[0])
+ else:
+ items = dct.iteritems()
+ for key, value in items:
+ if isinstance(key, basestring):
+ pass
+ # JavaScript is weakly typed for these, so it makes sense to
+ # also allow them. Many encoders seem to do something like this.
+ elif isinstance(key, float):
+ key = _floatstr(key)
+ elif key is True:
+ key = 'true'
+ elif key is False:
+ key = 'false'
+ elif key is None:
+ key = 'null'
+ elif isinstance(key, (int, long)):
+ key = str(key)
+ elif _skipkeys:
+ continue
+ else:
+ raise TypeError("key " + repr(key) + " is not a string")
+ if first:
+ first = False
+ else:
+ yield item_separator
+ yield _encoder(key)
+ yield _key_separator
+ if isinstance(value, basestring):
+ yield _encoder(value)
+ elif value is None:
+ yield 'null'
+ elif value is True:
+ yield 'true'
+ elif value is False:
+ yield 'false'
+ elif isinstance(value, (int, long)):
+ yield str(value)
+ elif isinstance(value, float):
+ yield _floatstr(value)
+ else:
+ if isinstance(value, (list, tuple)):
+ chunks = _iterencode_list(value, _current_indent_level)
+ elif isinstance(value, dict):
+ chunks = _iterencode_dict(value, _current_indent_level)
+ else:
+ chunks = _iterencode(value, _current_indent_level)
+ for chunk in chunks:
+ yield chunk
+ if newline_indent is not None:
+ _current_indent_level -= 1
+ yield '\n' + (' ' * (_indent * _current_indent_level))
+ yield '}'
+ if markers is not None:
+ del markers[markerid]
+
+ def _iterencode(o, _current_indent_level):
+ if isinstance(o, basestring):
+ yield _encoder(o)
+ elif o is None:
+ yield 'null'
+ elif o is True:
+ yield 'true'
+ elif o is False:
+ yield 'false'
+ elif isinstance(o, (int, long)):
+ yield str(o)
+ elif isinstance(o, float):
+ yield _floatstr(o)
+ elif isinstance(o, (list, tuple)):
+ for chunk in _iterencode_list(o, _current_indent_level):
+ yield chunk
+ elif isinstance(o, dict):
+ for chunk in _iterencode_dict(o, _current_indent_level):
+ yield chunk
+ else:
+ if markers is not None:
+ markerid = id(o)
+ if markerid in markers:
+ raise ValueError("Circular reference detected")
+ markers[markerid] = o
+ o = _default(o)
+ for chunk in _iterencode(o, _current_indent_level):
+ yield chunk
+ if markers is not None:
+ del markers[markerid]
+
+ return _iterencode
diff --git a/lib/chef/provider/package/yum/simplejson/encoder.pyc b/lib/chef/provider/package/yum/simplejson/encoder.pyc
new file mode 100644
index 0000000000..207bce5cfb
--- /dev/null
+++ b/lib/chef/provider/package/yum/simplejson/encoder.pyc
Binary files differ
diff --git a/lib/chef/provider/package/yum/simplejson/scanner.py b/lib/chef/provider/package/yum/simplejson/scanner.py
new file mode 100644
index 0000000000..adbc6ec979
--- /dev/null
+++ b/lib/chef/provider/package/yum/simplejson/scanner.py
@@ -0,0 +1,65 @@
+"""JSON token scanner
+"""
+import re
+try:
+ from simplejson._speedups import make_scanner as c_make_scanner
+except ImportError:
+ c_make_scanner = None
+
+__all__ = ['make_scanner']
+
+NUMBER_RE = re.compile(
+ r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?',
+ (re.VERBOSE | re.MULTILINE | re.DOTALL))
+
+def py_make_scanner(context):
+ parse_object = context.parse_object
+ parse_array = context.parse_array
+ parse_string = context.parse_string
+ match_number = NUMBER_RE.match
+ encoding = context.encoding
+ strict = context.strict
+ parse_float = context.parse_float
+ parse_int = context.parse_int
+ parse_constant = context.parse_constant
+ object_hook = context.object_hook
+
+ def _scan_once(string, idx):
+ try:
+ nextchar = string[idx]
+ except IndexError:
+ raise StopIteration
+
+ if nextchar == '"':
+ return parse_string(string, idx + 1, encoding, strict)
+ elif nextchar == '{':
+ return parse_object((string, idx + 1), encoding, strict, _scan_once, object_hook)
+ elif nextchar == '[':
+ return parse_array((string, idx + 1), _scan_once)
+ elif nextchar == 'n' and string[idx:idx + 4] == 'null':
+ return None, idx + 4
+ elif nextchar == 't' and string[idx:idx + 4] == 'true':
+ return True, idx + 4
+ elif nextchar == 'f' and string[idx:idx + 5] == 'false':
+ return False, idx + 5
+
+ m = match_number(string, idx)
+ if m is not None:
+ integer, frac, exp = m.groups()
+ if frac or exp:
+ res = parse_float(integer + (frac or '') + (exp or ''))
+ else:
+ res = parse_int(integer)
+ return res, m.end()
+ elif nextchar == 'N' and string[idx:idx + 3] == 'NaN':
+ return parse_constant('NaN'), idx + 3
+ elif nextchar == 'I' and string[idx:idx + 8] == 'Infinity':
+ return parse_constant('Infinity'), idx + 8
+ elif nextchar == '-' and string[idx:idx + 9] == '-Infinity':
+ return parse_constant('-Infinity'), idx + 9
+ else:
+ raise StopIteration
+
+ return _scan_once
+
+make_scanner = c_make_scanner or py_make_scanner
diff --git a/lib/chef/provider/package/yum/simplejson/scanner.pyc b/lib/chef/provider/package/yum/simplejson/scanner.pyc
new file mode 100644
index 0000000000..12df070e44
--- /dev/null
+++ b/lib/chef/provider/package/yum/simplejson/scanner.pyc
Binary files differ
diff --git a/lib/chef/provider/package/yum/simplejson/tool.py b/lib/chef/provider/package/yum/simplejson/tool.py
new file mode 100644
index 0000000000..90443317b2
--- /dev/null
+++ b/lib/chef/provider/package/yum/simplejson/tool.py
@@ -0,0 +1,37 @@
+r"""Command-line tool to validate and pretty-print JSON
+
+Usage::
+
+ $ echo '{"json":"obj"}' | python -m simplejson.tool
+ {
+ "json": "obj"
+ }
+ $ echo '{ 1.2:3.4}' | python -m simplejson.tool
+ Expecting property name: line 1 column 2 (char 2)
+
+"""
+import sys
+import simplejson
+
+def main():
+ if len(sys.argv) == 1:
+ infile = sys.stdin
+ outfile = sys.stdout
+ elif len(sys.argv) == 2:
+ infile = open(sys.argv[1], 'rb')
+ outfile = sys.stdout
+ elif len(sys.argv) == 3:
+ infile = open(sys.argv[1], 'rb')
+ outfile = open(sys.argv[2], 'wb')
+ else:
+ raise SystemExit(sys.argv[0] + " [infile [outfile]]")
+ try:
+ obj = simplejson.load(infile)
+ except ValueError, e:
+ raise SystemExit(e)
+ simplejson.dump(obj, outfile, sort_keys=True, indent=4)
+ outfile.write('\n')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/lib/chef/provider/package/yum/version.rb b/lib/chef/provider/package/yum/version.rb
new file mode 100644
index 0000000000..b19f52fe09
--- /dev/null
+++ b/lib/chef/provider/package/yum/version.rb
@@ -0,0 +1,56 @@
+#
+# Copyright:: Copyright 2016-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ class Provider
+ class Package
+ class Yum < Chef::Provider::Package
+
+ # helper class to assist in passing around name/version/arch triples
+ class Version
+ attr_accessor :name
+ attr_accessor :version
+ attr_accessor :arch
+
+ def initialize(name, version, arch)
+ @name = name
+ @version = version
+ @arch = arch
+ end
+
+ def to_s
+ "#{name}-#{version}.#{arch}" unless version.nil?
+ end
+
+ def version_with_arch
+ "#{version}.#{arch}" unless version.nil?
+ end
+
+ def matches_name_and_arch?(other)
+ other.version == version && other.arch == arch
+ end
+
+ def ==(other)
+ name == other.name && version == other.version && arch == other.arch
+ end
+
+ alias eql? ==
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/yum/yum_cache.rb b/lib/chef/provider/package/yum/yum_cache.rb
new file mode 100644
index 0000000000..fa0930109f
--- /dev/null
+++ b/lib/chef/provider/package/yum/yum_cache.rb
@@ -0,0 +1,93 @@
+
+# Author:: Adam Jacob (<adam@chef.io>)
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/provider/package/yum/python_helper"
+require "chef/provider/package"
+require "singleton"
+
+#
+# These are largely historical APIs, the YumCache object no longer exists and this is a
+# fascade over the python helper class. It should be considered deprecated-lite and
+# no new APIs should be added and should be added to the python_helper instead.
+#
+
+class Chef
+ class Provider
+ class Package
+ class Yum < Chef::Provider::Package
+ class YumCache
+ include Singleton
+
+ def refresh
+ python_helper.restart
+ end
+
+ def reload
+ python_helper.restart
+ end
+
+ def reload_installed
+ python_helper.restart
+ end
+
+ def reload_provides
+ python_helper.restart
+ end
+
+ def reset
+ python_helper.restart
+ end
+
+ def reset_installed
+ python_helper.restart
+ end
+
+ def available_version(name, arch = nil)
+ p = python_helper.package_query(:whatavailable, name, arch: arch)
+ "#{p.version}.#{p.arch}" unless p.version.nil?
+ end
+
+ def installed_version(name, arch = nil)
+ p = python_helper.package_query(:whatinstalled, name, arch: arch)
+ "#{p.version}.#{p.arch}" unless p.version.nil?
+ end
+
+ def package_available?(name, arch = nil)
+ p = python_helper.package_query(:whatavailable, name, arch: arch)
+ !p.version.nil?
+ end
+
+ # NOTE that it is the responsibility of the python_helper to get these APIs correct and
+ # we do not do any validation here that the e.g. version or arch matches the requested value
+ # (because the bigger issue there is a buggy+broken python_helper -- so don't try to fix those
+ # kinds of bugs here)
+ def version_available?(name, version, arch = nil)
+ p = python_helper.package_query(:whatavailable, name, version: version, arch: arch)
+ !p.version.nil?
+ end
+
+ # @api private
+ def python_helper
+ @python_helper ||= PythonHelper.instance
+ end
+
+ end # YumCache
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/yum/yum_helper.py b/lib/chef/provider/package/yum/yum_helper.py
new file mode 100644
index 0000000000..d2c04c72db
--- /dev/null
+++ b/lib/chef/provider/package/yum/yum_helper.py
@@ -0,0 +1,210 @@
+#!/usr/bin/env python3
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
+
+#
+# NOTE: this actually needs to run under python2.4 and centos 5.x through python3 and centos 7.x
+# please manually test changes on centos5 boxes or you will almost certainly break things.
+#
+
+import sys
+import yum
+import signal
+import os
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'simplejson'))
+try: import json
+except ImportError: import simplejson as json
+import re
+from rpmUtils.miscutils import stringToVersion,compareEVR
+from rpmUtils.arch import getBaseArch, getArchList
+
+
+try: from yum.misc import string_to_prco_tuple
+except ImportError:
+ # RHEL5 compat
+ def string_to_prco_tuple(prcoString):
+ prco_split = prcoString.split()
+ n, f, v = prco_split
+ (prco_e, prco_v, prco_r) = stringToVersion(v)
+ return (n, f, (prco_e, prco_v, prco_r))
+
+# hack to work around https://github.com/chef/chef/issues/7126
+# see https://bugzilla.redhat.com/show_bug.cgi?id=1396248
+if not hasattr(yum.packages.FakeRepository, 'compare_providers_priority'):
+ yum.packages.FakeRepository.compare_providers_priority = 99
+
+base = None
+
+def get_base():
+ global base
+ if base is None:
+ base = yum.YumBase()
+ setup_exit_handler()
+ return base
+
+def versioncompare(versions):
+ arch_list = getArchList()
+ candidate_arch1 = versions[0].split(".")[-1]
+ candidate_arch2 = versions[1].split(".")[-1]
+
+ # The first version number passed to this method is always a valid nevra (the current version)
+ # If the second version number looks like it does not contain a valid arch
+ # then we'll chop the arch component (assuming it *is* a valid one) from the first version string
+ # so we're only comparing the evr portions.
+ if (candidate_arch2 not in arch_list) and (candidate_arch1 in arch_list):
+ final_version1 = versions[0].replace("." + candidate_arch1,"")
+ else:
+ final_version1 = versions[0]
+
+ final_version2 = versions[1]
+
+ (e1, v1, r1) = stringToVersion(final_version1)
+ (e2, v2, r2) = stringToVersion(final_version2)
+
+ evr_comparison = compareEVR((e1, v1, r1), (e2, v2, r2))
+ outpipe.write("%(e)s\n" % { 'e': evr_comparison })
+ outpipe.flush()
+
+def install_only_packages(name):
+ base = get_base()
+ if name in base.conf.installonlypkgs:
+ outpipe.write('True')
+ else:
+ outpipe.write('False')
+ outpipe.flush()
+
+# python2.4 / centos5 compat
+try:
+ any
+except NameError:
+ def any(s):
+ for v in s:
+ if v:
+ return True
+ return False
+
+def query(command):
+ base = get_base()
+
+ enabled_repos = base.repos.listEnabled()
+
+ # Handle any repocontrols passed in with our options
+
+ if 'repos' in command:
+ for repo in command['repos']:
+ if 'enable' in repo:
+ base.repos.enableRepo(repo['enable'])
+ if 'disable' in repo:
+ base.repos.disableRepo(repo['disable'])
+
+ args = { 'name': command['provides'] }
+ do_nevra = False
+ if 'epoch' in command:
+ args['epoch'] = command['epoch']
+ do_nevra = True
+ if 'version' in command:
+ args['ver'] = command['version']
+ do_nevra = True
+ if 'release' in command:
+ args['rel'] = command['release']
+ do_nevra = True
+ if 'arch' in command:
+ desired_arch = command['arch']
+ args['arch'] = command['arch']
+ do_nevra = True
+ else:
+ desired_arch = getBaseArch()
+
+ obj = None
+ if command['action'] == "whatinstalled":
+ obj = base.rpmdb
+ else:
+ obj = base.pkgSack
+
+ # if we are given "name == 1.2.3" then we must use the getProvides() API.
+ # - this means that we ignore arch and version properties when given prco tuples as a package_name
+ # - in order to fix this, something would have to happen where getProvides was called first and
+ # then the result was searchNevra'd. please be extremely careful if attempting to fix that
+ # since searchNevra does not support prco tuples.
+ if any(elem in command['provides'] for elem in r"<=>"):
+ # handles flags (<, >, =, etc) and versions, but no wildcareds
+ pkgs = obj.getProvides(*string_to_prco_tuple(command['provides']))
+ elif do_nevra:
+ # now if we're given version or arch properties explicitly, then we do a SearchNevra.
+ # - this means that wildcard version in the package_name with an arch property will not work correctly
+ # - again don't try to fix this just by pushing bugs around in the code, you would need to call
+ # returnPackages and searchProvides and then apply the Nevra filters to those results.
+ pkgs = obj.searchNevra(**args)
+ if (command['action'] == "whatinstalled") and (not pkgs):
+ pkgs = obj.searchNevra(name=args['name'], arch=desired_arch)
+ else:
+ pats = [command['provides']]
+ pkgs = obj.returnPackages(patterns=pats)
+
+ if not pkgs:
+ # handles wildcards
+ pkgs = obj.searchProvides(command['provides'])
+
+ if not pkgs:
+ outpipe.write(command['provides'].split().pop(0)+' nil nil\n')
+ outpipe.flush()
+ else:
+ # make sure we picked the package with the highest version
+ pkgs = base.bestPackagesFromList(pkgs,single_name=True)
+ pkg = pkgs.pop(0)
+ outpipe.write("%(n)s %(e)s:%(v)s-%(r)s %(a)s\n" % { 'n': pkg.name, 'e': pkg.epoch, 'v': pkg.version, 'r': pkg.release, 'a': pkg.arch })
+ outpipe.flush()
+
+ # Reset any repos we were passed in enablerepo/disablerepo to the original state in enabled_repos
+ if 'repos' in command:
+ for repo in command['repos']:
+ if 'enable' in repo:
+ if base.repos.getRepo(repo['enable']) not in enabled_repos:
+ base.repos.disableRepo(repo['enable'])
+ if 'disable' in repo:
+ if base.repos.getRepo(repo['disable']) in enabled_repos:
+ base.repos.enableRepo(repo['disable'])
+
+# the design of this helper is that it should try to be 'brittle' and fail hard and exit in order
+# to keep process tables clean. additional error handling should probably be added to the retry loop
+# on the ruby side.
+def exit_handler(signal, frame):
+ base.closeRpmDB()
+ sys.exit(0)
+
+def setup_exit_handler():
+ signal.signal(signal.SIGINT, exit_handler)
+ signal.signal(signal.SIGHUP, exit_handler)
+ signal.signal(signal.SIGPIPE, exit_handler)
+ signal.signal(signal.SIGQUIT, exit_handler)
+
+if len(sys.argv) < 3:
+ inpipe = sys.stdin
+ outpipe = sys.stdout
+else:
+ inpipe = os.fdopen(int(sys.argv[1]), "r")
+ outpipe = os.fdopen(int(sys.argv[2]), "w")
+
+while 1:
+ # kill self if we get orphaned (tragic)
+ ppid = os.getppid()
+ if ppid == 1:
+ sys.exit(0)
+ setup_exit_handler()
+ line = inpipe.readline()
+
+ try:
+ command = json.loads(line)
+ except ValueError, e:
+ base.closeRpmDB()
+ sys.exit(0)
+
+ if command['action'] == "whatinstalled":
+ query(command)
+ elif command['action'] == "whatavailable":
+ query(command)
+ elif command['action'] == "versioncompare":
+ versioncompare(command['versions'])
+ elif command['action'] == "installonlypkgs":
+ install_only_packages(command['package'])
+ else:
+ raise RuntimeError("bad command")
diff --git a/lib/chef/provider/package/zypper.rb b/lib/chef/provider/package/zypper.rb
index 5ee1dbea8e..c2638fbfc1 100644
--- a/lib/chef/provider/package/zypper.rb
+++ b/lib/chef/provider/package/zypper.rb
@@ -2,7 +2,7 @@
#
# Authors:: Adam Jacob (<adam@chef.io>)
# Ionuț Arțăriși (<iartarisi@suse.cz>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# Copyright 2013-2016, SUSE Linux GmbH
# License:: Apache License, Version 2.0
#
@@ -29,24 +29,24 @@ class Chef
use_multipackage_api
provides :package, platform_family: "suse"
- provides :zypper_package, os: "linux"
+ provides :zypper_package
def get_versions(package_name)
candidate_version = current_version = nil
is_installed = false
- Chef::Log.debug("#{new_resource} checking zypper")
- status = shell_out_with_timeout!("zypper --non-interactive info #{package_name}")
+ logger.trace("#{new_resource} checking zypper")
+ status = shell_out_compact_timeout!("zypper", "--non-interactive", "info", package_name)
status.stdout.each_line do |line|
case line
- when /^Version: (.+)$/
- candidate_version = $1
- Chef::Log.debug("#{new_resource} version #{$1}")
- when /^Installed: Yes$/
+ when /^Version *: (.+) *$/
+ candidate_version = $1.strip
+ logger.trace("#{new_resource} version #{candidate_version}")
+ when /^Installed *: Yes.*$/ # http://rubular.com/r/9StcAMjOn6
is_installed = true
- Chef::Log.debug("#{new_resource} is installed")
- when /^Status: out-of-date \(version (.+) installed\)$/
- current_version = $1
- Chef::Log.debug("#{new_resource} out of date version #{$1}")
+ logger.trace("#{new_resource} is installed")
+ when /^Status *: out-of-date \(version (.+) installed\) *$/
+ current_version = $1.strip
+ logger.trace("#{new_resource} out of date version #{current_version}")
end
end
current_version = candidate_version if is_installed
@@ -75,6 +75,24 @@ class Chef
end
end
+ def packages_all_locked?(names, versions)
+ names.all? { |n| locked_packages.include? n }
+ end
+
+ def packages_all_unlocked?(names, versions)
+ names.all? { |n| !locked_packages.include? n }
+ end
+
+ def locked_packages
+ @locked_packages ||=
+ begin
+ locked = shell_out_compact_timeout!("zypper", "locks")
+ locked.stdout.each_line.map do |line|
+ line.split("|").shift(2).last.strip
+ end
+ end
+ end
+
def load_current_resource
@current_resource = Chef::Resource::ZypperPackage.new(new_resource.name)
current_resource.package_name(new_resource.package_name)
@@ -91,7 +109,7 @@ class Chef
end
def install_package(name, version)
- zypper_package("install --auto-agree-with-licenses", name, version)
+ zypper_package("install", *options, "--auto-agree-with-licenses", allow_downgrade, name, version)
end
def upgrade_package(name, version)
@@ -100,11 +118,19 @@ class Chef
end
def remove_package(name, version)
- zypper_package("remove", name, version)
+ zypper_package("remove", *options, name, version)
end
def purge_package(name, version)
- zypper_package("remove --clean-deps", name, version)
+ zypper_package("remove", *options, "--clean-deps", name, version)
+ end
+
+ def lock_package(name, version)
+ zypper_package("addlock", *options, name, version)
+ end
+
+ def unlock_package(name, version)
+ zypper_package("removelock", *options, name, version)
end
private
@@ -115,27 +141,21 @@ class Chef
end
end
- def zypper_package(command, names, versions)
+ def zypper_package(command, *options, names, versions)
zipped_names = zip(names, versions)
if zypper_version < 1.0
- shell_out_with_timeout!(a_to_s("zypper", gpg_checks, command, "-y", names))
+ shell_out_compact_timeout!("zypper", gpg_checks, command, *options, "-y", names)
else
- shell_out_with_timeout!(a_to_s("zypper --non-interactive", gpg_checks, command, zipped_names))
+ shell_out_compact_timeout!("zypper", "--non-interactive", gpg_checks, command, *options, zipped_names)
end
end
- def gpg_checks()
- case Chef::Config[:zypper_check_gpg]
- when true
- ""
- when false
- "--no-gpg-checks"
- when nil
- Chef::Log.warn("Chef::Config[:zypper_check_gpg] was not set. " +
- "All packages will be installed without gpg signature checks. " +
- "This is a security hazard.")
- "--no-gpg-checks"
- end
+ def gpg_checks
+ "--no-gpg-checks" unless new_resource.gpg_check
+ end
+
+ def allow_downgrade
+ "--oldpackage" if new_resource.allow_downgrade
end
end
end
diff --git a/lib/chef/provider/powershell_script.rb b/lib/chef/provider/powershell_script.rb
index 6365f6a171..5af73b8b69 100644
--- a/lib/chef/provider/powershell_script.rb
+++ b/lib/chef/provider/powershell_script.rb
@@ -23,7 +23,7 @@ class Chef
class Provider
class PowershellScript < Chef::Provider::WindowsScript
- provides :powershell_script, os: "windows"
+ provides :powershell_script
def initialize(new_resource, run_context)
super(new_resource, run_context, ".ps1")
@@ -36,7 +36,7 @@ class Chef
end
def command
- basepath = is_forced_32bit ? wow64_directory : run_context.node.kernel.os_info.system_directory
+ basepath = is_forced_32bit ? wow64_directory : run_context.node["kernel"]["os_info"]["system_directory"]
# Powershell.exe is always in "v1.0" folder (for backwards compatibility)
interpreter_path = Chef::Util::PathHelper.join(basepath, "WindowsPowerShell", "v1.0", interpreter)
@@ -60,8 +60,8 @@ class Chef
def flags
interpreter_flags = [*default_interpreter_flags].join(" ")
- if ! (@new_resource.flags.nil?)
- interpreter_flags = [@new_resource.flags, interpreter_flags].join(" ")
+ if ! (new_resource.flags.nil?)
+ interpreter_flags = [new_resource.flags, interpreter_flags].join(" ")
end
interpreter_flags
@@ -73,8 +73,8 @@ class Chef
# special handling to cover common use cases.
def add_exit_status_wrapper
self.code = wrapper_script
- Chef::Log.debug("powershell_script provider called with script code:\n\n#{@new_resource.code}\n")
- Chef::Log.debug("powershell_script provider will execute transformed code:\n\n#{self.code}\n")
+ logger.trace("powershell_script provider called with script code:\n\n#{new_resource.code}\n")
+ logger.trace("powershell_script provider will execute transformed code:\n\n#{code}\n")
end
def validate_script_syntax!
@@ -87,7 +87,7 @@ class Chef
# actually running the script.
user_code_wrapped_in_powershell_script_block = <<-EOH
{
- #{@new_resource.code}
+ #{new_resource.code}
}
EOH
user_script_file.puts user_code_wrapped_in_powershell_script_block
@@ -149,6 +149,14 @@ EOH
<<-EOH
# Chef Client wrapper for powershell_script resources
+# In rare cases, such as when PowerShell is executed
+# as an alternate user, the new-variable cmdlet is not
+# available, so import it just in case
+if ( get-module -ListAvailable Microsoft.PowerShell.Utility )
+{
+ Import-Module Microsoft.PowerShell.Utility
+}
+
# LASTEXITCODE can be uninitialized -- make it explictly 0
# to avoid incorrect detection of failure (non-zero) codes
$global:LASTEXITCODE = 0
@@ -159,7 +167,7 @@ $global:LASTEXITCODE = 0
trap [Exception] {write-error ($_.Exception.Message);exit 1}
# Variable state that should not be accessible to the user code
-new-variable -name interpolatedexitcode -visibility private -value $#{@new_resource.convert_boolean_return}
+new-variable -name interpolatedexitcode -visibility private -value $#{new_resource.convert_boolean_return}
new-variable -name chefscriptresult -visibility private
# Initialize a variable we use to capture $? inside a block
@@ -168,7 +176,7 @@ $global:lastcmdlet = $null
# Execute the user's code in a script block --
$chefscriptresult =
{
- #{@new_resource.code}
+ #{new_resource.code}
# This assignment doesn't affect the block's return value
$global:lastcmdlet = $?
diff --git a/lib/chef/provider/reboot.rb b/lib/chef/provider/reboot.rb
index 34eee9236d..f054af0567 100644
--- a/lib/chef/provider/reboot.rb
+++ b/lib/chef/provider/reboot.rb
@@ -21,39 +21,47 @@ require "chef/provider"
class Chef
class Provider
+ # Use the reboot resource to reboot a node, a necessary step with some
+ # installations on certain platforms. This resource is supported for use on
+ # the Microsoft Windows, macOS, and Linux platforms.
+ #
+ # In using this resource via notifications, it's important to *only* use
+ # immediate notifications. Delayed notifications produce unintuitive and
+ # probably undesired results.
+ #
+ # @since 12.0.0
class Reboot < Chef::Provider
provides :reboot
- def whyrun_supported?
- true
- end
-
+ # @return [void]
def load_current_resource
- @current_resource ||= Chef::Resource::Reboot.new(@new_resource.name)
- @current_resource.reason(@new_resource.reason)
- @current_resource.delay_mins(@new_resource.delay_mins)
- @current_resource
+ @current_resource ||= Chef::Resource::Reboot.new(new_resource.name)
+ current_resource.reason(new_resource.reason)
+ current_resource.delay_mins(new_resource.delay_mins)
+ current_resource
end
+ # add a reboot to the node run_context
+ # @return [void]
def request_reboot
node.run_context.request_reboot(
- :delay_mins => @new_resource.delay_mins,
- :reason => @new_resource.reason,
+ :delay_mins => new_resource.delay_mins,
+ :reason => new_resource.reason,
:timestamp => Time.now,
- :requested_by => @new_resource.name
+ :requested_by => new_resource.name
)
end
def action_request_reboot
converge_by("request a system reboot to occur if the run succeeds") do
- Chef::Log.warn "Reboot requested:'#{@new_resource.name}'"
+ logger.warn "Reboot requested:'#{new_resource.name}'"
request_reboot
end
end
def action_reboot_now
converge_by("rebooting the system immediately") do
- Chef::Log.warn "Rebooting system immediately, requested by '#{@new_resource.name}'"
+ logger.warn "Rebooting system immediately, requested by '#{new_resource.name}'"
request_reboot
throw :end_client_run_early
end
@@ -61,7 +69,7 @@ class Chef
def action_cancel
converge_by("cancel any existing end-of-run reboot request") do
- Chef::Log.warn "Reboot canceled: '#{@new_resource.name}'"
+ logger.warn "Reboot canceled: '#{new_resource.name}'"
node.run_context.cancel_reboot
end
end
diff --git a/lib/chef/provider/registry_key.rb b/lib/chef/provider/registry_key.rb
index e516433ac8..a4a0465e11 100644
--- a/lib/chef/provider/registry_key.rb
+++ b/lib/chef/provider/registry_key.rb
@@ -2,7 +2,7 @@
# Author:: Prajakta Purohit (<prajakta@chef.io>)
# Author:: Lamont Granquist (<lamont@chef.io>)
#
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright 2011-2017, Chef Software Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -35,10 +35,6 @@ class Chef
include Chef::Mixin::Checksum
- def whyrun_supported?
- true
- end
-
def running_on_windows!
unless Chef::Platform.windows?
raise Chef::Exceptions::Win32NotWindows, "Attempt to manipulate the windows registry on a non-windows node"
@@ -47,19 +43,19 @@ class Chef
def load_current_resource
running_on_windows!
- @current_resource ||= Chef::Resource::RegistryKey.new(@new_resource.key, run_context)
- @current_resource.key(@new_resource.key)
- @current_resource.architecture(@new_resource.architecture)
- @current_resource.recursive(@new_resource.recursive)
- if registry.key_exists?(@new_resource.key)
- @current_resource.values(registry.get_values(@new_resource.key))
- end
- values_to_hash(@current_resource.unscrubbed_values)
- @current_resource
+ @current_resource ||= Chef::Resource::RegistryKey.new(new_resource.key, run_context)
+ current_resource.key(new_resource.key)
+ current_resource.architecture(new_resource.architecture)
+ current_resource.recursive(new_resource.recursive)
+ if registry.key_exists?(new_resource.key)
+ current_resource.values(registry.get_values(new_resource.key))
+ end
+ values_to_hash(current_resource.unscrubbed_values)
+ current_resource
end
def registry
- @registry ||= Chef::Win32::Registry.new(@run_context, @new_resource.architecture)
+ @registry ||= Chef::Win32::Registry.new(@run_context, new_resource.architecture)
end
def values_to_hash(values)
@@ -70,72 +66,111 @@ class Chef
end
end
+ def key_missing?(values, name)
+ values.each do |v|
+ return true unless v.has_key?(name)
+ end
+ false
+ end
+
def define_resource_requirements
requirements.assert(:create, :create_if_missing, :delete, :delete_key) do |a|
- a.assertion { registry.hive_exists?(@new_resource.key) }
- a.failure_message(Chef::Exceptions::Win32RegHiveMissing, "Hive #{@new_resource.key.split("\\").shift} does not exist")
+ a.assertion { registry.hive_exists?(new_resource.key) }
+ a.failure_message(Chef::Exceptions::Win32RegHiveMissing, "Hive #{new_resource.key.split("\\").shift} does not exist")
end
+
requirements.assert(:create) do |a|
- a.assertion { registry.key_exists?(@new_resource.key) }
- a.whyrun("Key #{@new_resource.key} does not exist. Unless it would have been created before, attempt to modify its values would fail.")
+ a.assertion { registry.key_exists?(new_resource.key) }
+ a.whyrun("Key #{new_resource.key} does not exist. Unless it would have been created before, attempt to modify its values would fail.")
end
+
requirements.assert(:create, :create_if_missing) do |a|
- #If keys missing in the path and recursive == false
- a.assertion { !registry.keys_missing?(@current_resource.key) || @new_resource.recursive }
+ # If keys missing in the path and recursive == false
+ a.assertion { !registry.keys_missing?(current_resource.key) || new_resource.recursive }
a.failure_message(Chef::Exceptions::Win32RegNoRecursive, "Intermediate keys missing but recursive is set to false")
- a.whyrun("Intermediate keys in #{@new_resource.key} do not exist. Unless they would have been created earlier, attempt to modify them would fail.")
+ a.whyrun("Intermediate keys in #{new_resource.key} do not exist. Unless they would have been created earlier, attempt to modify them would fail.")
end
+
requirements.assert(:delete_key) do |a|
- #If key to be deleted has subkeys but recurssive == false
- a.assertion { !registry.key_exists?(@new_resource.key) || !registry.has_subkeys?(@new_resource.key) || @new_resource.recursive }
- a.failure_message(Chef::Exceptions::Win32RegNoRecursive, "#{@new_resource.key} has subkeys but recursive is set to false.")
- a.whyrun("#{@current_resource.key} has subkeys, but recursive is set to false. attempt to delete would fails unless subkeys were deleted prior to this action.")
+ # If key to be deleted has subkeys but recurssive == false
+ a.assertion { !registry.key_exists?(new_resource.key) || !registry.has_subkeys?(new_resource.key) || new_resource.recursive }
+ a.failure_message(Chef::Exceptions::Win32RegNoRecursive, "#{new_resource.key} has subkeys but recursive is set to false.")
+ a.whyrun("#{current_resource.key} has subkeys, but recursive is set to false. attempt to delete would fails unless subkeys were deleted prior to this action.")
+ end
+
+ requirements.assert(:create, :create_if_missing) do |a|
+ # If type key missing in the RegistryKey values hash
+ a.assertion { !key_missing?(new_resource.values, :type) }
+ a.failure_message(Chef::Exceptions::RegKeyValuesTypeMissing, "Missing type key in RegistryKey values hash")
+ a.whyrun("Type key does not exist. Attempt would fail unless the complete values hash containing all the keys does not exist for registry_key resource's create action.")
+ end
+
+ requirements.assert(:create, :create_if_missing) do |a|
+ # If data key missing in the RegistryKey values hash
+ a.assertion { !key_missing?(new_resource.values, :data) }
+ a.failure_message(Chef::Exceptions::RegKeyValuesDataMissing, "Missing data key in RegistryKey values hash")
+ a.whyrun("Data key does not exist. Attempt would fail unless the complete values hash containing all the keys does not exist for registry_key resource's create action.")
end
end
def action_create
- unless registry.key_exists?(@current_resource.key)
- converge_by("create key #{@new_resource.key}") do
- registry.create_key(@new_resource.key, @new_resource.recursive)
+ unless registry.key_exists?(current_resource.key)
+ converge_by("create key #{new_resource.key}") do
+ registry.create_key(new_resource.key, new_resource.recursive)
end
end
- @new_resource.unscrubbed_values.each do |value|
+ new_resource.unscrubbed_values.each do |value|
if @name_hash.has_key?(value[:name].downcase)
current_value = @name_hash[value[:name].downcase]
+ if [:dword, :dword_big_endian, :qword].include? value[:type]
+ value[:data] = value[:data].to_i
+ end
unless current_value[:type] == value[:type] && current_value[:data] == value[:data]
- converge_by("set value #{value}") do
- registry.set_value(@new_resource.key, value)
+ converge_by_value = value
+ converge_by_value[:data] = "*sensitive value suppressed*" if new_resource.sensitive
+
+ converge_by("set value #{converge_by_value}") do
+ registry.set_value(new_resource.key, value)
end
end
else
- converge_by("set value #{value}") do
- registry.set_value(@new_resource.key, value)
+ converge_by_value = value
+ converge_by_value[:data] = "*sensitive value suppressed*" if new_resource.sensitive
+
+ converge_by("set value #{converge_by_value}") do
+ registry.set_value(new_resource.key, value)
end
end
end
end
def action_create_if_missing
- unless registry.key_exists?(@new_resource.key)
- converge_by("create key #{@new_resource.key}") do
- registry.create_key(@new_resource.key, @new_resource.recursive)
+ unless registry.key_exists?(new_resource.key)
+ converge_by("create key #{new_resource.key}") do
+ registry.create_key(new_resource.key, new_resource.recursive)
end
end
- @new_resource.unscrubbed_values.each do |value|
+ new_resource.unscrubbed_values.each do |value|
unless @name_hash.has_key?(value[:name].downcase)
- converge_by("create value #{value}") do
- registry.set_value(@new_resource.key, value)
+ converge_by_value = value
+ converge_by_value[:data] = "*sensitive value suppressed*" if new_resource.sensitive
+
+ converge_by("create value #{converge_by_value}") do
+ registry.set_value(new_resource.key, value)
end
end
end
end
def action_delete
- if registry.key_exists?(@new_resource.key)
- @new_resource.unscrubbed_values.each do |value|
+ if registry.key_exists?(new_resource.key)
+ new_resource.unscrubbed_values.each do |value|
if @name_hash.has_key?(value[:name].downcase)
- converge_by("delete value #{value}") do
- registry.delete_value(@new_resource.key, value)
+ converge_by_value = value
+ converge_by_value[:data] = "*sensitive value suppressed*" if new_resource.sensitive
+
+ converge_by("delete value #{converge_by_value}") do
+ registry.delete_value(new_resource.key, value)
end
end
end
@@ -143,9 +178,9 @@ class Chef
end
def action_delete_key
- if registry.key_exists?(@new_resource.key)
- converge_by("delete key #{@new_resource.key}") do
- registry.delete_key(@new_resource.key, @new_resource.recursive)
+ if registry.key_exists?(new_resource.key)
+ converge_by("delete key #{new_resource.key}") do
+ registry.delete_key(new_resource.key, new_resource.recursive)
end
end
end
diff --git a/lib/chef/provider/remote_directory.rb b/lib/chef/provider/remote_directory.rb
index e3bc579107..94de68c557 100644
--- a/lib/chef/provider/remote_directory.rb
+++ b/lib/chef/provider/remote_directory.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,8 +23,6 @@ require "chef/resource/cookbook_file"
require "chef/mixin/file_class"
require "chef/platform/query_helpers"
require "chef/util/path_helper"
-require "chef/deprecation/warnings"
-require "chef/deprecation/provider/remote_directory"
require "forwardable"
@@ -36,9 +34,9 @@ class Chef
provides :remote_directory
- def_delegators :@new_resource, :purge, :path, :source, :cookbook, :cookbook_name
- def_delegators :@new_resource, :files_rights, :files_mode, :files_group, :files_owner, :files_backup
- def_delegators :@new_resource, :rights, :mode, :group, :owner
+ def_delegators :new_resource, :purge, :path, :source, :cookbook, :cookbook_name
+ def_delegators :new_resource, :files_rights, :files_mode, :files_group, :files_owner, :files_backup
+ def_delegators :new_resource, :rights, :mode, :group, :owner
# The overwrite property on the resource. Delegates to new_resource but can be mutated.
#
@@ -49,8 +47,6 @@ class Chef
!!@overwrite
end
- attr_accessor :managed_files
-
# Hash containing keys of the paths for all the files that we sync, plus all their
# parent directories.
#
@@ -155,7 +151,7 @@ class Chef
#
# FIXME: it should do breadth-first, see CHEF-5080 (please use a performant sort)
#
- # @return Array<String> The list of files to transfer
+ # @return [Array<String>] The list of files to transfer
# @api private
#
def files_to_transfer
@@ -209,6 +205,8 @@ class Chef
def cookbook_file_resource(target_path, relative_source_path)
res = Chef::Resource::CookbookFile.new(target_path, run_context)
res.cookbook_name = resource_cookbook
+ # Set the sensitivity level
+ res.sensitive(new_resource.sensitive)
res.source(::File.join(source, relative_source_path))
if Chef::Platform.windows? && files_rights
files_rights.each_pair do |permission, *args|
@@ -266,16 +264,6 @@ class Chef
res
end
- #
- # Add back deprecated methods and aliases that are internally unused and should be removed in Chef-13
- #
- extend Chef::Deprecation::Warnings
- include Chef::Deprecation::Provider::RemoteDirectory
- add_deprecation_warnings_for(Chef::Deprecation::Provider::RemoteDirectory.instance_methods)
-
- alias_method :resource_for_directory, :directory_resource
- add_deprecation_warnings_for([:resource_for_directory])
-
end
end
end
diff --git a/lib/chef/provider/remote_file.rb b/lib/chef/provider/remote_file.rb
index 9207e62ac6..d2de3d0b5f 100644
--- a/lib/chef/provider/remote_file.rb
+++ b/lib/chef/provider/remote_file.rb
@@ -18,33 +18,46 @@
#
require "chef/provider/file"
-require "chef/deprecation/provider/remote_file"
-require "chef/deprecation/warnings"
class Chef
class Provider
class RemoteFile < Chef::Provider::File
provides :remote_file
- extend Chef::Deprecation::Warnings
- include Chef::Deprecation::Provider::RemoteFile
- add_deprecation_warnings_for(Chef::Deprecation::Provider::RemoteFile.instance_methods)
-
def initialize(new_resource, run_context)
@content_class = Chef::Provider::RemoteFile::Content
super
end
+ def define_resource_requirements
+ [ new_resource.remote_user, new_resource.remote_domain,
+ new_resource.remote_password ].each do |prop|
+ requirements.assert(:all_actions) do |a|
+ a.assertion do
+ if prop
+ node[:platform_family] == "windows"
+ else
+ true
+ end
+ end
+ a.failure_message Chef::Exceptions::UnsupportedPlatform, "'remote_user', 'remote_domain' and 'remote_password' properties are supported only for Windows platform"
+ a.whyrun("Assuming that the platform is Windows while passing 'remote_user', 'remote_domain' and 'remote_password' properties")
+ end
+ end
+
+ super
+ end
+
def load_current_resource
- @current_resource = Chef::Resource::RemoteFile.new(@new_resource.name)
+ @current_resource = Chef::Resource::RemoteFile.new(new_resource.name)
super
end
private
def managing_content?
- return true if @new_resource.checksum
- return true if !@new_resource.source.nil? && @action != :create_if_missing
+ return true if new_resource.checksum
+ return true if !new_resource.source.nil? && @action != :create_if_missing
false
end
diff --git a/lib/chef/provider/remote_file/cache_control_data.rb b/lib/chef/provider/remote_file/cache_control_data.rb
index 8d7de5c370..32c1542d57 100644
--- a/lib/chef/provider/remote_file/cache_control_data.rb
+++ b/lib/chef/provider/remote_file/cache_control_data.rb
@@ -153,7 +153,7 @@ class Chef
if Chef::FileCache.has_key?(old_path)
# We found an old cache control data file. We started using sha256 instead of md5
# to name these. Upgrade the file to the new name.
- Chef::Log.debug("Found old cache control data file at #{old_path}. Moving to #{path}.")
+ Chef::Log.trace("Found old cache control data file at #{old_path}. Moving to #{path}.")
Chef::FileCache.load(old_path).tap do |data|
Chef::FileCache.store(path, data)
Chef::FileCache.delete(old_path)
diff --git a/lib/chef/provider/remote_file/content.rb b/lib/chef/provider/remote_file/content.rb
index e44096428b..4cf2c32287 100644
--- a/lib/chef/provider/remote_file/content.rb
+++ b/lib/chef/provider/remote_file/content.rb
@@ -32,10 +32,10 @@ class Chef
include Chef::Mixin::Uris
def file_for_provider
- Chef::Log.debug("#{@new_resource} checking for changes")
+ logger.trace("#{@new_resource} checking for changes")
if current_resource_matches_target_checksum?
- Chef::Log.debug("#{@new_resource} checksum matches target checksum (#{@new_resource.checksum}) - not updating")
+ logger.trace("#{@new_resource} checksum matches target checksum (#{@new_resource.checksum}) - not updating")
else
sources = @new_resource.source
raw_file = try_multiple_sources(sources)
@@ -54,10 +54,10 @@ class Chef
as_uri(source)
end
raw_file = grab_file_from_uri(uri)
- rescue SocketError, Errno::ECONNREFUSED, Errno::ENOENT, Errno::EACCES, Timeout::Error, Net::HTTPServerException, Net::HTTPFatalError, Net::FTPError => e
- Chef::Log.warn("#{@new_resource} cannot be downloaded from #{source}: #{e}")
+ rescue SocketError, Errno::ECONNREFUSED, Errno::ENOENT, Errno::EACCES, Timeout::Error, Net::HTTPServerException, Net::HTTPFatalError, Net::FTPError, Errno::ETIMEDOUT => e
+ logger.warn("#{@new_resource} cannot be downloaded from #{source}: #{e}")
if source = sources.shift
- Chef::Log.info("#{@new_resource} trying to download from another mirror")
+ logger.info("#{@new_resource} trying to download from another mirror")
retry
else
raise e
diff --git a/lib/chef/provider/remote_file/fetcher.rb b/lib/chef/provider/remote_file/fetcher.rb
index 563d135d6a..3011dd80a0 100644
--- a/lib/chef/provider/remote_file/fetcher.rb
+++ b/lib/chef/provider/remote_file/fetcher.rb
@@ -24,6 +24,9 @@ class Chef
def self.for_resource(uri, new_resource, current_resource)
if network_share?(uri)
+ if !Chef::Platform.windows?
+ raise Exceptions::UnsupportedPlatform, "Fetching the file on a network share is supported only on the Windows platform. Please change your source: #{uri}"
+ end
Chef::Provider::RemoteFile::NetworkFile.new(uri, new_resource, current_resource)
else
case uri.scheme
diff --git a/lib/chef/provider/remote_file/ftp.rb b/lib/chef/provider/remote_file/ftp.rb
index 5935e83301..b382c20c31 100644
--- a/lib/chef/provider/remote_file/ftp.rb
+++ b/lib/chef/provider/remote_file/ftp.rb
@@ -153,9 +153,9 @@ class Chef
def parse_path
path = uri.path.sub(%r{\A/}, "%2F") # re-encode the beginning slash because uri library decodes it.
directories = path.split(%r{/}, -1)
- directories.each {|d|
+ directories.each do |d|
d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") }
- }
+ end
unless filename = directories.pop
raise ArgumentError, "no filename: #{path.inspect}"
end
diff --git a/lib/chef/provider/remote_file/http.rb b/lib/chef/provider/remote_file/http.rb
index ad044f9e3c..2122142608 100644
--- a/lib/chef/provider/remote_file/http.rb
+++ b/lib/chef/provider/remote_file/http.rb
@@ -31,12 +31,14 @@ class Chef
attr_reader :uri
attr_reader :new_resource
attr_reader :current_resource
+ attr_reader :logger
# Parse the uri into instance variables
- def initialize(uri, new_resource, current_resource)
+ def initialize(uri, new_resource, current_resource, logger = Chef::Log.with_child)
@uri = uri
@new_resource = new_resource
@current_resource = current_resource
+ @logger = logger
end
def events
@@ -55,22 +57,28 @@ class Chef
if (etag = cache_control_data.etag) && want_etag_cache_control?
cache_control_headers["if-none-match"] = etag
end
- Chef::Log.debug("Cache control headers: #{cache_control_headers.inspect}")
+ logger.trace("Cache control headers: #{cache_control_headers.inspect}")
cache_control_headers
end
def fetch
http = Chef::HTTP::Simple.new(uri, http_client_opts)
+ orig_tempfile = Chef::FileContentManagement::Tempfile.new(@new_resource).tempfile
if want_progress?
- tempfile = http.streaming_request_with_progress(uri, headers) do |size, total|
+ tempfile = http.streaming_request_with_progress(uri, headers, orig_tempfile) do |size, total|
events.resource_update_progress(new_resource, size, total, progress_interval)
end
else
- tempfile = http.streaming_request(uri, headers)
+ tempfile = http.streaming_request(uri, headers, orig_tempfile)
end
if tempfile
update_cache_control_data(tempfile, http.last_response)
tempfile.close
+ else
+ # cache_control shows the file is unchanged, so we got back nil from the streaming_request above, and it is
+ # now our responsibility to unlink the tempfile we created
+ orig_tempfile.close
+ orig_tempfile.unlink
end
tempfile
end
@@ -123,7 +131,7 @@ class Chef
# case you'd end up with a tar archive (no gzip) named, e.g., foo.tgz,
# which is not what you wanted.
if uri.to_s =~ /gz$/
- Chef::Log.debug("Turning gzip compression off due to filename ending in gz")
+ logger.trace("Turning gzip compression off due to filename ending in gz")
opts[:disable_gzip] = true
end
opts
diff --git a/lib/chef/provider/remote_file/local_file.rb b/lib/chef/provider/remote_file/local_file.rb
index 613db02337..0719e5dbf7 100644
--- a/lib/chef/provider/remote_file/local_file.rb
+++ b/lib/chef/provider/remote_file/local_file.rb
@@ -48,7 +48,7 @@ class Chef
# Fetches the file at uri, returning a Tempfile-like File handle
def fetch
tempfile = Chef::FileContentManagement::Tempfile.new(new_resource).tempfile
- Chef::Log.debug("#{new_resource} staging #{source_path} to #{tempfile.path}")
+ Chef::Log.trace("#{new_resource} staging #{source_path} to #{tempfile.path}")
FileUtils.cp(source_path, tempfile.path)
tempfile.close if tempfile
tempfile
diff --git a/lib/chef/provider/remote_file/network_file.rb b/lib/chef/provider/remote_file/network_file.rb
index 44046132a9..ffd2d0bbce 100644
--- a/lib/chef/provider/remote_file/network_file.rb
+++ b/lib/chef/provider/remote_file/network_file.rb
@@ -19,14 +19,18 @@
require "uri"
require "tempfile"
require "chef/provider/remote_file"
+require "chef/mixin/user_context"
class Chef
class Provider
class RemoteFile
class NetworkFile
+ include Chef::Mixin::UserContext
attr_reader :new_resource
+ TRANSFER_CHUNK_SIZE = 1048576
+
def initialize(source, new_resource, current_resource)
@new_resource = new_resource
@source = source
@@ -35,13 +39,22 @@ class Chef
# Fetches the file on a network share, returning a Tempfile-like File handle
# windows only
def fetch
- tempfile = Chef::FileContentManagement::Tempfile.new(new_resource).tempfile
- Chef::Log.debug("#{new_resource} staging #{@source} to #{tempfile.path}")
- FileUtils.cp(@source, tempfile.path)
- tempfile.close if tempfile
+ begin
+ tempfile = Chef::FileContentManagement::Tempfile.new(new_resource).tempfile
+ Chef::Log.trace("#{new_resource} staging #{@source} to #{tempfile.path}")
+
+ with_user_context(new_resource.remote_user, new_resource.remote_password, new_resource.remote_domain, new_resource.authentication) do
+ ::File.open(@source, "rb") do |remote_file|
+ while data = remote_file.read(TRANSFER_CHUNK_SIZE)
+ tempfile.write(data)
+ end
+ end
+ end
+ ensure
+ tempfile.close if tempfile
+ end
tempfile
end
-
end
end
end
diff --git a/lib/chef/provider/remote_file/sftp.rb b/lib/chef/provider/remote_file/sftp.rb
index 530977e3c8..21c5c4ca04 100644
--- a/lib/chef/provider/remote_file/sftp.rb
+++ b/lib/chef/provider/remote_file/sftp.rb
@@ -68,9 +68,9 @@ class Chef
def validate_path!
path = uri.path.sub(%r{\A/}, "%2F") # re-encode the beginning slash because uri library decodes it.
directories = path.split(%r{/}, -1)
- directories.each {|d|
+ directories.each do |d|
d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") }
- }
+ end
unless filename = directories.pop
raise ArgumentError, "no filename: #{path.inspect}"
end
diff --git a/lib/chef/provider/route.rb b/lib/chef/provider/route.rb
index 64c89aac6d..b23d0307cc 100644
--- a/lib/chef/provider/route.rb
+++ b/lib/chef/provider/route.rb
@@ -17,213 +17,232 @@
#
require "chef/log"
-require "chef/mixin/command"
require "chef/provider"
require "ipaddr"
-class Chef::Provider::Route < Chef::Provider
- include Chef::Mixin::Command
-
- provides :route
-
- attr_accessor :is_running
-
- MASK = { "0.0.0.0" => "0",
- "128.0.0.0" => "1",
- "192.0.0.0" => "2",
- "224.0.0.0" => "3",
- "240.0.0.0" => "4",
- "248.0.0.0" => "5",
- "252.0.0.0" => "6",
- "254.0.0.0" => "7",
- "255.0.0.0" => "8",
- "255.128.0.0" => "9",
- "255.192.0.0" => "10",
- "255.224.0.0" => "11",
- "255.240.0.0" => "12",
- "255.248.0.0" => "13",
- "255.252.0.0" => "14",
- "255.254.0.0" => "15",
- "255.255.0.0" => "16",
- "255.255.128.0" => "17",
- "255.255.192.0" => "18",
- "255.255.224.0" => "19",
- "255.255.240.0" => "20",
- "255.255.248.0" => "21",
- "255.255.252.0" => "22",
- "255.255.254.0" => "23",
- "255.255.255.0" => "24",
- "255.255.255.128" => "25",
- "255.255.255.192" => "26",
- "255.255.255.224" => "27",
- "255.255.255.240" => "28",
- "255.255.255.248" => "29",
- "255.255.255.252" => "30",
- "255.255.255.254" => "31",
- "255.255.255.255" => "32" }
-
- def hex2ip(hex_data)
- # Cleanup hex data
- hex_ip = hex_data.to_s.downcase.gsub(/[^0-9a-f]/, "")
-
- # Check hex data format (IP is a 32bit integer, so should be 8 chars long)
- return nil if hex_ip.length != hex_data.length || hex_ip.length != 8
-
- # Extract octets from hex data
- octets = hex_ip.scan(/../).reverse.collect { |octet| [octet].pack("H2").unpack("C").first }
-
- # Validate IP
- ip = octets.join(".")
- begin
- IPAddr.new(ip, Socket::AF_INET).to_s
- rescue ArgumentError
- Chef::Log.debug("Invalid IP address data: hex=#{hex_ip}, ip=#{ip}")
- return nil
- end
- end
-
- def whyrun_supported?
- true
- end
-
- def load_current_resource
- self.is_running = false
+class Chef
+ class Provider
+ class Route < Chef::Provider
+
+ provides :route
+
+ attr_accessor :is_running
+
+ MASK = { "0.0.0.0" => "0",
+ "128.0.0.0" => "1",
+ "192.0.0.0" => "2",
+ "224.0.0.0" => "3",
+ "240.0.0.0" => "4",
+ "248.0.0.0" => "5",
+ "252.0.0.0" => "6",
+ "254.0.0.0" => "7",
+ "255.0.0.0" => "8",
+ "255.128.0.0" => "9",
+ "255.192.0.0" => "10",
+ "255.224.0.0" => "11",
+ "255.240.0.0" => "12",
+ "255.248.0.0" => "13",
+ "255.252.0.0" => "14",
+ "255.254.0.0" => "15",
+ "255.255.0.0" => "16",
+ "255.255.128.0" => "17",
+ "255.255.192.0" => "18",
+ "255.255.224.0" => "19",
+ "255.255.240.0" => "20",
+ "255.255.248.0" => "21",
+ "255.255.252.0" => "22",
+ "255.255.254.0" => "23",
+ "255.255.255.0" => "24",
+ "255.255.255.128" => "25",
+ "255.255.255.192" => "26",
+ "255.255.255.224" => "27",
+ "255.255.255.240" => "28",
+ "255.255.255.248" => "29",
+ "255.255.255.252" => "30",
+ "255.255.255.254" => "31",
+ "255.255.255.255" => "32" }.freeze
+
+ def hex2ip(hex_data)
+ # Cleanup hex data
+ hex_ip = hex_data.to_s.downcase.gsub(/[^0-9a-f]/, "")
+
+ # Check hex data format (IP is a 32bit integer, so should be 8 chars long)
+ return nil if hex_ip.length != hex_data.length || hex_ip.length != 8
+
+ # Extract octets from hex data
+ octets = hex_ip.scan(/../).reverse.collect { |octet| [octet].pack("H2").unpack("C").first }
+
+ # Validate IP
+ ip = octets.join(".")
+ begin
+ IPAddr.new(ip, Socket::AF_INET).to_s
+ rescue ArgumentError
+ logger.trace("Invalid IP address data: hex=#{hex_ip}, ip=#{ip}")
+ return nil
+ end
+ end
- # cidr or quad dot mask
- if @new_resource.netmask
- new_ip = IPAddr.new("#{@new_resource.target}/#{@new_resource.netmask}")
- else
- new_ip = IPAddr.new(@new_resource.target)
- end
+ def load_current_resource
+ self.is_running = false
+
+ # cidr or quad dot mask
+ new_ip = if new_resource.target == "default"
+ IPAddr.new(new_resource.gateway)
+ elsif new_resource.netmask
+ IPAddr.new("#{new_resource.target}/#{new_resource.netmask}")
+ else
+ IPAddr.new(new_resource.target)
+ end
+
+ # For linux, we use /proc/net/route file to read proc table info
+ return if node[:os] != "linux"
+
+ route_file = ::File.open("/proc/net/route", "r")
+
+ # Read all routes
+ while (line = route_file.gets)
+ # Get all the fields for a route
+ _, destination, gateway, _, _, _, _, mask = line.split
+
+ # Convert hex-encoded values to quad-dotted notation (e.g. 0064A8C0 => 192.168.100.0)
+ destination = hex2ip(destination)
+ gateway = hex2ip(gateway)
+ mask = hex2ip(mask)
+
+ # Skip formatting lines (header, etc)
+ next unless destination && gateway && mask
+ logger.trace("#{new_resource} system has route: dest=#{destination} mask=#{mask} gw=#{gateway}")
+
+ # check if what were trying to configure is already there
+ # use an ipaddr object with ip/mask this way we can have
+ # a new resource be in cidr format (i don't feel like
+ # expanding bitmask by hand.
+ #
+ running_ip = IPAddr.new("#{destination}/#{mask}")
+ logger.trace("#{new_resource} new ip: #{new_ip.inspect} running ip: #{running_ip.inspect}")
+ self.is_running = true if running_ip == new_ip && gateway == new_resource.gateway
+ end
- # For linux, we use /proc/net/route file to read proc table info
- if node[:os] == "linux"
- route_file = ::File.open("/proc/net/route", "r")
-
- # Read all routes
- while (line = route_file.gets)
- # Get all the fields for a route
- iface, destination, gateway, flags, refcnt, use, metric, mask, mtu, window, irtt = line.split
-
- # Convert hex-encoded values to quad-dotted notation (e.g. 0064A8C0 => 192.168.100.0)
- destination = hex2ip(destination)
- gateway = hex2ip(gateway)
- mask = hex2ip(mask)
-
- # Skip formatting lines (header, etc)
- next unless destination && gateway && mask
- Chef::Log.debug("#{@new_resource} system has route: dest=#{destination} mask=#{mask} gw=#{gateway}")
-
- # check if what were trying to configure is already there
- # use an ipaddr object with ip/mask this way we can have
- # a new resource be in cidr format (i don't feel like
- # expanding bitmask by hand.
- #
- running_ip = IPAddr.new("#{destination}/#{mask}")
- Chef::Log.debug("#{@new_resource} new ip: #{new_ip.inspect} running ip: #{running_ip.inspect}")
- self.is_running = true if running_ip == new_ip && gateway == @new_resource.gateway
+ route_file.close
end
- route_file.close
- end
- end
+ def action_add
+ # check to see if load_current_resource found the route
+ if is_running
+ logger.trace("#{new_resource} route already active - nothing to do")
+ else
+ command = generate_command(:add)
+ converge_by("run #{command.join(' ')} to add route") do
+ shell_out_compact!(command)
+ logger.info("#{new_resource} added")
+ end
+ end
- def action_add
- # check to see if load_current_resource found the route
- if is_running
- Chef::Log.debug("#{@new_resource} route already active - nothing to do")
- else
- command = generate_command(:add)
- converge_by ("run #{ command } to add route") do
- run_command( :command => command )
- Chef::Log.info("#{@new_resource} added")
+ # for now we always write the file (ugly but its what it is)
+ generate_config
end
- end
- #for now we always write the file (ugly but its what it is)
- generate_config
- end
+ def action_delete
+ if is_running
+ command = generate_command(:delete)
+ converge_by("run #{command.join(' ')} to delete route ") do
+ shell_out_compact!(command)
+ logger.info("#{new_resource} removed")
+ end
+ else
+ logger.trace("#{new_resource} route does not exist - nothing to do")
+ end
- def action_delete
- if is_running
- command = generate_command(:delete)
- converge_by ("run #{ command } to delete route ") do
- run_command( :command => command )
- Chef::Log.info("#{@new_resource} removed")
+ # for now we always write the file (ugly but its what it is)
+ generate_config
end
- else
- Chef::Log.debug("#{@new_resource} route does not exist - nothing to do")
- end
-
- #for now we always write the file (ugly but its what it is)
- generate_config
- end
- def generate_config
- conf = Hash.new
- case node[:platform]
- when "centos", "redhat", "fedora"
- # walk the collection
- run_context.resource_collection.each do |resource|
- if resource.is_a? Chef::Resource::Route
- # default to eth0
- if resource.device
- dev = resource.device
- else
- dev = "eth0"
+ def generate_config
+ conf = {}
+ case node[:platform]
+ when "centos", "redhat", "fedora"
+ # walk the collection
+ run_context.resource_collection.each do |resource|
+ next unless resource.is_a? Chef::Resource::Route
+ # default to eth0
+ dev = if resource.device
+ resource.device
+ else
+ "eth0"
+ end
+
+ conf[dev] = "" if conf[dev].nil?
+ case @action
+ when :add
+ conf[dev] << config_file_contents(:add, comment: resource.comment, device: resource.device, target: resource.target, metric: resource.metric, netmask: resource.netmask, gateway: resource.gateway) if resource.action == [:add]
+ when :delete
+ # need to do this for the case when the last route on an int
+ # is removed
+ conf[dev] << config_file_contents(:delete)
+ end
end
-
- conf[dev] = String.new if conf[dev].nil?
- case @action
- when :add
- conf[dev] << config_file_contents(:add, :target => resource.target, :netmask => resource.netmask, :gateway => resource.gateway) if resource.action == [:add]
- when :delete
- # need to do this for the case when the last route on an int
- # is removed
- conf[dev] << config_file_contents(:delete)
+ conf.each_key do |k|
+ if new_resource.target == "default"
+ network_file_name = "/etc/sysconfig/network"
+ converge_by("write route default route to #{network_file_name}") do
+ logger.trace("#{new_resource} writing default route #{new_resource.gateway} to #{network_file_name}")
+ if ::File.exist?(network_file_name)
+ network_file = ::Chef::Util::FileEdit.new(network_file_name)
+ network_file.search_file_replace_line /^GATEWAY=/, "GATEWAY=#{new_resource.gateway}"
+ network_file.insert_line_if_no_match /^GATEWAY=/, "GATEWAY=#{new_resource.gateway}"
+ network_file.write_file
+ else
+ network_file = ::File.new(network_file_name, "w")
+ network_file.puts("GATEWAY=#{new_resource.gateway}")
+ network_file.close
+ end
+ end
+ else
+ network_file_name = "/etc/sysconfig/network-scripts/route-#{k}"
+ converge_by("write route route.#{k}\n#{conf[k]} to #{network_file_name}") do
+ network_file = ::File.new(network_file_name, "w")
+ network_file.puts(conf[k])
+ logger.trace("#{new_resource} writing route.#{k}\n#{conf[k]}")
+ network_file.close
+ end
+ end
end
end
end
- conf.each do |k, v|
- network_file_name = "/etc/sysconfig/network-scripts/route-#{k}"
- converge_by ("write route route.#{k}\n#{conf[k]} to #{ network_file_name }") do
- network_file = ::File.new(network_file_name, "w")
- network_file.puts(conf[k])
- Chef::Log.debug("#{@new_resource} writing route.#{k}\n#{conf[k]}")
- network_file.close
+
+ def generate_command(action)
+ target = new_resource.target
+ target = "#{target}/#{MASK[new_resource.netmask.to_s]}" if new_resource.netmask
+
+ case action
+ when :add
+ command = [ "ip", "route", "replace", target ]
+ command += [ "via", new_resource.gateway ] if new_resource.gateway
+ command += [ "dev", new_resource.device ] if new_resource.device
+ command += [ "metric", new_resource.metric ] if new_resource.metric
+ when :delete
+ command = [ "ip", "route", "delete", target ]
+ command += [ "via", new_resource.gateway ] if new_resource.gateway
end
- end
- end
- end
- def generate_command(action)
- common_route_items = ""
- common_route_items << "/#{MASK[@new_resource.netmask.to_s]}" if @new_resource.netmask
- common_route_items << " via #{@new_resource.gateway} " if @new_resource.gateway
-
- case action
- when :add
- command = "ip route replace #{@new_resource.target}"
- command << common_route_items
- command << " dev #{@new_resource.device} " if @new_resource.device
- when :delete
- command = "ip route delete #{@new_resource.target}"
- command << common_route_items
- end
+ command
+ end
- return command
- end
+ def config_file_contents(action, options = {})
+ content = ""
+ case action
+ when :add
+ content << "# #{options[:comment]}\n" if options[:comment]
+ content << (options[:target]).to_s
+ content << "/#{MASK[options[:netmask].to_s]}" if options[:netmask]
+ content << " via #{options[:gateway]}" if options[:gateway]
+ content << " dev #{options[:device]}" if options[:device]
+ content << " metric #{options[:metric]}" if options[:metric]
+ content << "\n"
+ end
- def config_file_contents(action, options = {})
- content = ""
- case action
- when :add
- content << "#{options[:target]}"
- content << "/#{options[:netmask]}" if options[:netmask]
- content << " via #{options[:gateway]}" if options[:gateway]
- content << "\n"
+ content
+ end
end
-
- return content
end
end
diff --git a/lib/chef/provider/ruby_block.rb b/lib/chef/provider/ruby_block.rb
index 0817b14044..01f041ca3b 100644
--- a/lib/chef/provider/ruby_block.rb
+++ b/lib/chef/provider/ruby_block.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2009-2016, Opscode
+# Copyright:: Copyright 2009-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,18 +22,14 @@ class Chef
class RubyBlock < Chef::Provider
provides :ruby_block
- def whyrun_supported?
- true
- end
-
def load_current_resource
true
end
def action_run
- converge_by("execute the ruby block #{@new_resource.name}") do
- @new_resource.block.call
- Chef::Log.info("#{@new_resource} called")
+ converge_by("execute the ruby block #{new_resource.name}") do
+ new_resource.block.call
+ logger.info("#{new_resource} called")
end
end
diff --git a/lib/chef/provider/script.rb b/lib/chef/provider/script.rb
index 6ca4e9f6f3..c5966370f7 100644
--- a/lib/chef/provider/script.rb
+++ b/lib/chef/provider/script.rb
@@ -18,6 +18,7 @@
require "tempfile"
require "chef/provider/execute"
+require "chef/win32/security" if Chef::Platform.windows?
require "forwardable"
class Chef
@@ -33,7 +34,7 @@ class Chef
provides :ruby
provides :script
- def_delegators :@new_resource, :interpreter, :flags
+ def_delegators :new_resource, :interpreter, :flags
attr_accessor :code
@@ -50,7 +51,7 @@ class Chef
super
# @todo Chef-13: change this to an exception
if code.nil?
- Chef::Log.warn "#{@new_resource}: No code attribute was given, resource does nothing, this behavior is deprecated and will be removed in Chef-13"
+ logger.warn "#{new_resource}: No code attribute was given, resource does nothing, this behavior is deprecated and will be removed in Chef-13"
end
end
@@ -66,10 +67,45 @@ class Chef
end
def set_owner_and_group
- # FileUtils itself implements a no-op if +user+ or +group+ are nil
- # You can prove this by running FileUtils.chown(nil,nil,'/tmp/file')
- # as an unprivileged user.
- FileUtils.chown(new_resource.user, new_resource.group, script_file.path)
+ if Chef::Platform.windows?
+ # And on Windows also this is a no-op if there is no user specified.
+ grant_alternate_user_read_access
+ else
+ # FileUtils itself implements a no-op if +user+ or +group+ are nil
+ # You can prove this by running FileUtils.chown(nil,nil,'/tmp/file')
+ # as an unprivileged user.
+ FileUtils.chown(new_resource.user, new_resource.group, script_file.path)
+ end
+ end
+
+ def grant_alternate_user_read_access
+ # Do nothing if an alternate user isn't specified -- the file
+ # will already have the correct permissions for the user as part
+ # of the default ACL behavior on Windows.
+ return if new_resource.user.nil?
+
+ # Duplicate the script file's existing DACL
+ # so we can add an ACE later
+ securable_object = Chef::ReservedNames::Win32::Security::SecurableObject.new(script_file.path)
+ aces = securable_object.security_descriptor.dacl.reduce([]) { |result, current| result.push(current) }
+
+ username = new_resource.user
+
+ if new_resource.domain
+ username = new_resource.domain + '\\' + new_resource.user
+ end
+
+ # Create an ACE that allows the alternate user read access to the script
+ # file so it can be read and executed.
+ user_sid = Chef::ReservedNames::Win32::Security::SID.from_account(username)
+ read_ace = Chef::ReservedNames::Win32::Security::ACE.access_allowed(user_sid, Chef::ReservedNames::Win32::API::Security::GENERIC_READ | Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE, 0)
+ aces.push(read_ace)
+ acl = Chef::ReservedNames::Win32::Security::ACL.create(aces)
+
+ # This actually applies the modified DACL to the file
+ # Use parentheses to bypass RuboCop / ChefStyle warning
+ # about useless setter
+ (securable_object.dacl = acl)
end
def script_file
diff --git a/lib/chef/provider/service.rb b/lib/chef/provider/service.rb
index e693bd2eed..c116d321f1 100644
--- a/lib/chef/provider/service.rb
+++ b/lib/chef/provider/service.rb
@@ -1,7 +1,7 @@
#
# Author:: AJ Christensen (<aj@hjksolutions.com>)
# Author:: Davide Cavalca (<dcavalca@fb.com>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,15 +17,12 @@
# limitations under the License.
#
-require "chef/mixin/command"
require "chef/provider"
class Chef
class Provider
class Service < Chef::Provider
- include Chef::Mixin::Command
-
def supports
@supports ||= new_resource.supports.dup
end
@@ -35,10 +32,6 @@ class Chef
@enabled = nil
end
- def whyrun_supported?
- true
- end
-
def load_current_resource
supports[:status] = false if supports[:status].nil?
supports[:reload] = false if supports[:reload].nil?
@@ -51,21 +44,21 @@ class Chef
# XXX?: the #nil? check below will likely fail if this is a cloned resource or if
# we just run multiple actions.
def load_new_resource_state
- if @new_resource.enabled.nil?
- @new_resource.enabled(@current_resource.enabled)
+ if new_resource.enabled.nil?
+ new_resource.enabled(current_resource.enabled)
end
- if @new_resource.running.nil?
- @new_resource.running(@current_resource.running)
+ if new_resource.running.nil?
+ new_resource.running(current_resource.running)
end
- if @new_resource.masked.nil?
- @new_resource.masked(@current_resource.masked)
+ if new_resource.masked.nil?
+ new_resource.masked(current_resource.masked)
end
end
# subclasses should override this if they do implement user services
def user_services_requirements
requirements.assert(:all_actions) do |a|
- a.assertion { @new_resource.user.nil? }
+ a.assertion { new_resource.user.nil? }
a.failure_message Chef::Exceptions::UnsupportedAction, "#{self} does not support user services"
end
end
@@ -76,7 +69,7 @@ class Chef
def define_resource_requirements
requirements.assert(:reload) do |a|
- a.assertion { supports[:reload] || @new_resource.reload_command }
+ a.assertion { supports[:reload] || new_resource.reload_command }
a.failure_message Chef::Exceptions::UnsupportedAction, "#{self} does not support :reload"
# if a service is not declared to support reload, that won't
# typically change during the course of a run - so no whyrun
@@ -85,97 +78,97 @@ class Chef
end
def action_enable
- if @current_resource.enabled
- Chef::Log.debug("#{@new_resource} already enabled - nothing to do")
+ if current_resource.enabled
+ logger.trace("#{new_resource} already enabled - nothing to do")
else
- converge_by("enable service #{@new_resource}") do
+ converge_by("enable service #{new_resource}") do
enable_service
- Chef::Log.info("#{@new_resource} enabled")
+ logger.info("#{new_resource} enabled")
end
end
load_new_resource_state
- @new_resource.enabled(true)
+ new_resource.enabled(true)
end
def action_disable
- if @current_resource.enabled
- converge_by("disable service #{@new_resource}") do
+ if current_resource.enabled
+ converge_by("disable service #{new_resource}") do
disable_service
- Chef::Log.info("#{@new_resource} disabled")
+ logger.info("#{new_resource} disabled")
end
else
- Chef::Log.debug("#{@new_resource} already disabled - nothing to do")
+ logger.trace("#{new_resource} already disabled - nothing to do")
end
load_new_resource_state
- @new_resource.enabled(false)
+ new_resource.enabled(false)
end
def action_mask
- if @current_resource.masked
- Chef::Log.debug("#{@new_resource} already masked - nothing to do")
+ if current_resource.masked
+ logger.trace("#{new_resource} already masked - nothing to do")
else
- converge_by("mask service #{@new_resource}") do
+ converge_by("mask service #{new_resource}") do
mask_service
- Chef::Log.info("#{@new_resource} masked")
+ logger.info("#{new_resource} masked")
end
end
load_new_resource_state
- @new_resource.masked(true)
+ new_resource.masked(true)
end
def action_unmask
- if @current_resource.masked
- converge_by("unmask service #{@new_resource}") do
+ if current_resource.masked
+ converge_by("unmask service #{new_resource}") do
unmask_service
- Chef::Log.info("#{@new_resource} unmasked")
+ logger.info("#{new_resource} unmasked")
end
else
- Chef::Log.debug("#{@new_resource} already unmasked - nothing to do")
+ logger.trace("#{new_resource} already unmasked - nothing to do")
end
load_new_resource_state
- @new_resource.masked(false)
+ new_resource.masked(false)
end
def action_start
- unless @current_resource.running
- converge_by("start service #{@new_resource}") do
+ unless current_resource.running
+ converge_by("start service #{new_resource}") do
start_service
- Chef::Log.info("#{@new_resource} started")
+ logger.info("#{new_resource} started")
end
else
- Chef::Log.debug("#{@new_resource} already running - nothing to do")
+ logger.trace("#{new_resource} already running - nothing to do")
end
load_new_resource_state
- @new_resource.running(true)
+ new_resource.running(true)
end
def action_stop
- if @current_resource.running
- converge_by("stop service #{@new_resource}") do
+ if current_resource.running
+ converge_by("stop service #{new_resource}") do
stop_service
- Chef::Log.info("#{@new_resource} stopped")
+ logger.info("#{new_resource} stopped")
end
else
- Chef::Log.debug("#{@new_resource} already stopped - nothing to do")
+ logger.trace("#{new_resource} already stopped - nothing to do")
end
load_new_resource_state
- @new_resource.running(false)
+ new_resource.running(false)
end
def action_restart
- converge_by("restart service #{@new_resource}") do
+ converge_by("restart service #{new_resource}") do
restart_service
- Chef::Log.info("#{@new_resource} restarted")
+ logger.info("#{new_resource} restarted")
end
load_new_resource_state
- @new_resource.running(true)
+ new_resource.running(true)
end
def action_reload
- if @current_resource.running
- converge_by("reload service #{@new_resource}") do
+ if current_resource.running
+ converge_by("reload service #{new_resource}") do
reload_service
- Chef::Log.info("#{@new_resource} reloaded")
+ logger.info("#{new_resource} reloaded")
end
end
load_new_resource_state
@@ -216,17 +209,17 @@ class Chef
protected
def default_init_command
- if @new_resource.init_command
- @new_resource.init_command
- elsif self.instance_variable_defined?(:@init_command)
+ if new_resource.init_command
+ new_resource.init_command
+ elsif instance_variable_defined?(:@init_command)
@init_command
end
end
def custom_command_for_action?(action)
method_name = "#{action}_command".to_sym
- @new_resource.respond_to?(method_name) &&
- !!@new_resource.send(method_name)
+ new_resource.respond_to?(method_name) &&
+ !!new_resource.send(method_name)
end
module ServicePriorityInit
@@ -252,7 +245,7 @@ class Chef
Chef.set_provider_priority_array :service, [ Systemd, Arch ], platform_family: "arch"
Chef.set_provider_priority_array :service, [ Systemd, Gentoo ], platform_family: "gentoo"
Chef.set_provider_priority_array :service, [ Systemd, Upstart, Insserv, Debian, Invokercd ], platform_family: "debian"
- Chef.set_provider_priority_array :service, [ Systemd, Insserv, Redhat ], platform_family: %w{rhel fedora suse}
+ Chef.set_provider_priority_array :service, [ Systemd, Insserv, Redhat ], platform_family: %w{rhel fedora suse amazon}
end
end
end
diff --git a/lib/chef/provider/service/aix.rb b/lib/chef/provider/service/aix.rb
index 201f9ff5f9..10ea06152b 100644
--- a/lib/chef/provider/service/aix.rb
+++ b/lib/chef/provider/service/aix.rb
@@ -1,6 +1,6 @@
#
# Author:: kaustubh (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -43,10 +43,6 @@ class Chef
@current_resource
end
- def whyrun_supported?
- true
- end
-
def start_service
if @is_resource_group
shell_out!("startsrc -g #{@new_resource.service_name}")
@@ -92,7 +88,7 @@ class Chef
protected
def determine_current_status!
- Chef::Log.debug "#{@new_resource} using lssrc to check the status"
+ logger.trace "#{@new_resource} using lssrc to check the status"
begin
if is_resource_group?
# Groups as a whole have no notion of whether they're running
@@ -105,7 +101,7 @@ class Chef
@current_resource.running false
end
end
- Chef::Log.debug "#{@new_resource} running: #{@current_resource.running}"
+ logger.trace "#{@new_resource} running: #{@current_resource.running}"
# ShellOut sometimes throws different types of Exceptions than ShellCommandFailed.
# Temporarily catching different types of exceptions here until we get Shellout fixed.
# TODO: Remove the line before one we get the ShellOut fix.
@@ -119,7 +115,7 @@ class Chef
def is_resource_group?
so = shell_out("lssrc -g #{@new_resource.service_name}")
if so.exitstatus == 0
- Chef::Log.debug("#{@new_resource.service_name} is a group")
+ logger.trace("#{@new_resource.service_name} is a group")
@is_resource_group = true
end
end
diff --git a/lib/chef/provider/service/aixinit.rb b/lib/chef/provider/service/aixinit.rb
index 73c5e07715..dd8514cf20 100644
--- a/lib/chef/provider/service/aixinit.rb
+++ b/lib/chef/provider/service/aixinit.rb
@@ -45,11 +45,11 @@ class Chef
priority_ok = @current_resource.priority == @new_resource.priority
end
if @current_resource.enabled && priority_ok
- Chef::Log.debug("#{@new_resource} already enabled - nothing to do")
+ logger.trace("#{@new_resource} already enabled - nothing to do")
else
converge_by("enable service #{@new_resource}") do
enable_service
- Chef::Log.info("#{@new_resource} enabled")
+ logger.info("#{@new_resource} enabled")
end
end
load_new_resource_state
diff --git a/lib/chef/provider/service/arch.rb b/lib/chef/provider/service/arch.rb
index 2fd32e37aa..e34227036a 100644
--- a/lib/chef/provider/service/arch.rb
+++ b/lib/chef/provider/service/arch.rb
@@ -66,7 +66,7 @@ class Chef::Provider::Service::Arch < Chef::Provider::Service::Init
end
end
- def enable_service()
+ def enable_service
new_daemons = []
entries = daemons
@@ -92,7 +92,7 @@ class Chef::Provider::Service::Arch < Chef::Provider::Service::Init
end
end
- def disable_service()
+ def disable_service
new_daemons = []
entries = daemons
diff --git a/lib/chef/provider/service/debian.rb b/lib/chef/provider/service/debian.rb
index 67b71953f7..09af9f224f 100644
--- a/lib/chef/provider/service/debian.rb
+++ b/lib/chef/provider/service/debian.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@hjksolutions.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -35,8 +35,6 @@ class Chef
def load_current_resource
super
- @priority_success = true
- @rcd_status = nil
current_resource.priority(get_priority)
current_resource.enabled(service_currently_enabled?(current_resource.priority))
current_resource
@@ -54,8 +52,8 @@ class Chef
end
requirements.assert(:all_actions) do |a|
- a.assertion { @priority_success }
- a.failure_message Chef::Exceptions::Service, "/usr/sbin/update-rc.d -n -f #{current_resource.service_name} failed - #{@rcd_status.inspect}"
+ a.assertion { @got_priority == true }
+ a.failure_message Chef::Exceptions::Service, "Unable to determine priority for service"
# This can happen if the service is not yet installed,so we'll fake it.
a.whyrun ["Unable to determine priority of service, assuming service would have been correctly installed earlier in the run.",
"Assigning temporary priorities to continue.",
@@ -72,23 +70,38 @@ class Chef
end
end
+ # returns a list of levels that the service should be stopped or started on
+ def parse_init_file(path)
+ return [] unless ::File.exist?(path)
+ in_info = false
+ ::File.readlines(path).each_with_object([]) do |line, acc|
+ if line =~ /^### BEGIN INIT INFO/
+ in_info = true
+ elsif line =~ /^### END INIT INFO/
+ in_info = false
+ elsif in_info
+ if line =~ /Default-(Start|Stop):\s+(\d.*)/
+ acc << $2.split(" ")
+ end
+ end
+ end.flatten
+ end
+
def get_priority
priority = {}
+ rc_files = []
- @rcd_status = popen4("/usr/sbin/update-rc.d -n -f #{current_resource.service_name} remove") do |pid, stdin, stdout, stderr|
-
- [stdout, stderr].each do |iop|
- iop.each_line do |line|
- if UPDATE_RC_D_PRIORITIES =~ line
- # priority[runlevel] = [ S|K, priority ]
- # S = Start, K = Kill
- # debian runlevels: 0 Halt, 1 Singleuser, 2 Multiuser, 3-5 == 2, 6 Reboot
- priority[$1] = [($2 == "S" ? :start : :stop), $3]
- end
- if line =~ UPDATE_RC_D_ENABLED_MATCHES
- enabled = true
- end
- end
+ levels = parse_init_file(@init_command)
+ levels.each do |level|
+ rc_files.push Dir.glob("/etc/rc#{level}.d/[SK][0-9][0-9]#{current_resource.service_name}")
+ end
+
+ rc_files.flatten.each do |line|
+ if UPDATE_RC_D_PRIORITIES =~ line
+ # priority[runlevel] = [ S|K, priority ]
+ # S = Start, K = Kill
+ # debian runlevels: 0 Halt, 1 Singleuser, 2 Multiuser, 3-5 == 2, 6 Reboot
+ priority[$1] = [($2 == "S" ? :start : :stop), $3]
end
end
@@ -98,21 +111,19 @@ class Chef
priority = priority[2].last
end
- unless @rcd_status.exitstatus == 0
- @priority_success = false
- end
+ @got_priority = true
priority
end
def service_currently_enabled?(priority)
enabled = false
- priority.each { |runlevel, arguments|
- Chef::Log.debug("#{new_resource} runlevel #{runlevel}, action #{arguments[0]}, priority #{arguments[1]}")
+ priority.each do |runlevel, arguments|
+ logger.trace("#{new_resource} runlevel #{runlevel}, action #{arguments[0]}, priority #{arguments[1]}")
# if we are in a update-rc.d default startup runlevel && we start in this runlevel
if %w{ 1 2 3 4 5 S }.include?(runlevel) && arguments[0] == :start
enabled = true
end
- }
+ end
enabled
end
@@ -125,11 +136,11 @@ class Chef
priority_ok = @current_resource.priority == new_resource.priority
end
if current_resource.enabled && priority_ok
- Chef::Log.debug("#{new_resource} already enabled - nothing to do")
+ logger.trace("#{new_resource} already enabled - nothing to do")
else
converge_by("enable service #{new_resource}") do
enable_service
- Chef::Log.info("#{new_resource} enabled")
+ logger.info("#{new_resource} enabled")
end
end
load_new_resource_state
diff --git a/lib/chef/provider/service/freebsd.rb b/lib/chef/provider/service/freebsd.rb
index 76d8c1d17b..68de39fd0f 100644
--- a/lib/chef/provider/service/freebsd.rb
+++ b/lib/chef/provider/service/freebsd.rb
@@ -18,7 +18,6 @@
require "chef/resource/service"
require "chef/provider/service/init"
-require "chef/mixin/command"
class Chef
class Provider
@@ -48,7 +47,7 @@ class Chef
return current_resource unless init_command
- Chef::Log.debug("#{current_resource} found at #{init_command}")
+ logger.trace("#{current_resource} found at #{init_command}")
@status_load_success = true
determine_current_status! # see Chef::Provider::Service::Simple
@@ -74,7 +73,7 @@ class Chef
end
requirements.assert(:start, :enable, :reload, :restart) do |a|
- a.assertion { service_enable_variable_name != nil }
+ a.assertion { !service_enable_variable_name.nil? }
a.failure_message Chef::Exceptions::Service, "Could not find the service name in #{init_command} and rcvar"
# No recovery in whyrun mode - the init file is present but not correct.
end
@@ -146,7 +145,7 @@ class Chef
end
# some scripts support multiple instances through symlinks such as openvpn.
# We should get the service name from rcvar.
- Chef::Log.debug("name=\"service\" not found at #{init_command}. falling back to rcvar")
+ logger.trace("name=\"service\" not found at #{init_command}. falling back to rcvar")
shell_out!("#{init_command} rcvar").stdout[/(\w+_enable)=/, 1]
else
# for why-run mode when the rcd_script is not there yet
@@ -172,7 +171,7 @@ class Chef
end
if current_resource.enabled.nil?
- Chef::Log.debug("#{new_resource.name} enable/disable state unknown")
+ logger.trace("#{new_resource.name} enable/disable state unknown")
current_resource.enabled false
end
end
diff --git a/lib/chef/provider/service/gentoo.rb b/lib/chef/provider/service/gentoo.rb
index 8fb6d1f9af..69b3d20a3f 100644
--- a/lib/chef/provider/service/gentoo.rb
+++ b/lib/chef/provider/service/gentoo.rb
@@ -18,7 +18,6 @@
#
require "chef/provider/service/init"
-require "chef/mixin/command"
require "chef/util/path_helper"
class Chef::Provider::Service::Gentoo < Chef::Provider::Service::Init
@@ -37,11 +36,11 @@ class Chef::Provider::Service::Gentoo < Chef::Provider::Service::Init
@found_script = true
exists = ::File.exists? file
readable = ::File.readable? file
- Chef::Log.debug "#{@new_resource} exists: #{exists}, readable: #{readable}"
+ logger.trace "#{@new_resource} exists: #{exists}, readable: #{readable}"
exists && readable
end
)
- Chef::Log.debug "#{@new_resource} enabled: #{@current_resource.enabled}"
+ logger.trace "#{@new_resource} enabled: #{@current_resource.enabled}"
@current_resource
end
@@ -61,11 +60,11 @@ class Chef::Provider::Service::Gentoo < Chef::Provider::Service::Init
end
end
- def enable_service()
+ def enable_service
shell_out!("/sbin/rc-update add #{@new_resource.service_name} default")
end
- def disable_service()
+ def disable_service
shell_out!("/sbin/rc-update del #{@new_resource.service_name} default")
end
end
diff --git a/lib/chef/provider/service/init.rb b/lib/chef/provider/service/init.rb
index dff627d016..c6c582f8b8 100644
--- a/lib/chef/provider/service/init.rb
+++ b/lib/chef/provider/service/init.rb
@@ -17,7 +17,6 @@
#
require "chef/provider/service/simple"
-require "chef/mixin/command"
require "chef/platform/service_helpers"
class Chef
diff --git a/lib/chef/provider/service/insserv.rb b/lib/chef/provider/service/insserv.rb
index 76b2ee7477..a8e841f8b3 100644
--- a/lib/chef/provider/service/insserv.rb
+++ b/lib/chef/provider/service/insserv.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright 2011-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,7 @@ class Chef
class Service
class Insserv < Chef::Provider::Service::Init
- provides :service, platform_family: %w{debian rhel fedora suse} do |node|
+ provides :service, platform_family: %w{debian rhel fedora suse amazon} do |node|
Chef::Platform::ServiceHelpers.service_resource_providers.include?(:insserv)
end
@@ -45,12 +45,12 @@ class Chef
current_resource
end
- def enable_service()
+ def enable_service
shell_out!("/sbin/insserv -r -f #{new_resource.service_name}")
shell_out!("/sbin/insserv -d -f #{new_resource.service_name}")
end
- def disable_service()
+ def disable_service
shell_out!("/sbin/insserv -r -f #{new_resource.service_name}")
end
end
diff --git a/lib/chef/provider/service/macosx.rb b/lib/chef/provider/service/macosx.rb
index 648cd9748b..40021f9ba6 100644
--- a/lib/chef/provider/service/macosx.rb
+++ b/lib/chef/provider/service/macosx.rb
@@ -28,7 +28,7 @@ class Chef
class Service
class Macosx < Chef::Provider::Service::Simple
- provides :macosx_service, os: "darwin"
+ provides :macosx_service
provides :service, os: "darwin"
def self.gather_plist_dirs
@@ -52,21 +52,22 @@ class Chef
@plist_size = 0
@plist = @new_resource.plist ? @new_resource.plist : find_service_plist
@service_label = find_service_label
- # LauchAgents should be loaded as the console user.
+ # LaunchAgents should be loaded as the console user.
@console_user = @plist ? @plist.include?("LaunchAgents") : false
@session_type = @new_resource.session_type
if @console_user
- @console_user = Etc.getlogin
- Chef::Log.debug("#{new_resource} console_user: '#{@console_user}'")
+ @console_user = Etc.getpwuid(::File.stat("/dev/console").uid).name
+ logger.trace("#{new_resource} console_user: '#{@console_user}'")
cmd = "su "
param = this_version_or_newer?("10.10") ? "" : "-l "
+ param = "-l " if this_version_or_newer?("10.12")
@base_user_cmd = cmd + param + "#{@console_user} -c"
- # Default LauchAgent session should be Aqua
+ # Default LaunchAgent session should be Aqua
@session_type = "Aqua" if @session_type.nil?
end
- Chef::Log.debug("#{new_resource} Plist: '#{@plist}' service_label: '#{@service_label}'")
+ logger.trace("#{new_resource} Plist: '#{@plist}' service_label: '#{@service_label}'")
set_service_status
@current_resource
@@ -107,7 +108,7 @@ class Chef
def start_service
if @current_resource.running
- Chef::Log.debug("#{@new_resource} already running, not starting")
+ logger.trace("#{@new_resource} already running, not starting")
else
if @new_resource.start_command
super
@@ -119,7 +120,7 @@ class Chef
def stop_service
unless @current_resource.running
- Chef::Log.debug("#{@new_resource} not running, not stopping")
+ logger.trace("#{@new_resource} not running, not stopping")
else
if @new_resource.stop_command
super
@@ -146,7 +147,7 @@ class Chef
# supervisor that will restart daemons that are crashing, etc.
def enable_service
if @current_resource.enabled
- Chef::Log.debug("#{@new_resource} already enabled, not enabling")
+ logger.trace("#{@new_resource} already enabled, not enabling")
else
load_service
end
@@ -154,7 +155,7 @@ class Chef
def disable_service
unless @current_resource.enabled
- Chef::Log.debug("#{@new_resource} not enabled, not disabling")
+ logger.trace("#{@new_resource} not enabled, not disabling")
else
unload_service
end
@@ -181,7 +182,7 @@ class Chef
end
def set_service_status
- return if @plist == nil || @service_label.to_s.empty?
+ return if @plist.nil? || @service_label.to_s.empty?
cmd = "launchctl list #{@service_label}"
res = shell_out_as_user(cmd)
@@ -197,8 +198,8 @@ class Chef
case line.downcase
when /\s+\"pid\"\s+=\s+(\d+).*/
pid = $1
- @current_resource.running(!pid.to_i.zero?)
- Chef::Log.debug("Current PID for #{@service_label} is #{pid}")
+ @current_resource.running(pid.to_i != 0)
+ logger.trace("Current PID for #{@service_label} is #{pid}")
end
end
else
diff --git a/lib/chef/provider/service/openbsd.rb b/lib/chef/provider/service/openbsd.rb
index c60bbf170c..552173fbee 100644
--- a/lib/chef/provider/service/openbsd.rb
+++ b/lib/chef/provider/service/openbsd.rb
@@ -16,7 +16,6 @@
# limitations under the License.
#
-require "chef/mixin/command"
require "chef/mixin/shell_out"
require "chef/provider/service/init"
require "chef/resource/service"
@@ -49,7 +48,7 @@ class Chef
@current_resource = Chef::Resource::Service.new(new_resource.name)
current_resource.service_name(new_resource.service_name)
- Chef::Log.debug("#{current_resource} found at #{init_command}")
+ logger.trace("#{current_resource} found at #{init_command}")
determine_current_status!
determine_enabled_status!
@@ -72,7 +71,7 @@ class Chef
end
requirements.assert(:start, :enable, :reload, :restart) do |a|
- a.assertion { init_command && builtin_service_enable_variable_name != nil }
+ a.assertion { init_command && !builtin_service_enable_variable_name.nil? }
a.failure_message Chef::Exceptions::Service, "Could not find the service name in #{init_command} and rcvar"
# No recovery in whyrun mode - the init file is present but not correct.
end
diff --git a/lib/chef/provider/service/redhat.rb b/lib/chef/provider/service/redhat.rb
index 200a2d3400..1da3d7c01a 100644
--- a/lib/chef/provider/service/redhat.rb
+++ b/lib/chef/provider/service/redhat.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@hjksolutions.com>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,7 +28,7 @@ class Chef
# @api private
attr_accessor :current_run_levels
- provides :service, platform_family: %w{rhel fedora suse} do |node|
+ provides :service, platform_family: %w{rhel fedora suse amazon} do |node|
Chef::Platform::ServiceHelpers.service_resource_providers.include?(:redhat)
end
@@ -109,7 +109,7 @@ class Chef
(run_levels.nil? || run_levels.empty?) ? "" : "--level #{run_levels.join('')} "
end
- def enable_service()
+ def enable_service
unless run_levels.nil? || run_levels.empty?
disable_levels = current_run_levels - run_levels
shell_out! "/sbin/chkconfig --level #{disable_levels.join('')} #{new_resource.service_name} off" unless disable_levels.empty?
@@ -117,7 +117,7 @@ class Chef
shell_out! "/sbin/chkconfig #{levels}#{new_resource.service_name} on"
end
- def disable_service()
+ def disable_service
shell_out! "/sbin/chkconfig #{levels}#{new_resource.service_name} off"
end
end
diff --git a/lib/chef/provider/service/simple.rb b/lib/chef/provider/service/simple.rb
index fe4768b2e8..7b5f75c4b3 100644
--- a/lib/chef/provider/service/simple.rb
+++ b/lib/chef/provider/service/simple.rb
@@ -18,7 +18,6 @@
require "chef/provider/service"
require "chef/resource/service"
-require "chef/mixin/command"
class Chef
class Provider
@@ -41,10 +40,6 @@ class Chef
@current_resource
end
- def whyrun_supported?
- true
- end
-
def shared_resource_requirements
super
requirements.assert(:all_actions) do |a|
@@ -76,8 +71,9 @@ class Chef
end
requirements.assert(:all_actions) do |a|
- a.assertion { @new_resource.status_command || supports[:status] ||
- (!ps_cmd.nil? && !ps_cmd.empty?) }
+ a.assertion do
+ @new_resource.status_command || supports[:status] ||
+ (!ps_cmd.nil? && !ps_cmd.empty?) end
a.failure_message Chef::Exceptions::Service, "#{@new_resource} could not determine how to inspect the process table, please set this node's 'command.ps' attribute"
end
requirements.assert(:all_actions) do |a|
@@ -108,16 +104,16 @@ class Chef
shell_out_with_systems_locale!(@new_resource.reload_command)
end
- protected
+ protected
def determine_current_status!
if @new_resource.status_command
- Chef::Log.debug("#{@new_resource} you have specified a status command, running..")
+ logger.trace("#{@new_resource} you have specified a status command, running..")
begin
if shell_out(@new_resource.status_command).exitstatus == 0
@current_resource.running true
- Chef::Log.debug("#{@new_resource} is running")
+ logger.trace("#{@new_resource} is running")
end
rescue Mixlib::ShellOut::ShellCommandFailed, SystemCallError
# ShellOut sometimes throws different types of Exceptions than ShellCommandFailed.
@@ -129,11 +125,11 @@ class Chef
end
elsif supports[:status]
- Chef::Log.debug("#{@new_resource} supports status, running")
+ logger.trace("#{@new_resource} supports status, running")
begin
if shell_out("#{default_init_command} status").exitstatus == 0
@current_resource.running true
- Chef::Log.debug("#{@new_resource} is running")
+ logger.trace("#{@new_resource} is running")
end
# ShellOut sometimes throws different types of Exceptions than ShellCommandFailed.
# Temporarily catching different types of exceptions here until we get Shellout fixed.
@@ -144,9 +140,9 @@ class Chef
nil
end
else
- Chef::Log.debug "#{@new_resource} falling back to process table inspection"
+ logger.trace "#{@new_resource} falling back to process table inspection"
r = Regexp.new(@new_resource.pattern)
- Chef::Log.debug "#{@new_resource} attempting to match '#{@new_resource.pattern}' (#{r.inspect}) against process list"
+ logger.trace "#{@new_resource} attempting to match '#{@new_resource.pattern}' (#{r.inspect}) against process list"
begin
shell_out!(ps_cmd).stdout.each_line do |line|
if r.match(line)
@@ -156,7 +152,7 @@ class Chef
end
@current_resource.running false unless @current_resource.running
- Chef::Log.debug "#{@new_resource} running: #{@current_resource.running}"
+ logger.trace "#{@new_resource} running: #{@current_resource.running}"
# ShellOut sometimes throws different types of Exceptions than ShellCommandFailed.
# Temporarily catching different types of exceptions here until we get Shellout fixed.
# TODO: Remove the line before one we get the ShellOut fix.
diff --git a/lib/chef/provider/service/solaris.rb b/lib/chef/provider/service/solaris.rb
index 1e5398eba8..f2b1ec4262 100644
--- a/lib/chef/provider/service/solaris.rb
+++ b/lib/chef/provider/service/solaris.rb
@@ -18,7 +18,6 @@
require "chef/provider/service"
require "chef/resource/service"
-require "chef/mixin/command"
class Chef
class Provider
@@ -32,7 +31,7 @@ class Chef
super
@init_command = "/usr/sbin/svcadm"
@status_command = "/bin/svcs"
- @maintenace = false
+ @maintenance = false
end
def load_current_resource
@@ -40,7 +39,7 @@ class Chef
@current_resource.service_name(@new_resource.service_name)
[@init_command, @status_command].each do |cmd|
- unless ::File.executable? cmd then
+ unless ::File.executable? cmd
raise Chef::Exceptions::Service, "#{cmd} not executable!"
end
end
@@ -55,12 +54,16 @@ class Chef
end
def enable_service
+ # Running service status to update maintenance status to invoke svcadm clear
+ service_status
shell_out!(default_init_command, "clear", @new_resource.service_name) if @maintenance
- shell_out!(default_init_command, "enable", "-s", @new_resource.service_name)
+ enable_flags = [ "-s", @new_resource.options ].flatten.compact
+ shell_out!(default_init_command, "enable", *enable_flags, @new_resource.service_name)
end
def disable_service
- shell_out!(default_init_command, "disable", "-s", @new_resource.service_name)
+ disable_flags = [ "-s", @new_resource.options ].flatten.compact
+ shell_out!(default_init_command, "disable", *disable_flags, @new_resource.service_name)
end
alias_method :stop_service, :disable_service
@@ -73,7 +76,7 @@ class Chef
def restart_service
## svcadm restart doesn't supports sync(-s) option
disable_service
- return enable_service
+ enable_service
end
def service_status
@@ -92,6 +95,9 @@ class Chef
# dependency require_all/error svc:/milestone/multi-user:default (online)
# $
+ # Set the default value for maintenance
+ @maintenance = false
+
# load output into hash
status = {}
cmd.stdout.each_line do |line|
@@ -100,7 +106,6 @@ class Chef
end
# check service state
- @maintenance = false
case status["state"]
when "online"
@current_resource.enabled(true)
diff --git a/lib/chef/provider/service/systemd.rb b/lib/chef/provider/service/systemd.rb
index 1597d46a3d..1bcc2f3a00 100644
--- a/lib/chef/provider/service/systemd.rb
+++ b/lib/chef/provider/service/systemd.rb
@@ -20,6 +20,7 @@
require "chef/resource/service"
require "chef/provider/service/simple"
require "chef/mixin/which"
+require "shellwords"
class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
@@ -41,7 +42,7 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
@status_check_success = true
if new_resource.status_command
- Chef::Log.debug("#{new_resource} you have specified a status command, running..")
+ logger.trace("#{new_resource} you have specified a status command, running..")
unless shell_out(new_resource.status_command).error?
current_resource.running(true)
@@ -76,12 +77,12 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
def get_systemctl_options_args
if new_resource.user
- uid = node["etc"]["passwd"][new_resource.user]["uid"]
+ uid = Etc.getpwuid(new_resource.user).uid
options = {
- "environment" => {
+ :environment => {
"DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/#{uid}/bus",
},
- "user" => new_resource.user,
+ :user => new_resource.user,
}
args = "--user"
else
@@ -89,31 +90,31 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
args = "--system"
end
- return options, args
+ [options, args]
end
def start_service
if current_resource.running
- Chef::Log.debug("#{new_resource} already running, not starting")
+ logger.trace("#{new_resource} already running, not starting")
else
if new_resource.start_command
super
else
options, args = get_systemctl_options_args
- shell_out_with_systems_locale!("#{systemctl_path} #{args} start #{new_resource.service_name}", options)
+ shell_out_with_systems_locale!("#{systemctl_path} #{args} start #{Shellwords.escape(new_resource.service_name)}", options)
end
end
end
def stop_service
unless current_resource.running
- Chef::Log.debug("#{new_resource} not running, not stopping")
+ logger.trace("#{new_resource} not running, not stopping")
else
if new_resource.stop_command
super
else
options, args = get_systemctl_options_args
- shell_out_with_systems_locale!("#{systemctl_path} #{args} stop #{new_resource.service_name}", options)
+ shell_out_with_systems_locale!("#{systemctl_path} #{args} stop #{Shellwords.escape(new_resource.service_name)}", options)
end
end
end
@@ -123,7 +124,7 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
super
else
options, args = get_systemctl_options_args
- shell_out_with_systems_locale!("#{systemctl_path} #{args} restart #{new_resource.service_name}", options)
+ shell_out_with_systems_locale!("#{systemctl_path} #{args} restart #{Shellwords.escape(new_resource.service_name)}", options)
end
end
@@ -133,7 +134,7 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
else
if current_resource.running
options, args = get_systemctl_options_args
- shell_out_with_systems_locale!("#{systemctl_path} #{args} reload #{new_resource.service_name}", options)
+ shell_out_with_systems_locale!("#{systemctl_path} #{args} reload #{Shellwords.escape(new_resource.service_name)}", options)
else
start_service
end
@@ -142,37 +143,37 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
def enable_service
options, args = get_systemctl_options_args
- shell_out!("#{systemctl_path} #{args} enable #{new_resource.service_name}", options)
+ shell_out!("#{systemctl_path} #{args} enable #{Shellwords.escape(new_resource.service_name)}", options)
end
def disable_service
options, args = get_systemctl_options_args
- shell_out!("#{systemctl_path} #{args} disable #{new_resource.service_name}", options)
+ shell_out!("#{systemctl_path} #{args} disable #{Shellwords.escape(new_resource.service_name)}", options)
end
def mask_service
options, args = get_systemctl_options_args
- shell_out!("#{systemctl_path} #{args} mask #{new_resource.service_name}", options)
+ shell_out!("#{systemctl_path} #{args} mask #{Shellwords.escape(new_resource.service_name)}", options)
end
def unmask_service
options, args = get_systemctl_options_args
- shell_out!("#{systemctl_path} #{args} unmask #{new_resource.service_name}", options)
+ shell_out!("#{systemctl_path} #{args} unmask #{Shellwords.escape(new_resource.service_name)}", options)
end
def is_active?
options, args = get_systemctl_options_args
- shell_out("#{systemctl_path} #{args} is-active #{new_resource.service_name} --quiet", options).exitstatus == 0
+ shell_out("#{systemctl_path} #{args} is-active #{Shellwords.escape(new_resource.service_name)} --quiet", options).exitstatus == 0
end
def is_enabled?
options, args = get_systemctl_options_args
- shell_out("#{systemctl_path} #{args} is-enabled #{new_resource.service_name} --quiet", options).exitstatus == 0
+ shell_out("#{systemctl_path} #{args} is-enabled #{Shellwords.escape(new_resource.service_name)} --quiet", options).exitstatus == 0
end
def is_masked?
options, args = get_systemctl_options_args
- s = shell_out("#{systemctl_path} #{args} is-enabled #{new_resource.service_name}", options)
+ s = shell_out("#{systemctl_path} #{args} is-enabled #{Shellwords.escape(new_resource.service_name)}", options)
s.exitstatus != 0 && s.stdout.include?("masked")
end
diff --git a/lib/chef/provider/service/upstart.rb b/lib/chef/provider/service/upstart.rb
index 3ac5ff51da..810d9eabb7 100644
--- a/lib/chef/provider/service/upstart.rb
+++ b/lib/chef/provider/service/upstart.rb
@@ -18,7 +18,6 @@
require "chef/resource/service"
require "chef/provider/service/simple"
-require "chef/mixin/command"
require "chef/util/file_edit"
class Chef
@@ -26,12 +25,16 @@ class Chef
class Service
class Upstart < Chef::Provider::Service::Simple
+ # to maintain a local state of service across restart's internal calls
+ attr_accessor :upstart_service_running
+
provides :service, platform_family: "debian", override: true do |node|
Chef::Platform::ServiceHelpers.service_resource_providers.include?(:upstart)
end
UPSTART_STATE_FORMAT = /\S+ \(?(start|stop)?\)? ?[\/ ](\w+)/
+ # Returns true if the configs for the service name has upstart variable
def self.supports?(resource, action)
Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:upstart)
end
@@ -80,8 +83,11 @@ class Chef
shared_resource_requirements
requirements.assert(:all_actions) do |a|
if !@command_success
- whyrun_msg = @new_resource.status_command ? "Provided status command #{@new_resource.status_command} failed." :
- "Could not determine upstart state for service"
+ whyrun_msg = if @new_resource.status_command
+ "Provided status command #{@new_resource.status_command} failed."
+ else
+ "Could not determine upstart state for service"
+ end
end
a.assertion { @command_success }
# no failure here, just document the assumptions made.
@@ -103,42 +109,42 @@ class Chef
# We do not support searching for a service via ps when using upstart since status is a native
# upstart function. We will however support status_command in case someone wants to do something special.
if @new_resource.status_command
- Chef::Log.debug("#{@new_resource} you have specified a status command, running..")
+ logger.trace("#{@new_resource} you have specified a status command, running..")
begin
if shell_out!(@new_resource.status_command).exitstatus == 0
- @current_resource.running true
+ @upstart_service_running = true
end
rescue
@command_success = false
- @current_resource.running false
+ @upstart_service_running = false
nil
end
else
begin
- if upstart_state == "running"
- @current_resource.running true
+ if upstart_goal_state == "start"
+ @upstart_service_running = true
else
- @current_resource.running false
+ @upstart_service_running = false
end
rescue Chef::Exceptions::Exec
@command_success = false
- @current_resource.running false
+ @upstart_service_running = false
nil
end
end
# Get enabled/disabled state by reading job configuration file
if ::File.exists?("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
- Chef::Log.debug("#{@new_resource} found #{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
+ logger.trace("#{@new_resource} found #{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
::File.open("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}", "r") do |file|
while line = file.gets
case line
when /^start on/
- Chef::Log.debug("#{@new_resource} enabled: #{line.chomp}")
+ logger.trace("#{@new_resource} enabled: #{line.chomp}")
@current_resource.enabled true
break
when /^#start on/
- Chef::Log.debug("#{@new_resource} disabled: #{line.chomp}")
+ logger.trace("#{@new_resource} disabled: #{line.chomp}")
@current_resource.enabled false
break
end
@@ -146,18 +152,19 @@ class Chef
end
else
@config_file_found = false
- Chef::Log.debug("#{@new_resource} did not find #{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
+ logger.trace("#{@new_resource} did not find #{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
@current_resource.enabled false
end
+ @current_resource.running @upstart_service_running
@current_resource
end
def start_service
# Calling start on a service that is already started will return 1
# Our 'goal' when we call start is to ensure the service is started
- if @current_resource.running
- Chef::Log.debug("#{@new_resource} already running, not starting")
+ if @upstart_service_running
+ logger.trace("#{@new_resource} already running, not starting")
else
if @new_resource.start_command
super
@@ -165,13 +172,15 @@ class Chef
shell_out_with_systems_locale!("/sbin/start #{@job}")
end
end
+
+ @upstart_service_running = true
end
def stop_service
# Calling stop on a service that is already stopped will return 1
# Our 'goal' when we call stop is to ensure the service is stopped
- unless @current_resource.running
- Chef::Log.debug("#{@new_resource} not running, not stopping")
+ unless @upstart_service_running
+ logger.trace("#{@new_resource} not running, not stopping")
else
if @new_resource.stop_command
super
@@ -179,6 +188,8 @@ class Chef
shell_out_with_systems_locale!("/sbin/stop #{@job}")
end
end
+
+ @upstart_service_running = false
end
def restart_service
@@ -186,13 +197,19 @@ class Chef
super
# Upstart always provides restart functionality so we don't need to mimic it with stop/sleep/start.
# Older versions of upstart would fail on restart if the service was currently stopped, check for that. LP:430883
+ # But for safe working of latest upstart job config being loaded, 'restart' can't be used as per link
+ # http://upstart.ubuntu.com/cookbook/#restart (it doesn't uses latest jon config from disk but retains old)
else
- if @current_resource.running
- shell_out_with_systems_locale!("/sbin/restart #{@job}")
+ if @upstart_service_running
+ stop_service
+ sleep 1
+ start_service
else
start_service
end
end
+
+ @upstart_service_running = true
end
def reload_service
@@ -202,37 +219,38 @@ class Chef
# upstart >= 0.6.3-4 supports reload (HUP)
shell_out_with_systems_locale!("/sbin/reload #{@job}")
end
+
+ @upstart_service_running = true
end
# https://bugs.launchpad.net/upstart/+bug/94065
def enable_service
- Chef::Log.debug("#{@new_resource} upstart lacks inherent support for enabling services, editing job config file")
+ logger.trace("#{@new_resource} upstart lacks inherent support for enabling services, editing job config file")
conf = Chef::Util::FileEdit.new("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
conf.search_file_replace(/^#start on/, "start on")
conf.write_file
end
def disable_service
- Chef::Log.debug("#{@new_resource} upstart lacks inherent support for disabling services, editing job config file")
+ logger.trace("#{@new_resource} upstart lacks inherent support for disabling services, editing job config file")
conf = Chef::Util::FileEdit.new("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
conf.search_file_replace(/^start on/, "#start on")
conf.write_file
end
- def upstart_state
+ def upstart_goal_state
command = "/sbin/status #{@job}"
- status = popen4(command) do |pid, stdin, stdout, stderr|
- stdout.each_line do |line|
- # service goal/state
- # OR
- # service (instance) goal/state
- # OR
- # service (goal) state
- line =~ UPSTART_STATE_FORMAT
- data = Regexp.last_match
- return data[2]
- end
+ so = shell_out(command)
+ so.stdout.each_line do |line|
+ # service goal/state
+ # OR
+ # service (instance) goal/state
+ # OR
+ # service (goal) state
+ line =~ UPSTART_STATE_FORMAT
+ data = Regexp.last_match
+ return data[1]
end
end
diff --git a/lib/chef/provider/service/windows.rb b/lib/chef/provider/service/windows.rb
index 9bfd9238cd..417ec03ef4 100644
--- a/lib/chef/provider/service/windows.rb
+++ b/lib/chef/provider/service/windows.rb
@@ -2,7 +2,7 @@
# Author:: Nuo Yan <nuo@chef.io>
# Author:: Bryan McLellan <btm@loftninjas.org>
# Author:: Seth Chisamore <schisamo@chef.io>
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright 2010-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,6 +19,7 @@
#
require "chef/provider/service/simple"
+require "chef/win32_service_constants"
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
require "chef/win32/error"
require "win32/service"
@@ -26,10 +27,11 @@ end
class Chef::Provider::Service::Windows < Chef::Provider::Service
provides :service, os: "windows"
- provides :windows_service, os: "windows"
+ provides :windows_service
include Chef::Mixin::ShellOut
include Chef::ReservedNames::Win32::API::Error rescue LoadError
+ include Chef::Win32ServiceConstants
#Win32::Service.get_start_type
AUTO_START = "auto start"
@@ -49,23 +51,34 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
SERVICE_RIGHT = "SeServiceLogonRight"
- def whyrun_supported?
- false
- end
-
def load_current_resource
- @current_resource = Chef::Resource::WindowsService.new(@new_resource.name)
- @current_resource.service_name(@new_resource.service_name)
- @current_resource.running(current_state == RUNNING)
- Chef::Log.debug "#{@new_resource} running: #{@current_resource.running}"
- case current_start_type
- when AUTO_START
- @current_resource.enabled(true)
- when DISABLED
- @current_resource.enabled(false)
+ @current_resource = Chef::Resource::WindowsService.new(new_resource.name)
+ current_resource.service_name(new_resource.service_name)
+
+ if Win32::Service.exists?(current_resource.service_name)
+ current_resource.running(current_state == RUNNING)
+ logger.trace "#{new_resource} running: #{current_resource.running}"
+ case current_startup_type
+ when :automatic
+ current_resource.enabled(true)
+ when :disabled
+ current_resource.enabled(false)
+ end
+ logger.trace "#{new_resource} enabled: #{current_resource.enabled}"
+
+ config_info = Win32::Service.config_info(current_resource.service_name)
+ current_resource.service_type(get_service_type(config_info.service_type)) if config_info.service_type
+ current_resource.startup_type(start_type_to_sym(config_info.start_type)) if config_info.start_type
+ current_resource.error_control(get_error_control(config_info.error_control)) if config_info.error_control
+ current_resource.binary_path_name(config_info.binary_path_name) if config_info.binary_path_name
+ current_resource.load_order_group(config_info.load_order_group) if config_info.load_order_group
+ current_resource.dependencies(config_info.dependencies) if config_info.dependencies
+ current_resource.run_as_user(config_info.service_start_name) if config_info.service_start_name
+ current_resource.display_name(config_info.display_name) if config_info.display_name
+ current_resource.delayed_start(current_delayed_start) if current_delayed_start
end
- Chef::Log.debug "#{@new_resource} enabled: #{@current_resource.enabled}"
- @current_resource
+
+ current_resource
end
def start_service
@@ -78,9 +91,10 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
}.reject { |k, v| v.nil? || v.length == 0 }
Win32::Service.configure(new_config)
- Chef::Log.info "#{@new_resource} configured with #{new_config.inspect}"
+ logger.info "#{@new_resource} configured with #{new_config.inspect}"
- if new_config.has_key?(:service_start_name)
+ # LocalSystem is the default runas user, which is a special service account that should ultimately have the rights of BUILTIN\Administrators, but we wouldn't see that from get_account_right
+ if new_config.has_key?(:service_start_name) && new_config[:service_start_name].casecmp("localsystem") != 0
unless Chef::ReservedNames::Win32::Security.get_account_right(canonicalize_username(new_config[:service_start_name])).include?(SERVICE_RIGHT)
grant_service_logon(new_config[:service_start_name])
end
@@ -88,13 +102,13 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
state = current_state
if state == RUNNING
- Chef::Log.debug "#{@new_resource} already started - nothing to do"
+ logger.trace "#{@new_resource} already started - nothing to do"
elsif state == START_PENDING
- Chef::Log.debug "#{@new_resource} already sent start signal - waiting for start"
+ logger.trace "#{@new_resource} already sent start signal - waiting for start"
wait_for_state(RUNNING)
elsif state == STOPPED
if @new_resource.start_command
- Chef::Log.debug "#{@new_resource} starting service using the given start_command"
+ logger.trace "#{@new_resource} starting service using the given start_command"
shell_out!(@new_resource.start_command)
else
spawn_command_thread do
@@ -102,7 +116,7 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
Win32::Service.start(@new_resource.service_name)
rescue SystemCallError => ex
if ex.errno == ERROR_SERVICE_LOGON_FAILED
- Chef::Log.error ex.message
+ logger.error ex.message
raise Chef::Exceptions::Service,
"Service #{@new_resource} did not start due to a logon failure (error #{ERROR_SERVICE_LOGON_FAILED}): possibly the specified user '#{@new_resource.run_as_user}' does not have the 'log on as a service' privilege, or the password is incorrect."
else
@@ -117,7 +131,7 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
raise Chef::Exceptions::Service, "Service #{@new_resource} can't be started from state [#{state}]"
end
else
- Chef::Log.debug "#{@new_resource} does not exist - nothing to do"
+ logger.trace "#{@new_resource} does not exist - nothing to do"
end
end
@@ -126,7 +140,7 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
state = current_state
if state == RUNNING
if @new_resource.stop_command
- Chef::Log.debug "#{@new_resource} stopping service using the given stop_command"
+ logger.trace "#{@new_resource} stopping service using the given stop_command"
shell_out!(@new_resource.stop_command)
else
spawn_command_thread do
@@ -136,22 +150,22 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
end
@new_resource.updated_by_last_action(true)
elsif state == STOPPED
- Chef::Log.debug "#{@new_resource} already stopped - nothing to do"
+ logger.trace "#{@new_resource} already stopped - nothing to do"
elsif state == STOP_PENDING
- Chef::Log.debug "#{@new_resource} already sent stop signal - waiting for stop"
+ logger.trace "#{@new_resource} already sent stop signal - waiting for stop"
wait_for_state(STOPPED)
else
raise Chef::Exceptions::Service, "Service #{@new_resource} can't be stopped from state [#{state}]"
end
else
- Chef::Log.debug "#{@new_resource} does not exist - nothing to do"
+ logger.trace "#{@new_resource} does not exist - nothing to do"
end
end
def restart_service
if Win32::Service.exists?(@new_resource.service_name)
if @new_resource.restart_command
- Chef::Log.debug "#{@new_resource} restarting service using the given restart_command"
+ logger.trace "#{@new_resource} restarting service using the given restart_command"
shell_out!(@new_resource.restart_command)
else
stop_service
@@ -159,7 +173,7 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
end
@new_resource.updated_by_last_action(true)
else
- Chef::Log.debug "#{@new_resource} does not exist - nothing to do"
+ logger.trace "#{@new_resource} does not exist - nothing to do"
end
end
@@ -167,7 +181,7 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
if Win32::Service.exists?(@new_resource.service_name)
set_startup_type(:automatic)
else
- Chef::Log.debug "#{@new_resource} does not exist - nothing to do"
+ logger.trace "#{@new_resource} does not exist - nothing to do"
end
end
@@ -175,62 +189,88 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
if Win32::Service.exists?(@new_resource.service_name)
set_startup_type(:disabled)
else
- Chef::Log.debug "#{@new_resource} does not exist - nothing to do"
+ logger.trace "#{@new_resource} does not exist - nothing to do"
+ end
+ end
+
+ action :create do
+ if Win32::Service.exists?(new_resource.service_name)
+ logger.trace "#{new_resource} already exists - nothing to do"
+ return
+ end
+
+ converge_by("create service #{new_resource.service_name}") do
+ Win32::Service.new(windows_service_config)
end
+
+ converge_delayed_start
+ end
+
+ action :delete do
+ unless Win32::Service.exists?(new_resource.service_name)
+ logger.trace "#{new_resource} does not exist - nothing to do"
+ return
+ end
+
+ converge_by("delete service #{new_resource.service_name}") do
+ Win32::Service.delete(new_resource.service_name)
+ end
+ end
+
+ action :configure do
+ unless Win32::Service.exists?(new_resource.service_name)
+ logger.warn "#{new_resource} does not exist. Maybe you need to prepend action :create"
+ return
+ end
+
+ # Until #6300 is solved this is required
+ if new_resource.run_as_user == new_resource.class.properties[:run_as_user].default
+ new_resource.run_as_user = new_resource.class.properties[:run_as_user].default
+ end
+
+ converge_if_changed :service_type, :startup_type, :error_control,
+ :binary_path_name, :load_order_group, :dependencies,
+ :run_as_user, :display_name, :description do
+ Win32::Service.configure(windows_service_config(:configure))
+ end
+
+ converge_delayed_start
end
def action_enable
- if current_start_type != AUTO_START
+ if current_startup_type != :automatic
converge_by("enable service #{@new_resource}") do
enable_service
- Chef::Log.info("#{@new_resource} enabled")
+ logger.info("#{@new_resource} enabled")
end
else
- Chef::Log.debug("#{@new_resource} already enabled - nothing to do")
+ logger.trace("#{@new_resource} already enabled - nothing to do")
end
load_new_resource_state
@new_resource.enabled(true)
end
def action_disable
- if current_start_type != DISABLED
+ if current_startup_type != :disabled
converge_by("disable service #{@new_resource}") do
disable_service
- Chef::Log.info("#{@new_resource} disabled")
+ logger.info("#{@new_resource} disabled")
end
else
- Chef::Log.debug("#{@new_resource} already disabled - nothing to do")
+ logger.trace("#{@new_resource} already disabled - nothing to do")
end
load_new_resource_state
@new_resource.enabled(false)
end
def action_configure_startup
- case @new_resource.startup_type
- when :automatic
- if current_start_type != AUTO_START
- converge_by("set service #{@new_resource} startup type to automatic") do
- set_startup_type(:automatic)
- end
- else
- Chef::Log.debug("#{@new_resource} startup_type already automatic - nothing to do")
- end
- when :manual
- if current_start_type != MANUAL
- converge_by("set service #{@new_resource} startup type to manual") do
- set_startup_type(:manual)
- end
- else
- Chef::Log.debug("#{@new_resource} startup_type already manual - nothing to do")
- end
- when :disabled
- if current_start_type != DISABLED
- converge_by("set service #{@new_resource} startup type to disabled") do
- set_startup_type(:disabled)
- end
- else
- Chef::Log.debug("#{@new_resource} startup_type already disabled - nothing to do")
+ startup_type = @new_resource.startup_type
+ if current_startup_type != startup_type
+ converge_by("set service #{@new_resource} startup type to #{startup_type}") do
+ set_startup_type(startup_type)
end
+ else
+ logger.trace("#{@new_resource} startup_type already #{startup_type} - nothing to do")
end
# Avoid changing enabled from true/false for now
@@ -239,15 +279,23 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
private
+ def current_delayed_start
+ if service = Win32::Service.services.find { |x| x.service_name == new_resource.service_name }
+ service.delayed_start == 0 ? false : true
+ else
+ nil
+ end
+ end
+
def grant_service_logon(username)
begin
Chef::ReservedNames::Win32::Security.add_account_right(canonicalize_username(username), SERVICE_RIGHT)
rescue Chef::Exceptions::Win32APIError => err
- Chef::Log.fatal "Logon-as-service grant failed with output: #{err}"
+ logger.fatal "Logon-as-service grant failed with output: #{err}"
raise Chef::Exceptions::Service, "Logon-as-service grant failed for #{username}: #{err}"
end
- Chef::Log.info "Grant logon-as-service to user '#{username}' successful."
+ logger.info "Grant logon-as-service to user '#{username}' successful."
true
end
@@ -264,8 +312,9 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
Win32::Service.status(@new_resource.service_name).current_state
end
- def current_start_type
- Win32::Service.config_info(@new_resource.service_name).start_type
+ def current_startup_type
+ start_type = Win32::Service.config_info(@new_resource.service_name).start_type
+ start_type_to_sym(start_type)
end
# Helper method that waits for a status to change its state since state
@@ -293,21 +342,143 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
end
end
- # Takes Win32::Service start_types
- def set_startup_type(type)
- # Set-Service Startup Type => Win32::Service Constant
- allowed_types = { :automatic => Win32::Service::AUTO_START,
- :manual => Win32::Service::DEMAND_START,
- :disabled => Win32::Service::DISABLED }
- unless allowed_types.keys.include?(type)
+ # @param type [Symbol]
+ # @return [Integer]
+ # @raise [Chef::Exceptions::ConfigurationError] if the startup type is
+ # not supported.
+ # @see Chef::Resource::WindowsService::ALLOWED_START_TYPES
+ def startup_type_to_int(type)
+ Chef::Resource::WindowsService::ALLOWED_START_TYPES.fetch(type) do
raise Chef::Exceptions::ConfigurationError, "#{@new_resource.name}: Startup type '#{type}' is not supported"
end
+ end
- Chef::Log.debug "#{@new_resource.name} setting start_type to #{type}"
+ # Takes Win32::Service start_types
+ def set_startup_type(type)
+ startup_type = startup_type_to_int(type)
+
+ logger.trace "#{@new_resource.name} setting start_type to #{type}"
Win32::Service.configure(
:service_name => @new_resource.service_name,
- :start_type => allowed_types[type]
+ :start_type => startup_type
)
@new_resource.updated_by_last_action(true)
end
+
+ def windows_service_config(action = :create)
+ config = {}
+
+ config[:service_name] = new_resource.service_name
+ config[:display_name] = new_resource.display_name if new_resource.display_name
+ config[:service_type] = new_resource.service_type if new_resource.service_type
+ config[:start_type] = startup_type_to_int(new_resource.startup_type) if new_resource.startup_type
+ config[:error_control] = new_resource.error_control if new_resource.error_control
+ config[:binary_path_name] = new_resource.binary_path_name if new_resource.binary_path_name
+ config[:load_order_group] = new_resource.load_order_group if new_resource.load_order_group
+ config[:dependencies] = new_resource.dependencies if new_resource.dependencies
+ config[:service_start_name] = new_resource.run_as_user unless new_resource.run_as_user.empty?
+ config[:password] = new_resource.run_as_password unless new_resource.run_as_user.empty? || new_resource.run_as_password.empty?
+ config[:description] = new_resource.description if new_resource.description
+
+ case action
+ when :create
+ config[:desired_access] = new_resource.desired_access if new_resource.desired_access
+ end
+
+ config
+ end
+
+ def converge_delayed_start
+ config = {}
+ config[:service_name] = new_resource.service_name
+ config[:delayed_start] = new_resource.delayed_start ? 1 : 0
+
+ # Until #6300 is solved this is required
+ if new_resource.delayed_start == new_resource.class.properties[:delayed_start].default
+ new_resource.delayed_start = new_resource.class.properties[:delayed_start].default
+ end
+
+ converge_if_changed :delayed_start do
+ Win32::Service.configure(config)
+ end
+ end
+
+ # @return [Symbol]
+ def start_type_to_sym(start_type)
+ case start_type
+ when "auto start"
+ :automatic
+ when "boot start"
+ raise("Unsupported start type, #{start_type}. Submit bug request to fix.")
+ when "demand start"
+ :manual
+ when "disabled"
+ :disabled
+ when "system start"
+ raise("Unsupported start type, #{start_type}. Submit bug request to fix.")
+ else
+ raise("Unsupported start type, #{start_type}. Submit bug request to fix.")
+ end
+ end
+
+ def get_service_type(service_type)
+ case service_type
+ when "file system driver"
+ SERVICE_FILE_SYSTEM_DRIVER
+ when "kernel driver"
+ SERVICE_KERNEL_DRIVER
+ when "own process"
+ SERVICE_WIN32_OWN_PROCESS
+ when "share process"
+ SERVICE_WIN32_SHARE_PROCESS
+ when "recognizer driver"
+ SERVICE_RECOGNIZER_DRIVER
+ when "driver"
+ SERVICE_DRIVER
+ when "win32"
+ SERVICE_WIN32
+ when "all"
+ SERVICE_TYPE_ALL
+ when "own process, interactive"
+ SERVICE_INTERACTIVE_PROCESS | SERVICE_WIN32_OWN_PROCESS
+ when "share process, interactive"
+ SERVICE_INTERACTIVE_PROCESS | SERVICE_WIN32_SHARE_PROCESS
+ else
+ raise("Unsupported service type, #{service_type}. Submit bug request to fix.")
+ end
+ end
+
+ # @return [Integer]
+ def get_start_type(start_type)
+ case start_type
+ when "auto start"
+ SERVICE_AUTO_START
+ when "boot start"
+ SERVICE_BOOT_START
+ when "demand start"
+ SERVICE_DEMAND_START
+ when "disabled"
+ SERVICE_DISABLED
+ when "system start"
+ SERVICE_SYSTEM_START
+ else
+ raise("Unsupported start type, #{start_type}. Submit bug request to fix.")
+ end
+ end
+
+ def get_error_control(error_control)
+ case error_control
+ when "critical"
+ SERVICE_ERROR_CRITICAL
+ when "ignore"
+ SERVICE_ERROR_IGNORE
+ when "normal"
+ SERVICE_ERROR_NORMAL
+ when "severe"
+ SERVICE_ERROR_SEVERE
+ else
+ nil
+ end
+ end
+
end
diff --git a/lib/chef/provider/subversion.rb b/lib/chef/provider/subversion.rb
index ea32283bc9..abcc260a78 100644
--- a/lib/chef/provider/subversion.rb
+++ b/lib/chef/provider/subversion.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,7 +20,6 @@
require "chef/log"
require "chef/provider"
-require "chef/mixin/command"
require "chef-config/mixin/fuzzy_hostname_matcher"
require "fileutils"
@@ -32,19 +31,14 @@ class Chef
SVN_INFO_PATTERN = /^([\w\s]+): (.+)$/
- include Chef::Mixin::Command
include ChefConfig::Mixin::FuzzyHostnameMatcher
- def whyrun_supported?
- true
- end
-
def load_current_resource
- @current_resource = Chef::Resource::Subversion.new(@new_resource.name)
+ @current_resource = Chef::Resource::Subversion.new(new_resource.name)
- unless [:export, :force_export].include?(Array(@new_resource.action).first)
+ unless [:export, :force_export].include?(Array(new_resource.action).first)
if current_revision = find_current_revision
- @current_resource.revision current_revision
+ current_resource.revision current_revision
end
end
end
@@ -53,21 +47,21 @@ class Chef
requirements.assert(:all_actions) do |a|
# Make sure the parent dir exists, or else fail.
# for why run, print a message explaining the potential error.
- parent_directory = ::File.dirname(@new_resource.destination)
+ parent_directory = ::File.dirname(new_resource.destination)
a.assertion { ::File.directory?(parent_directory) }
a.failure_message(Chef::Exceptions::MissingParentDirectory,
- "Cannot clone #{@new_resource} to #{@new_resource.destination}, the enclosing directory #{parent_directory} does not exist")
+ "Cannot clone #{new_resource} to #{new_resource.destination}, the enclosing directory #{parent_directory} does not exist")
a.whyrun("Directory #{parent_directory} does not exist, assuming it would have been created")
end
end
def action_checkout
if target_dir_non_existent_or_empty?
- converge_by("perform checkout of #{@new_resource.repository} into #{@new_resource.destination}") do
+ converge_by("perform checkout of #{new_resource.repository} into #{new_resource.destination}") do
shell_out!(checkout_command, run_options)
end
else
- Chef::Log.debug "#{@new_resource} checkout destination #{@new_resource.destination} already exists or is a non-empty directory - nothing to do"
+ logger.trace "#{new_resource} checkout destination #{new_resource.destination} already exists or is a non-empty directory - nothing to do"
end
end
@@ -75,25 +69,25 @@ class Chef
if target_dir_non_existent_or_empty?
action_force_export
else
- Chef::Log.debug "#{@new_resource} export destination #{@new_resource.destination} already exists or is a non-empty directory - nothing to do"
+ logger.trace "#{new_resource} export destination #{new_resource.destination} already exists or is a non-empty directory - nothing to do"
end
end
def action_force_export
- converge_by("export #{@new_resource.repository} into #{@new_resource.destination}") do
+ converge_by("export #{new_resource.repository} into #{new_resource.destination}") do
shell_out!(export_command, run_options)
end
end
def action_sync
assert_target_directory_valid!
- if ::File.exist?(::File.join(@new_resource.destination, ".svn"))
+ if ::File.exist?(::File.join(new_resource.destination, ".svn"))
current_rev = find_current_revision
- Chef::Log.debug "#{@new_resource} current revision: #{current_rev} target revision: #{revision_int}"
+ logger.trace "#{new_resource} current revision: #{current_rev} target revision: #{revision_int}"
unless current_revision_matches_target_revision?
- converge_by("sync #{@new_resource.destination} from #{@new_resource.repository}") do
+ converge_by("sync #{new_resource.destination} from #{new_resource.repository}") do
shell_out!(sync_command, run_options)
- Chef::Log.info "#{@new_resource} updated to revision: #{revision_int}"
+ logger.info "#{new_resource} updated to revision: #{revision_int}"
end
end
else
@@ -102,24 +96,24 @@ class Chef
end
def sync_command
- c = scm :update, @new_resource.svn_arguments, verbose, authentication, proxy, "-r#{revision_int}", @new_resource.destination
- Chef::Log.debug "#{@new_resource} updated working copy #{@new_resource.destination} to revision #{@new_resource.revision}"
+ c = scm :update, new_resource.svn_arguments, verbose, authentication, proxy, "-r#{revision_int}", new_resource.destination
+ logger.trace "#{new_resource} updated working copy #{new_resource.destination} to revision #{new_resource.revision}"
c
end
def checkout_command
- c = scm :checkout, @new_resource.svn_arguments, verbose, authentication, proxy,
- "-r#{revision_int}", @new_resource.repository, @new_resource.destination
- Chef::Log.info "#{@new_resource} checked out #{@new_resource.repository} at revision #{@new_resource.revision} to #{@new_resource.destination}"
+ c = scm :checkout, new_resource.svn_arguments, verbose, authentication, proxy,
+ "-r#{revision_int}", new_resource.repository, new_resource.destination
+ logger.info "#{new_resource} checked out #{new_resource.repository} at revision #{new_resource.revision} to #{new_resource.destination}"
c
end
def export_command
args = ["--force"]
- args << @new_resource.svn_arguments << verbose << authentication << proxy <<
- "-r#{revision_int}" << @new_resource.repository << @new_resource.destination
+ args << new_resource.svn_arguments << verbose << authentication << proxy <<
+ "-r#{revision_int}" << new_resource.repository << new_resource.destination
c = scm :export, *args
- Chef::Log.info "#{@new_resource} exported #{@new_resource.repository} at revision #{@new_resource.revision} to #{@new_resource.destination}"
+ logger.info "#{new_resource} exported #{new_resource.repository} at revision #{new_resource.revision} to #{new_resource.destination}"
c
end
@@ -128,10 +122,10 @@ class Chef
# If the specified revision is an integer, trust it.
def revision_int
@revision_int ||= begin
- if @new_resource.revision =~ /^\d+$/
- @new_resource.revision
+ if new_resource.revision =~ /^\d+$/
+ new_resource.revision
else
- command = scm(:info, @new_resource.repository, @new_resource.svn_info_args, authentication, "-r#{@new_resource.revision}")
+ command = scm(:info, new_resource.repository, new_resource.svn_info_args, authentication, "-r#{new_resource.revision}")
svn_info = shell_out!(command, run_options(:cwd => cwd, :returns => [0, 1])).stdout
extract_revision_info(svn_info)
@@ -142,7 +136,7 @@ class Chef
alias :revision_slug :revision_int
def find_current_revision
- return nil unless ::File.exist?(::File.join(@new_resource.destination, ".svn"))
+ return nil unless ::File.exist?(::File.join(new_resource.destination, ".svn"))
command = scm(:info)
svn_info = shell_out!(command, run_options(:cwd => cwd, :returns => [0, 1])).stdout
@@ -150,20 +144,20 @@ class Chef
end
def current_revision_matches_target_revision?
- (!@current_resource.revision.nil?) && (revision_int.strip.to_i == @current_resource.revision.strip.to_i)
+ (!current_resource.revision.nil?) && (revision_int.strip.to_i == current_resource.revision.strip.to_i)
end
def run_options(run_opts = {})
- run_opts[:user] = @new_resource.user if @new_resource.user
- run_opts[:group] = @new_resource.group if @new_resource.group
- run_opts[:timeout] = @new_resource.timeout if @new_resource.timeout
+ run_opts[:user] = new_resource.user if new_resource.user
+ run_opts[:group] = new_resource.group if new_resource.group
+ run_opts[:timeout] = new_resource.timeout if new_resource.timeout
run_opts
end
private
def cwd
- @new_resource.destination
+ new_resource.destination
end
def verbose
@@ -181,7 +175,7 @@ class Chef
rev = (repo_attrs["Last Changed Rev"] || repo_attrs["Revision"])
rev.strip! if rev
raise "Could not parse `svn info` data: #{svn_info}" if repo_attrs.empty?
- Chef::Log.debug "#{@new_resource} resolved revision #{@new_resource.revision} to #{rev}"
+ logger.trace "#{new_resource} resolved revision #{new_resource.revision} to #{rev}"
rev
end
@@ -190,14 +184,14 @@ class Chef
# switch, since Capistrano will check for that prompt in the output
# and will respond appropriately.
def authentication
- return "" unless @new_resource.svn_username
- result = "--username #{@new_resource.svn_username} "
- result << "--password #{@new_resource.svn_password} "
+ return "" unless new_resource.svn_username
+ result = "--username #{new_resource.svn_username} "
+ result << "--password #{new_resource.svn_password} "
result
end
def proxy
- repo_uri = URI.parse(@new_resource.repository)
+ repo_uri = URI.parse(new_resource.repository)
proxy_uri = Chef::Config.proxy_uri(repo_uri.scheme, repo_uri.host, repo_uri.port)
return "" if proxy_uri.nil?
@@ -213,18 +207,18 @@ class Chef
end
def target_dir_non_existent_or_empty?
- !::File.exist?(@new_resource.destination) || Dir.entries(@new_resource.destination).sort == [".", ".."]
+ !::File.exist?(new_resource.destination) || Dir.entries(new_resource.destination).sort == [".", ".."]
end
def svn_binary
- @new_resource.svn_binary ||
+ new_resource.svn_binary ||
(Chef::Platform.windows? ? "svn.exe" : "svn")
end
def assert_target_directory_valid!
- target_parent_directory = ::File.dirname(@new_resource.destination)
+ target_parent_directory = ::File.dirname(new_resource.destination)
unless ::File.directory?(target_parent_directory)
- msg = "Cannot clone #{@new_resource} to #{@new_resource.destination}, the enclosing directory #{target_parent_directory} does not exist"
+ msg = "Cannot clone #{new_resource} to #{new_resource.destination}, the enclosing directory #{target_parent_directory} does not exist"
raise Chef::Exceptions::MissingParentDirectory, msg
end
end
diff --git a/lib/chef/provider/support/yum_repo.erb b/lib/chef/provider/support/yum_repo.erb
new file mode 100644
index 0000000000..f60d8688da
--- /dev/null
+++ b/lib/chef/provider/support/yum_repo.erb
@@ -0,0 +1,138 @@
+# This file was generated by Chef
+# Do NOT modify this file by hand.
+
+[<%= @config.repositoryid %>]
+name=<%= @config.description %>
+<% if @config.baseurl %>
+baseurl=<%= case @config.baseurl
+ when Array
+ @config.baseurl.join("\n")
+ else
+ @config.baseurl
+ end %>
+<% end -%>
+<% if @config.cost %>
+cost=<%= @config.cost %>
+<% end %>
+<% if @config.enabled %>
+enabled=1
+<% else %>
+enabled=0
+<% end %>
+<% if @config.enablegroups %>
+enablegroups=1
+<% end %>
+<% if @config.exclude %>
+exclude=<%= @config.exclude %>
+<% end %>
+<% if @config.failovermethod %>
+failovermethod=<%= @config.failovermethod %>
+<% end %>
+<% if @config.fastestmirror_enabled %>
+fastestmirror_enabled=1
+<% else %>
+fastestmirror_enabled=0
+<% end %>
+<% if @config.gpgcheck %>
+gpgcheck=1
+<% else %>
+gpgcheck=0
+<% end %>
+<% if @config.gpgkey %>
+gpgkey=<%= case @config.gpgkey
+ when Array
+ @config.gpgkey.join("\n ")
+ else
+ @config.gpgkey
+ end %>
+<% end -%>
+<% if @config.http_caching %>
+http_caching=<%= @config.http_caching %>
+<% end %>
+<% if @config.include_config %>
+include=<%= @config.include_config %>
+<% end %>
+<% if @config.includepkgs %>
+includepkgs=<%= @config.includepkgs %>
+<% end %>
+<% if @config.keepalive %>
+keepalive=1
+<% end %>
+<% if @config.metadata_expire %>
+metadata_expire=<%= @config.metadata_expire %>
+<% end %>
+<% if @config.metalink %>
+metalink=<%= @config.metalink %>
+<% end %>
+<% if @config.mirrorlist %>
+mirrorlist=<%= @config.mirrorlist %>
+<% end %>
+<% if @config.mirror_expire %>
+mirror_expire=<%= @config.mirror_expire %>
+<% end %>
+<% if @config.mirrorlist_expire %>
+mirrorlist_expire=<%= @config.mirrorlist_expire %>
+<% end %>
+<% if @config.priority %>
+priority=<%= @config.priority %>
+<% end %>
+<% if @config.proxy %>
+proxy=<%= @config.proxy %>
+<% end %>
+<% if @config.proxy_username %>
+proxy_username=<%= @config.proxy_username %>
+<% end %>
+<% if @config.proxy_password %>
+proxy_password=<%= @config.proxy_password %>
+<% end %>
+<% if @config.username %>
+username=<%= @config.username %>
+<% end %>
+<% if @config.password %>
+password=<%= @config.password %>
+<% end %>
+<% if @config.repo_gpgcheck %>
+repo_gpgcheck=1
+<% end %>
+<% if @config.max_retries %>
+retries=<%= @config.max_retries %>
+<% end %>
+<% if @config.report_instanceid %>
+report_instanceid=<%= @config.report_instanceid %>
+<% end %>
+<% if @config.skip_if_unavailable %>
+skip_if_unavailable=1
+<% end %>
+<% if @config.sslcacert %>
+sslcacert=<%= @config.sslcacert %>
+<% end %>
+<% if @config.sslclientcert %>
+sslclientcert=<%= @config.sslclientcert %>
+<% end %>
+<% if @config.sslclientkey %>
+sslclientkey=<%= @config.sslclientkey %>
+<% end %>
+<% unless @config.sslverify.nil? %>
+sslverify=<%= ( @config.sslverify ) ? 'true' : 'false' %>
+<% end %>
+<% if @config.throttle %>
+throttle=<%= @config.throttle %>
+<% end %>
+<% if @config.timeout %>
+timeout=<%= @config.timeout %>
+<% end %>
+<% if @config.options -%>
+<% @config.options.each do |key, value| -%>
+<%= key %>=<%=
+ case value
+ when Array
+ value.join("\n ")
+ when TrueClass
+ '1'
+ when FalseClass
+ '0'
+ else
+ value
+ end %>
+<% end -%>
+<% end -%>
diff --git a/lib/chef/provider/support/zypper_repo.erb b/lib/chef/provider/support/zypper_repo.erb
new file mode 100644
index 0000000000..6d508fa77f
--- /dev/null
+++ b/lib/chef/provider/support/zypper_repo.erb
@@ -0,0 +1,17 @@
+# This file was generated by Chef
+# Do NOT modify this file by hand.
+
+[<%= @config.repo_name %>]
+<% %w{ type enabled autorefresh gpgcheck gpgkey baseurl mirrorlist path priority keeppackages mode refresh_cache }.each do |prop| -%>
+<% next if @config.send(prop.to_sym).nil? -%>
+<%= prop %>=<%=
+ case @config.send(prop.to_sym)
+ when TrueClass
+ '1'
+ when FalseClass
+ '0'
+ else
+ @config.send(prop.to_sym)
+ end %>
+<% end -%>
+name=<%= @config.description || @config.repo_name %>
diff --git a/lib/chef/provider/systemd_unit.rb b/lib/chef/provider/systemd_unit.rb
index db71a6c234..d8c83d2b4b 100644
--- a/lib/chef/provider/systemd_unit.rb
+++ b/lib/chef/provider/systemd_unit.rb
@@ -1,6 +1,6 @@
#
# Author:: Nathan Williams (<nath.e.will@gmail.com>)
-# Copyright:: Copyright 2016, Nathan Williams
+# Copyright:: Copyright 2016-2018, Nathan Williams
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,6 +20,9 @@ require "chef/provider"
require "chef/mixin/which"
require "chef/mixin/shell_out"
require "chef/resource/file"
+require "chef/resource/file/verification/systemd_unit"
+require "iniparse"
+require "shellwords"
class Chef
class Provider
@@ -27,11 +30,12 @@ class Chef
include Chef::Mixin::Which
include Chef::Mixin::ShellOut
- provides :systemd_unit, os: "linux"
+ provides :systemd_unit
def load_current_resource
@current_resource = Chef::Resource::SystemdUnit.new(new_resource.name)
+ current_resource.unit_name(new_resource.unit_name)
current_resource.content(::File.read(unit_path)) if ::File.exist?(unit_path)
current_resource.user(new_resource.user)
current_resource.enabled(enabled?)
@@ -43,9 +47,18 @@ class Chef
current_resource
end
+ def define_resource_requirements
+ super
+
+ requirements.assert(:create) do |a|
+ a.assertion { IniParse.parse(new_resource.to_ini) }
+ a.failure_message "Unit content is not valid INI text"
+ end
+ end
+
def action_create
if current_resource.content != new_resource.to_ini
- converge_by("creating unit: #{new_resource.name}") do
+ converge_by("creating unit: #{new_resource.unit_name}") do
manage_unit_file(:create)
daemon_reload if new_resource.triggers_reload
end
@@ -54,108 +67,144 @@ class Chef
def action_delete
if ::File.exist?(unit_path)
- converge_by("deleting unit: #{new_resource.name}") do
+ converge_by("deleting unit: #{new_resource.unit_name}") do
manage_unit_file(:delete)
daemon_reload if new_resource.triggers_reload
end
end
end
+ def action_preset
+ converge_by("restoring enable/disable preset configuration for unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:preset, new_resource.unit_name)
+ end
+ end
+
+ def action_revert
+ converge_by("reverting to vendor version of unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:revert, new_resource.unit_name)
+ end
+ end
+
def action_enable
if current_resource.static
- Chef::Log.debug("#{new_resource.name} is a static unit, enabling is a NOP.")
+ logger.trace("#{new_resource.unit_name} is a static unit, enabling is a NOP.")
end
unless current_resource.enabled || current_resource.static
- converge_by("enabling unit: #{new_resource.name}") do
- systemctl_execute!(:enable, new_resource.name)
+ converge_by("enabling unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:enable, new_resource.unit_name)
end
end
end
def action_disable
if current_resource.static
- Chef::Log.debug("#{new_resource.name} is a static unit, disabling is a NOP.")
+ logger.trace("#{new_resource.unit_name} is a static unit, disabling is a NOP.")
end
if current_resource.enabled && !current_resource.static
- converge_by("disabling unit: #{new_resource.name}") do
- systemctl_execute!(:disable, new_resource.name)
+ converge_by("disabling unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:disable, new_resource.unit_name)
end
end
end
+ def action_reenable
+ converge_by("reenabling unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:reenable, new_resource.unit_name)
+ end
+ end
+
def action_mask
unless current_resource.masked
- converge_by("masking unit: #{new_resource.name}") do
- systemctl_execute!(:mask, new_resource.name)
+ converge_by("masking unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:mask, new_resource.unit_name)
end
end
end
def action_unmask
if current_resource.masked
- converge_by("unmasking unit: #{new_resource.name}") do
- systemctl_execute!(:unmask, new_resource.name)
+ converge_by("unmasking unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:unmask, new_resource.unit_name)
end
end
end
def action_start
unless current_resource.active
- converge_by("starting unit: #{new_resource.name}") do
- systemctl_execute!(:start, new_resource.name)
+ converge_by("starting unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:start, new_resource.unit_name)
end
end
end
def action_stop
if current_resource.active
- converge_by("stopping unit: #{new_resource.name}") do
- systemctl_execute!(:stop, new_resource.name)
+ converge_by("stopping unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:stop, new_resource.unit_name)
end
end
end
def action_restart
- converge_by("restarting unit: #{new_resource.name}") do
- systemctl_execute!(:restart, new_resource.name)
+ converge_by("restarting unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:restart, new_resource.unit_name)
end
end
def action_reload
if current_resource.active
- converge_by("reloading unit: #{new_resource.name}") do
- systemctl_execute!(:reload, new_resource.name)
+ converge_by("reloading unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:reload, new_resource.unit_name)
end
else
- Chef::Log.debug("#{new_resource.name} is not active, skipping reload.")
+ logger.trace("#{new_resource.unit_name} is not active, skipping reload.")
+ end
+ end
+
+ def action_try_restart
+ converge_by("try-restarting unit: #{new_resource.unit_name}") do
+ systemctl_execute!("try-restart", new_resource.unit_name)
+ end
+ end
+
+ def action_reload_or_restart
+ converge_by("reload-or-restarting unit: #{new_resource.unit_name}") do
+ systemctl_execute!("reload-or-restart", new_resource.unit_name)
+ end
+ end
+
+ def action_reload_or_try_restart
+ converge_by("reload-or-try-restarting unit: #{new_resource.unit_name}") do
+ systemctl_execute!("reload-or-try-restart", new_resource.unit_name)
end
end
def active?
- systemctl_execute("is-active", new_resource.name).exitstatus == 0
+ systemctl_execute("is-active", new_resource.unit_name).exitstatus == 0
end
def enabled?
- systemctl_execute("is-enabled", new_resource.name).exitstatus == 0
+ systemctl_execute("is-enabled", new_resource.unit_name).exitstatus == 0
end
def masked?
- systemctl_execute(:status, new_resource.name).stdout.include?("masked")
+ systemctl_execute(:status, new_resource.unit_name).stdout.include?("masked")
end
def static?
- systemctl_execute("is-enabled", new_resource.name).stdout.include?("static")
+ systemctl_execute("is-enabled", new_resource.unit_name).stdout.include?("static")
end
private
def unit_path
if new_resource.user
- "/etc/systemd/user/#{new_resource.name}"
+ "/etc/systemd/user/#{new_resource.unit_name}"
else
- "/etc/systemd/system/#{new_resource.name}"
+ "/etc/systemd/system/#{new_resource.unit_name}"
end
end
@@ -165,6 +214,7 @@ class Chef
f.group "root"
f.mode "0644"
f.content new_resource.to_ini
+ f.verify :systemd_unit if new_resource.verify
end.run_action(action)
end
@@ -173,11 +223,11 @@ class Chef
end
def systemctl_execute!(action, unit)
- shell_out_with_systems_locale!("#{systemctl_cmd} #{action} #{unit}", systemctl_opts)
+ shell_out_with_systems_locale!("#{systemctl_cmd} #{action} #{Shellwords.escape(unit)}", systemctl_opts)
end
def systemctl_execute(action, unit)
- shell_out("#{systemctl_cmd} #{action} #{unit}", systemctl_opts)
+ shell_out("#{systemctl_cmd} #{action} #{Shellwords.escape(unit)}", systemctl_opts)
end
def systemctl_cmd
@@ -195,10 +245,11 @@ class Chef
def systemctl_opts
@systemctl_opts ||=
if new_resource.user
+ uid = Etc.getpwuid(new_resource.user).uid
{
- "user" => new_resource.user,
- "environment" => {
- "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/#{node['etc']['passwd'][new_resource.user]['uid']}/bus",
+ :user => new_resource.user,
+ :environment => {
+ "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/#{uid}/bus",
},
}
else
diff --git a/lib/chef/provider/template.rb b/lib/chef/provider/template.rb
index 3c46a6eb7d..05cdbdbf62 100644
--- a/lib/chef/provider/template.rb
+++ b/lib/chef/provider/template.rb
@@ -19,25 +19,19 @@
require "chef/provider/template_finder"
require "chef/provider/file"
-require "chef/deprecation/provider/template"
-require "chef/deprecation/warnings"
class Chef
class Provider
class Template < Chef::Provider::File
provides :template
- extend Chef::Deprecation::Warnings
- include Chef::Deprecation::Provider::Template
- add_deprecation_warnings_for(Chef::Deprecation::Provider::Template.instance_methods)
-
def initialize(new_resource, run_context)
@content_class = Chef::Provider::Template::Content
super
end
def load_current_resource
- @current_resource = Chef::Resource::Template.new(@new_resource.name)
+ @current_resource = Chef::Resource::Template.new(new_resource.name)
super
end
@@ -55,8 +49,8 @@ class Chef
private
def managing_content?
- return true if @new_resource.checksum
- return true if !@new_resource.source.nil? && @action != :create_if_missing
+ return true if new_resource.checksum
+ return true if !new_resource.source.nil? && @action != :create_if_missing
false
end
diff --git a/lib/chef/provider/template/content.rb b/lib/chef/provider/template/content.rb
index acf200621d..b40794564a 100644
--- a/lib/chef/provider/template/content.rb
+++ b/lib/chef/provider/template/content.rb
@@ -36,7 +36,30 @@ class Chef
private
def file_for_provider
- context = TemplateContext.new(new_resource.variables)
+ # Deal with any DelayedEvaluator values in the template variables.
+ visitor = lambda do |obj|
+ case obj
+ when Hash
+ # If this is an Attribute object, we need to change class otherwise
+ # we get the immutable behavior. This could probably be fixed by
+ # using Hash#transform_values once we only support Ruby 2.4.
+ obj_class = obj.is_a?(Chef::Node::ImmutableMash) ? Mash : obj.class
+ # Avoid mutating hashes in the resource in case we're changing anything.
+ obj.each_with_object(obj_class.new) do |(key, value), memo|
+ memo[key] = visitor.call(value)
+ end
+ when Array
+ # Avoid mutating arrays in the resource in case we're changing anything.
+ obj.map { |value| visitor.call(value) }
+ when DelayedEvaluator
+ new_resource.instance_eval(&obj)
+ else
+ obj
+ end
+ end
+ variables = visitor.call(new_resource.variables)
+
+ context = TemplateContext.new(variables)
context[:node] = run_context.node
context[:template_finder] = template_finder
diff --git a/lib/chef/provider/template_finder.rb b/lib/chef/provider/template_finder.rb
index 67342a86ea..1e8b925071 100644
--- a/lib/chef/provider/template_finder.rb
+++ b/lib/chef/provider/template_finder.rb
@@ -40,7 +40,7 @@ class Chef
cookbook.preferred_filename_on_disk_location(@node, :templates, template_name)
end
- protected
+ protected
def template_source_name(name, options)
if options[:source]
diff --git a/lib/chef/provider/user.rb b/lib/chef/provider/user.rb
index 85bd674d8d..18cf2d4d99 100644
--- a/lib/chef/provider/user.rb
+++ b/lib/chef/provider/user.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,13 +17,11 @@
#
require "chef/provider"
-require "chef/mixin/command"
require "etc"
class Chef
class Provider
class User < Chef::Provider
- include Chef::Mixin::Command
attr_accessor :user_exists, :locked
@@ -36,74 +34,70 @@ class Chef
end
def convert_group_name
- if @new_resource.gid.is_a? String
- @new_resource.gid(Etc.getgrnam(@new_resource.gid).gid)
+ if new_resource.gid.is_a? String
+ new_resource.gid(Etc.getgrnam(new_resource.gid).gid)
end
- rescue ArgumentError => e
+ rescue ArgumentError
@group_name_resolved = false
end
- def whyrun_supported?
- true
- end
-
def load_current_resource
- @current_resource = Chef::Resource::User.new(@new_resource.name)
- @current_resource.username(@new_resource.username)
+ @current_resource = Chef::Resource::User.new(new_resource.name)
+ current_resource.username(new_resource.username)
begin
- user_info = Etc.getpwnam(@new_resource.username)
- rescue ArgumentError => e
+ user_info = Etc.getpwnam(new_resource.username)
+ rescue ArgumentError
@user_exists = false
- Chef::Log.debug("#{@new_resource} user does not exist")
+ logger.trace("#{new_resource} user does not exist")
user_info = nil
end
if user_info
- @current_resource.uid(user_info.uid)
- @current_resource.gid(user_info.gid)
- @current_resource.home(user_info.dir)
- @current_resource.shell(user_info.shell)
- @current_resource.password(user_info.passwd)
-
- if @new_resource.comment
- user_info.gecos.force_encoding(@new_resource.comment.encoding)
+ current_resource.uid(user_info.uid)
+ current_resource.gid(user_info.gid)
+ current_resource.home(user_info.dir)
+ current_resource.shell(user_info.shell)
+ current_resource.password(user_info.passwd)
+
+ if new_resource.comment
+ user_info.gecos.force_encoding(new_resource.comment.encoding)
end
- @current_resource.comment(user_info.gecos)
+ current_resource.comment(user_info.gecos)
- if @new_resource.password && @current_resource.password == "x"
+ if new_resource.password && current_resource.password == "x"
begin
require "shadow"
rescue LoadError
@shadow_lib_ok = false
else
- shadow_info = Shadow::Passwd.getspnam(@new_resource.username)
- @current_resource.password(shadow_info.sp_pwdp)
+ shadow_info = Shadow::Passwd.getspnam(new_resource.username)
+ current_resource.password(shadow_info.sp_pwdp)
end
end
- convert_group_name if @new_resource.gid
+ convert_group_name if new_resource.gid
end
- @current_resource
+ current_resource
end
def define_resource_requirements
requirements.assert(:create, :modify, :manage, :lock, :unlock) do |a|
a.assertion { @group_name_resolved }
- a.failure_message Chef::Exceptions::User, "Couldn't lookup integer GID for group name #{@new_resource.gid}"
- a.whyrun "group name #{@new_resource.gid} does not exist. This will cause group assignment to fail. Assuming this group will have been created previously."
+ a.failure_message Chef::Exceptions::User, "Couldn't lookup integer GID for group name #{new_resource.gid}"
+ a.whyrun "group name #{new_resource.gid} does not exist. This will cause group assignment to fail. Assuming this group will have been created previously."
end
requirements.assert(:all_actions) do |a|
a.assertion { @shadow_lib_ok }
a.failure_message Chef::Exceptions::MissingLibrary, "You must have ruby-shadow installed for password support!"
- a.whyrun "ruby-shadow is not installed. Attempts to set user password will cause failure. Assuming that this gem will have been previously installed." +
+ a.whyrun "ruby-shadow is not installed. Attempts to set user password will cause failure. Assuming that this gem will have been previously installed." \
"Note that user update converge may report false-positive on the basis of mismatched password. "
end
requirements.assert(:modify, :lock, :unlock) do |a|
a.assertion { @user_exists }
- a.failure_message(Chef::Exceptions::User, "Cannot modify user #{@new_resource.username} - does not exist!")
- a.whyrun("Assuming user #{@new_resource.username} would have been created")
+ a.failure_message(Chef::Exceptions::User, "Cannot modify user #{new_resource.username} - does not exist!")
+ a.whyrun("Assuming user #{new_resource.username} would have been created")
end
end
@@ -113,99 +107,98 @@ class Chef
# <true>:: If a change is required
# <false>:: If the users are identical
def compare_user
- changed = [ :comment, :home, :shell, :password ].select do |user_attrib|
- !@new_resource.send(user_attrib).nil? && @new_resource.send(user_attrib) != @current_resource.send(user_attrib)
- end
+ return true if !new_resource.home.nil? && Pathname.new(new_resource.home).cleanpath != Pathname.new(current_resource.home).cleanpath
- changed += [ :uid, :gid ].select do |user_attrib|
- !@new_resource.send(user_attrib).nil? && @new_resource.send(user_attrib).to_s != @current_resource.send(user_attrib).to_s
+ [ :comment, :shell, :password, :uid, :gid ].each do |user_attrib|
+ return true if !new_resource.send(user_attrib).nil? && new_resource.send(user_attrib).to_s != current_resource.send(user_attrib).to_s
end
- changed.any?
+ false
end
def action_create
if !@user_exists
- converge_by("create user #{@new_resource.username}") do
+ converge_by("create user #{new_resource.username}") do
create_user
- Chef::Log.info("#{@new_resource} created")
+ logger.info("#{new_resource} created")
end
elsif compare_user
- converge_by("alter user #{@new_resource.username}") do
+ converge_by("alter user #{new_resource.username}") do
manage_user
- Chef::Log.info("#{@new_resource} altered")
+ logger.info("#{new_resource} altered")
end
end
end
def action_remove
- if @user_exists
- converge_by("remove user #{@new_resource.username}") do
- remove_user
- Chef::Log.info("#{@new_resource} removed")
- end
+ return unless @user_exists
+ converge_by("remove user #{new_resource.username}") do
+ remove_user
+ logger.info("#{new_resource} removed")
end
end
- def remove_user
- raise NotImplementedError
- end
-
def action_manage
- if @user_exists && compare_user
- converge_by("manage user #{@new_resource.username}") do
- manage_user
- Chef::Log.info("#{@new_resource} managed")
- end
+ return unless @user_exists && compare_user
+ converge_by("manage user #{new_resource.username}") do
+ manage_user
+ logger.info("#{new_resource} managed")
end
end
- def manage_user
- raise NotImplementedError
- end
-
def action_modify
- if compare_user
- converge_by("modify user #{@new_resource.username}") do
- manage_user
- Chef::Log.info("#{@new_resource} modified")
- end
+ return unless compare_user
+ converge_by("modify user #{new_resource.username}") do
+ manage_user
+ logger.info("#{new_resource} modified")
end
end
def action_lock
- if check_lock() == false
- converge_by("lock the user #{@new_resource.username}") do
+ if check_lock == false
+ converge_by("lock the user #{new_resource.username}") do
lock_user
- Chef::Log.info("#{@new_resource} locked")
+ logger.info("#{new_resource} locked")
end
else
- Chef::Log.debug("#{@new_resource} already locked - nothing to do")
+ logger.trace("#{new_resource} already locked - nothing to do")
end
end
- def check_lock
+ def action_unlock
+ if check_lock == true
+ converge_by("unlock user #{new_resource.username}") do
+ unlock_user
+ logger.info("#{new_resource} unlocked")
+ end
+ else
+ logger.trace("#{new_resource} already unlocked - nothing to do")
+ end
+ end
+
+ def create_user
raise NotImplementedError
end
- def lock_user
+ def remove_user
raise NotImplementedError
end
- def action_unlock
- if check_lock() == true
- converge_by("unlock user #{@new_resource.username}") do
- unlock_user
- Chef::Log.info("#{@new_resource} unlocked")
- end
- else
- Chef::Log.debug("#{@new_resource} already unlocked - nothing to do")
- end
+ def manage_user
+ raise NotImplementedError
+ end
+
+ def lock_user
+ raise NotImplementedError
end
def unlock_user
raise NotImplementedError
end
+
+ def check_lock
+ raise NotImplementedError
+ end
end
end
end
diff --git a/lib/chef/provider/user/aix.rb b/lib/chef/provider/user/aix.rb
index 3f168b8da3..64a088dd5c 100644
--- a/lib/chef/provider/user/aix.rb
+++ b/lib/chef/provider/user/aix.rb
@@ -14,13 +14,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "chef/provider/user/useradd"
+
class Chef
class Provider
class User
class Aix < Chef::Provider::User::Useradd
- provides :user, platform: %w{aix}
+ provides :user, os: "aix"
+ provides :aix_user
- UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]]
+ UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]].freeze
def create_user
super
@@ -41,53 +44,52 @@ class Chef
end
def check_lock
- lock_info = shell_out!("lsuser -a account_locked #{new_resource.username}")
+ lock_info = shell_out_compact!("lsuser", "-a", "account_locked", new_resource.username)
if whyrun_mode? && passwd_s.stdout.empty? && lock_info.stderr.match(/does not exist/)
# if we're in whyrun mode and the user is not yet created we assume it would be
return false
end
- raise Chef::Exceptions::User, "Cannot determine if #{@new_resource} is locked!" if lock_info.stdout.empty?
+ raise Chef::Exceptions::User, "Cannot determine if #{new_resource} is locked!" if lock_info.stdout.empty?
status = /\S+\s+account_locked=(\S+)/.match(lock_info.stdout)
- if status && status[1] == "true"
- @locked = true
- else
- @locked = false
- end
+ @locked =
+ if status && status[1] == "true"
+ true
+ else
+ false
+ end
@locked
end
def lock_user
- shell_out!("chuser account_locked=true #{new_resource.username}")
+ shell_out_compact!("chuser", "account_locked=true", new_resource.username)
end
def unlock_user
- shell_out!("chuser account_locked=false #{new_resource.username}")
+ shell_out_compact!("chuser", "account_locked=false", new_resource.username)
end
- private
+ private
def add_password
- if @current_resource.password != @new_resource.password && @new_resource.password
- Chef::Log.debug("#{@new_resource.username} setting password to #{@new_resource.password}")
- command = "echo '#{@new_resource.username}:#{@new_resource.password}' | chpasswd -e"
- shell_out!(command)
- end
+ return unless current_resource.password != new_resource.password && new_resource.password
+ logger.trace("#{new_resource.username} setting password to #{new_resource.password}")
+ command = "echo '#{new_resource.username}:#{new_resource.password}' | chpasswd -e"
+ shell_out!(command)
end
# Aix specific handling to update users home directory.
def manage_home
+ return unless updating_home? && new_resource.manage_home
# -m option does not work on aix, so move dir.
- if updating_home? && managing_home_dir?
- universal_options.delete("-m")
- if ::File.directory?(@current_resource.home)
- Chef::Log.debug("Changing users home directory from #{@current_resource.home} to #{new_resource.home}")
- shell_out!("mv #{@current_resource.home} #{new_resource.home}")
- else
- Chef::Log.debug("Creating users home directory #{new_resource.home}")
- shell_out!("mkdir -p #{new_resource.home}")
- end
+ universal_options.delete("-m")
+ if ::File.directory?(current_resource.home)
+ logger.trace("Changing users home directory from #{current_resource.home} to #{new_resource.home}")
+ FileUtils.mv current_resource.home, new_resource.home
+ else
+ logger.trace("Creating users home directory #{new_resource.home}")
+ FileUtils.mkdir_p new_resource.home
end
end
diff --git a/lib/chef/provider/user/dscl.rb b/lib/chef/provider/user/dscl.rb
index 541160bf93..a1ff30ef1c 100644
--- a/lib/chef/provider/user/dscl.rb
+++ b/lib/chef/provider/user/dscl.rb
@@ -1,6 +1,6 @@
#
# Author:: Dreamcat4 (<dreamcat4@gmail.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -48,8 +48,14 @@ class Chef
attr_accessor :authentication_authority
attr_accessor :password_shadow_conversion_algorithm
+ provides :dscl_user
provides :user, os: "darwin"
+ # Just-in-case a recipe calls the user dscl provider without specifying
+ # a gid property. Avoids chown issues in move_home when the manage_home
+ # property is in use. #5393
+ STAFF_GROUP_ID = 20
+
def define_resource_requirements
super
@@ -59,12 +65,12 @@ class Chef
end
requirements.assert(:all_actions) do |a|
- a.assertion { ::File.exists?("/usr/bin/dscl") }
+ a.assertion { ::File.exist?("/usr/bin/dscl") }
a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/dscl' on the system for #{new_resource}!")
end
requirements.assert(:all_actions) do |a|
- a.assertion { ::File.exists?("/usr/bin/plutil") }
+ a.assertion { ::File.exist?("/usr/bin/plutil") }
a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/plutil' on the system for #{new_resource}!")
end
@@ -151,7 +157,7 @@ user password using shadow hash.")
convert_group_name if new_resource.gid
else
@user_exists = false
- Chef::Log.debug("#{new_resource} user does not exist")
+ logger.trace("#{new_resource} user does not exist")
end
current_resource
@@ -193,7 +199,7 @@ user password using shadow hash.")
# Create a user using dscl
#
def dscl_create_user
- run_dscl("create /Users/#{new_resource.username}")
+ run_dscl("create", "/Users/#{new_resource.username}")
end
#
@@ -202,7 +208,7 @@ user password using shadow hash.")
#
def dscl_create_comment
comment = new_resource.comment || new_resource.username
- run_dscl("create /Users/#{new_resource.username} RealName '#{comment}'")
+ run_dscl("create", "/Users/#{new_resource.username}", "RealName", comment)
end
#
@@ -218,7 +224,7 @@ user password using shadow hash.")
raise(Chef::Exceptions::RequestedUIDUnavailable, "uid #{new_resource.uid} is already in use")
end
- run_dscl("create /Users/#{new_resource.username} UniqueID #{new_resource.uid}")
+ run_dscl("create", "/Users/#{new_resource.username}", "UniqueID", new_resource.uid)
end
#
@@ -229,7 +235,7 @@ user password using shadow hash.")
uid = nil
base_uid = new_resource.system ? 200 : 501
next_uid_guess = base_uid
- users_uids = run_dscl("list /Users uid")
+ users_uids = run_dscl("list", "/Users", "uid")
while next_uid_guess < search_limit + base_uid
if users_uids =~ Regexp.new("#{Regexp.escape(next_uid_guess.to_s)}\n")
next_uid_guess += 1
@@ -238,7 +244,7 @@ user password using shadow hash.")
break
end
end
- return uid || raise("uid not found. Exhausted. Searched #{search_limit} times")
+ uid || raise("uid not found. Exhausted. Searched #{search_limit} times")
end
#
@@ -246,39 +252,39 @@ user password using shadow hash.")
#
def uid_used?(uid)
return false unless uid
- users_uids = run_dscl("list /Users uid").split("\n")
- uid_map = users_uids.inject({}) do |tmap, tuid|
+ users_uids = run_dscl("list", "/Users", "uid").split("\n")
+ uid_map = users_uids.each_with_object({}) do |tuid, tmap|
x = tuid.split
tmap[x[1]] = x[0]
tmap
end
if uid_map[uid.to_s]
- unless uid_map[uid.to_s] == new_resource.username.to_s
+ unless uid_map[uid.to_s] == new_resource.username
return true
end
end
- return false
+ false
end
#
# Sets the group id for the user using dscl. Fails if a group doesn't
# exist on the system with given group id. If `gid` is not specified, it
- # sets a default Mac user group "staff", with id 20.
+ # sets a default Mac user group "staff", with id 20 using the CONSTANT
#
def dscl_set_gid
if new_resource.gid.nil?
# XXX: mutates the new resource
- new_resource.gid(20)
+ new_resource.gid(STAFF_GROUP_ID)
elsif !new_resource.gid.to_s.match(/^\d+$/)
begin
- possible_gid = run_dscl("read /Groups/#{new_resource.gid} PrimaryGroupID").split(" ").last
- rescue Chef::Exceptions::DsclCommandFailed => e
- raise Chef::Exceptions::GroupIDNotFound.new("Group not found for #{new_resource.gid} when creating user #{new_resource.username}")
+ possible_gid = run_dscl("read", "/Groups/#{new_resource.gid}", "PrimaryGroupID").split(" ").last
+ rescue Chef::Exceptions::DsclCommandFailed
+ raise Chef::Exceptions::GroupIDNotFound, "Group not found for #{new_resource.gid} when creating user #{new_resource.username}"
end
# XXX: mutates the new resource
new_resource.gid(possible_gid) if possible_gid && possible_gid.match(/^\d+$/)
end
- run_dscl("create /Users/#{new_resource.username} PrimaryGroupID '#{new_resource.gid}'")
+ run_dscl("create", "/Users/#{new_resource.username}", "PrimaryGroupID", new_resource.gid)
end
#
@@ -287,13 +293,11 @@ user password using shadow hash.")
#
def dscl_set_home
if new_resource.home.nil? || new_resource.home.empty?
- run_dscl("delete /Users/#{new_resource.username} NFSHomeDirectory")
+ run_dscl("delete", "/Users/#{new_resource.username}", "NFSHomeDirectory")
return
end
- run_dscl("create /Users/#{new_resource.username} NFSHomeDirectory '#{new_resource.home}'")
-
- if new_resource.supports[:manage_home]
+ if new_resource.manage_home
validate_home_dir_specification!
if (current_resource.home == new_resource.home) && !new_home_exists?
@@ -304,6 +308,7 @@ user password using shadow hash.")
move_home
end
end
+ run_dscl("create", "/Users/#{new_resource.username}", "NFSHomeDirectory", new_resource.home)
end
def validate_home_dir_specification!
@@ -313,24 +318,24 @@ user password using shadow hash.")
end
def current_home_exists?
- ::File.exist?("#{current_resource.home}")
+ !!current_resource.home && ::File.exist?(current_resource.home)
end
def new_home_exists?
- ::File.exist?("#{new_resource.home}")
+ ::File.exist?(new_resource.home)
end
def ditto_home
- shell_out! "/usr/sbin/createhomedir -c -u #{new_resource.username}"
+ shell_out_compact!("/usr/sbin/createhomedir -c -u #{new_resource.username}")
end
def move_home
- Chef::Log.debug("#{new_resource} moving #{self} home from #{current_resource.home} to #{new_resource.home}")
-
+ logger.trace("#{new_resource} moving #{self} home from #{current_resource.home} to #{new_resource.home}")
+ new_resource.gid(STAFF_GROUP_ID) if new_resource.gid.nil?
src = current_resource.home
FileUtils.mkdir_p(new_resource.home)
files = ::Dir.glob("#{Chef::Util::PathHelper.escape_glob_dir(src)}/*", ::File::FNM_DOTMATCH) - ["#{src}/.", "#{src}/.."]
- ::FileUtils.mv(files, new_resource.home, :force => true)
+ ::FileUtils.mv(files, new_resource.home, force: true)
::FileUtils.rmdir(src)
::FileUtils.chown_R(new_resource.username, new_resource.gid.to_s, new_resource.home)
end
@@ -339,10 +344,10 @@ user password using shadow hash.")
# Sets the shell for the user using dscl.
#
def dscl_set_shell
- if new_resource.shell || ::File.exists?("#{new_resource.shell}")
- run_dscl("create /Users/#{new_resource.username} UserShell '#{new_resource.shell}'")
+ if new_resource.shell
+ run_dscl("create", "/Users/#{new_resource.username}", "UserShell", new_resource.shell)
else
- run_dscl("create /Users/#{new_resource.username} UserShell '/usr/bin/false'")
+ run_dscl("create", "/Users/#{new_resource.username}", "UserShell", "/usr/bin/false")
end
end
@@ -359,9 +364,8 @@ user password using shadow hash.")
# Shadow info is saved as binary plist. Convert the info to binary plist.
shadow_info_binary = StringIO.new
- command = Mixlib::ShellOut.new("plutil -convert binary1 -o - -",
- :input => shadow_info.to_plist, :live_stream => shadow_info_binary)
- command.run_command
+ shell_out_compact("plutil", "-convert", "binary1", "-o", "-", "-",
+ input: shadow_info.to_plist, live_stream: shadow_info_binary)
if user_info.nil?
# User is just created. read_user_info() will read the fresh information
@@ -393,7 +397,7 @@ user password using shadow hash.")
# Create a random 4 byte salt
salt = OpenSSL::Random.random_bytes(4)
encoded_password = OpenSSL::Digest::SHA512.hexdigest(salt + new_resource.password)
- hash_value = salt.unpack("H*").first + encoded_password
+ salt.unpack("H*").first + encoded_password
end
shadow_info["SALTED-SHA512"] = StringIO.new
@@ -435,27 +439,27 @@ user password using shadow hash.")
# and deleting home directory if needed.
#
def remove_user
- if new_resource.supports[:manage_home]
+ if new_resource.manage_home
# Remove home directory
FileUtils.rm_rf(current_resource.home)
end
# Remove the user from its groups
- run_dscl("list /Groups").each_line do |group|
+ run_dscl("list", "/Groups").each_line do |group|
if member_of_group?(group.chomp)
- run_dscl("delete /Groups/#{group.chomp} GroupMembership '#{new_resource.username}'")
+ run_dscl("delete", "/Groups/#{group.chomp}", "GroupMembership", new_resource.username)
end
end
# Remove user account
- run_dscl("delete /Users/#{new_resource.username}")
+ run_dscl("delete", "/Users/#{new_resource.username}")
end
#
# Locks the user.
#
def lock_user
- run_dscl("append /Users/#{new_resource.username} AuthenticationAuthority ';DisabledUser;'")
+ run_dscl("append", "/Users/#{new_resource.username}", "AuthenticationAuthority", ";DisabledUser;")
end
#
@@ -463,7 +467,7 @@ user password using shadow hash.")
#
def unlock_user
auth_string = authentication_authority.gsub(/AuthenticationAuthority: /, "").gsub(/;DisabledUser;/, "").strip
- run_dscl("create /Users/#{new_resource.username} AuthenticationAuthority '#{auth_string}'")
+ run_dscl("create", "/Users/#{new_resource.username}", "AuthenticationAuthority", auth_string)
end
#
@@ -481,7 +485,7 @@ user password using shadow hash.")
# This is the interface base User provider requires to provide idempotency.
#
def check_lock
- return @locked = locked?
+ @locked = locked?
end
#
@@ -493,11 +497,11 @@ user password using shadow hash.")
# given attribute.
#
def diverged?(parameter)
- parameter_updated?(parameter) && (not new_resource.send(parameter).nil?)
+ parameter_updated?(parameter) && !new_resource.send(parameter).nil?
end
def parameter_updated?(parameter)
- not (new_resource.send(parameter) == current_resource.send(parameter))
+ !(new_resource.send(parameter) == current_resource.send(parameter))
end
#
@@ -543,7 +547,7 @@ user password using shadow hash.")
def member_of_group?(group_name)
membership_info = ""
begin
- membership_info = run_dscl("read /Groups/#{group_name}")
+ membership_info = run_dscl("read", "/Groups/#{group_name}")
rescue Chef::Exceptions::DsclCommandFailed
# Raised if the group doesn't contain any members
end
@@ -560,14 +564,14 @@ user password using shadow hash.")
# A simple map of Chef's terms to DSCL's terms.
DSCL_PROPERTY_MAP = {
- :uid => "uid",
- :gid => "gid",
- :home => "home",
- :shell => "shell",
- :comment => "realname",
- :password => "passwd",
- :auth_authority => "authentication_authority",
- :shadow_hash => "ShadowHashData",
+ uid: "uid",
+ gid: "gid",
+ home: "home",
+ shell: "shell",
+ comment: "realname",
+ password: "passwd",
+ auth_authority: "authentication_authority",
+ shadow_hash: "ShadowHashData",
}.freeze
# Directory where the user plist files are stored for versions 10.7 and above
@@ -582,11 +586,11 @@ user password using shadow hash.")
# We flush the cache here in order to make sure that we read fresh information
# for the user.
- shell_out("dscacheutil '-flushcache'")
+ shell_out_compact("dscacheutil", "-flushcache") # FIXME: this is MacOS version dependent
begin
user_plist_file = "#{USER_PLIST_DIRECTORY}/#{new_resource.username}.plist"
- user_plist_info = run_plutil("convert xml1 -o - #{user_plist_file}")
+ user_plist_info = run_plutil("convert", "xml1", "-o", "-", user_plist_file)
user_info = Plist.parse_xml(user_plist_info)
rescue Chef::Exceptions::PlistUtilCommandFailed
end
@@ -601,7 +605,7 @@ user password using shadow hash.")
def save_user_info(user_info)
user_plist_file = "#{USER_PLIST_DIRECTORY}/#{new_resource.username}.plist"
Plist::Emit.save_plist(user_info, user_plist_file)
- run_plutil("convert binary1 #{user_plist_file}")
+ run_plutil("convert", "binary1", user_plist_file)
end
#
@@ -650,7 +654,9 @@ user password using shadow hash.")
end
def run_dscl(*args)
- result = shell_out("dscl . -#{args.join(' ')}")
+ argdup = args.dup
+ cmd = argdup.shift
+ result = shell_out_compact("dscl", ".", "-#{cmd}", argdup)
return "" if ( args.first =~ /^delete/ ) && ( result.exitstatus != 0 )
raise(Chef::Exceptions::DsclCommandFailed, "dscl error: #{result.inspect}") unless result.exitstatus == 0
raise(Chef::Exceptions::DsclCommandFailed, "dscl error: #{result.inspect}") if result.stdout =~ /No such key: /
@@ -658,17 +664,19 @@ user password using shadow hash.")
end
def run_plutil(*args)
- result = shell_out("plutil -#{args.join(' ')}")
+ argdup = args.dup
+ cmd = argdup.shift
+ result = shell_out_compact("plutil", "-#{cmd}", argdup)
raise(Chef::Exceptions::PlistUtilCommandFailed, "plutil error: #{result.inspect}") unless result.exitstatus == 0
if result.stdout.encoding == Encoding::ASCII_8BIT
- result.stdout.encode("utf-8", "binary", :undef => :replace, :invalid => :replace, :replace => "?")
+ result.stdout.encode("utf-8", "binary", undef: :replace, invalid: :replace, replace: "?")
else
result.stdout
end
end
def convert_binary_plist_to_xml(binary_plist_string)
- Mixlib::ShellOut.new("plutil -convert xml1 -o - -", :input => binary_plist_string).run_command.stdout
+ shell_out_compact("plutil", "-convert", "xml1", "-o", "-", "-", input: binary_plist_string).stdout
end
def convert_to_binary(string)
diff --git a/lib/chef/provider/user/linux.rb b/lib/chef/provider/user/linux.rb
new file mode 100644
index 0000000000..2db6c218bd
--- /dev/null
+++ b/lib/chef/provider/user/linux.rb
@@ -0,0 +1,126 @@
+#
+# Copyright:: Copyright 2016-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "chef/provider/user"
+
+class Chef
+ class Provider
+ class User
+ class Linux < Chef::Provider::User
+ provides :linux_user
+ provides :user, os: "linux"
+
+ def create_user
+ shell_out_compact!("useradd", universal_options, useradd_options, new_resource.username)
+ end
+
+ def manage_user
+ shell_out_compact!("usermod", universal_options, usermod_options, new_resource.username)
+ end
+
+ def remove_user
+ shell_out_compact!("userdel", userdel_options, new_resource.username)
+ end
+
+ def lock_user
+ shell_out_compact!("usermod", "-L", new_resource.username)
+ end
+
+ def unlock_user
+ shell_out_compact!("usermod", "-U", new_resource.username)
+ end
+
+ # common to usermod and useradd
+ def universal_options
+ opts = []
+ opts << "-c" << new_resource.comment if should_set?(:comment)
+ opts << "-g" << new_resource.gid if should_set?(:gid)
+ opts << "-p" << new_resource.password if should_set?(:password)
+ opts << "-s" << new_resource.shell if should_set?(:shell)
+ opts << "-u" << new_resource.uid if should_set?(:uid)
+ opts << "-d" << new_resource.home if updating_home?
+ opts << "-o" if new_resource.non_unique
+ opts
+ end
+
+ def usermod_options
+ opts = []
+ opts += [ "-u", new_resource.uid ] if new_resource.non_unique
+ if updating_home?
+ if new_resource.manage_home
+ opts << "-m"
+ end
+ end
+ opts
+ end
+
+ def useradd_options
+ opts = []
+ opts << "-r" if new_resource.system
+ opts << if new_resource.manage_home
+ "-m"
+ else
+ "-M"
+ end
+ opts
+ end
+
+ def userdel_options
+ opts = []
+ opts << "-r" if new_resource.manage_home
+ opts << "-f" if new_resource.force
+ opts
+ end
+
+ def should_set?(sym)
+ current_resource.send(sym).to_s != new_resource.send(sym).to_s && new_resource.send(sym)
+ end
+
+ def updating_home?
+ return false unless new_resource.home
+ return true unless current_resource.home
+ new_resource.home && Pathname.new(current_resource.home).cleanpath != Pathname.new(new_resource.home).cleanpath
+ end
+
+ def check_lock
+ # there's an old bug in rhel (https://bugzilla.redhat.com/show_bug.cgi?id=578534)
+ # which means that both 0 and 1 can be success.
+ passwd_s = shell_out_compact("passwd", "-S", new_resource.username, returns: [ 0, 1 ])
+
+ # checking "does not exist" has to come before exit code handling since centos and ubuntu differ in exit codes
+ if passwd_s.stderr =~ /does not exist/
+ return false if whyrun_mode?
+ raise Chef::Exceptions::User, "User #{new_resource.username} does not exist when checking lock status for #{new_resource}"
+ end
+
+ # now raise if we didn't get a 0 or 1 (see above)
+ passwd_s.error!
+
+ # now the actual output parsing
+ @locked = nil
+ status_line = passwd_s.stdout.split(" ")
+ @locked = false if status_line[1] =~ /^[PN]/
+ @locked = true if status_line[1] =~ /^L/
+
+ raise Chef::Exceptions::User, "Cannot determine if user #{new_resource.username} is locked for #{new_resource}" if @locked.nil?
+
+ # FIXME: should probably go on the current_resource
+ @locked
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/user/pw.rb b/lib/chef/provider/user/pw.rb
index 949a21790b..695dbfd539 100644
--- a/lib/chef/provider/user/pw.rb
+++ b/lib/chef/provider/user/pw.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Haynes (<sh@nomitor.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,53 +22,50 @@ class Chef
class Provider
class User
class Pw < Chef::Provider::User
- provides :user, platform: %w{freebsd}
+ provides :pw_user
+ provides :user, os: "freebsd"
def load_current_resource
super
- raise Chef::Exceptions::User, "Could not find binary /usr/sbin/pw for #{@new_resource}" unless ::File.exists?("/usr/sbin/pw")
+ raise Chef::Exceptions::User, "Could not find binary /usr/sbin/pw for #{new_resource}" unless ::File.exist?("/usr/sbin/pw")
end
def create_user
- command = "pw useradd"
- command << set_options
- run_command(:command => command)
+ shell_out_compact!("pw", "useradd", set_options)
modify_password
end
def manage_user
- command = "pw usermod"
- command << set_options
- run_command(:command => command)
+ shell_out_compact!("pw", "usermod", set_options)
modify_password
end
def remove_user
- command = "pw userdel #{@new_resource.username}"
- command << " -r" if @new_resource.supports[:manage_home]
- run_command(:command => command)
+ command = [ "pw", "userdel", new_resource.username ]
+ command << "-r" if new_resource.manage_home
+ shell_out_compact!(command)
end
def check_lock
- case @current_resource.password
- when /^\*LOCKED\*/
- @locked = true
- else
- @locked = false
- end
+ @locked = case current_resource.password
+ when /^\*LOCKED\*/
+ true
+ else
+ false
+ end
@locked
end
def lock_user
- run_command(:command => "pw lock #{@new_resource.username}")
+ shell_out_compact!("pw", "lock", new_resource.username)
end
def unlock_user
- run_command(:command => "pw unlock #{@new_resource.username}")
+ shell_out_compact!("pw", "unlock", new_resource.username)
end
def set_options
- opts = " #{@new_resource.username}"
+ opts = [ new_resource.username ]
field_list = {
"comment" => "-c",
@@ -77,35 +74,29 @@ class Chef
"uid" => "-u",
"shell" => "-s",
}
- field_list.sort { |a, b| a[0] <=> b[0] }.each do |field, option|
+ field_list.sort_by { |a| a[0] }.each do |field, option|
field_symbol = field.to_sym
- if @current_resource.send(field_symbol) != @new_resource.send(field_symbol)
- if @new_resource.send(field_symbol)
- Chef::Log.debug("#{@new_resource} setting #{field} to #{@new_resource.send(field_symbol)}")
- opts << " #{option} '#{@new_resource.send(field_symbol)}'"
- end
+ next unless current_resource.send(field_symbol) != new_resource.send(field_symbol)
+ if new_resource.send(field_symbol)
+ logger.trace("#{new_resource} setting #{field} to #{new_resource.send(field_symbol)}")
+ opts << option
+ opts << new_resource.send(field_symbol)
end
end
- if @new_resource.supports[:manage_home]
- Chef::Log.debug("#{@new_resource} is managing the users home directory")
- opts << " -m"
+ if new_resource.manage_home
+ logger.trace("#{new_resource} is managing the users home directory")
+ opts << "-m"
end
opts
end
def modify_password
- if (not @new_resource.password.nil?) && (@current_resource.password != @new_resource.password)
- Chef::Log.debug("#{new_resource} updating password")
- command = "pw usermod #{@new_resource.username} -H 0"
- status = popen4(command, :waitlast => true) do |pid, stdin, stdout, stderr|
- stdin.puts "#{@new_resource.password}"
- end
-
- unless status.exitstatus == 0
- raise Chef::Exceptions::User, "pw failed - #{status.inspect}!"
- end
+ if !new_resource.password.nil? && (current_resource.password != new_resource.password)
+ logger.trace("#{new_resource} updating password")
+ command = "pw usermod #{new_resource.username} -H 0"
+ shell_out!(command, input: new_resource.password.to_s)
else
- Chef::Log.debug("#{new_resource} no change needed to password")
+ logger.trace("#{new_resource} no change needed to password")
end
end
end
diff --git a/lib/chef/provider/user/solaris.rb b/lib/chef/provider/user/solaris.rb
index 1f0cbb6054..59074d5ba8 100644
--- a/lib/chef/provider/user/solaris.rb
+++ b/lib/chef/provider/user/solaris.rb
@@ -2,7 +2,7 @@
# Author:: Stephen Nelson-Smith (<sns@chef.io>)
# Author:: Jon Ramsey (<jonathon.ramsey@gmail.com>)
# Author:: Dave Eddy (<dave@daveeddy.com>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright 2012-2017, Chef Software Inc.
# Copyright:: Copyright 2015-2016, Dave Eddy
# License:: Apache License, Version 2.0
#
@@ -24,8 +24,9 @@ class Chef
class Provider
class User
class Solaris < Chef::Provider::User::Useradd
- provides :user, platform: %w{omnios solaris2}
- UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]]
+ provides :solaris_user
+ provides :user, os: %w{omnios solaris2}
+ UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]].freeze
attr_writer :password_file
@@ -45,38 +46,45 @@ class Chef
end
def check_lock
- shadow_line = shell_out!("getent", "shadow", new_resource.username).stdout.strip rescue nil
+ user = IO.read(@password_file).match(/^#{Regexp.escape(new_resource.username)}:([^:]*):/)
- # if the command fails we return nil, this can happen if the user
- # in question doesn't exist
- return nil if shadow_line.nil?
+ # If we're in whyrun mode, and the user is not created, we assume it will be
+ return false if whyrun_mode? && user.nil?
- # convert "dave:NP:16507::::::\n" to "NP"
- fields = shadow_line.split(":")
+ raise Chef::Exceptions::User, "Cannot determine if #{new_resource} is locked!" if user.nil?
- # '*LK*...' and 'LK' are both considered locked,
- # so look for LK at the beginning of the shadow entry
- # optionally surrounded by '*'
- @locked = !!fields[1].match(/^\*?LK\*?/)
-
- @locked
+ @locked = user[1].start_with?("*LK*")
end
def lock_user
- shell_out!("passwd", "-l", new_resource.username)
+ shell_out_compact!("passwd", "-l", new_resource.username)
end
def unlock_user
- shell_out!("passwd", "-u", new_resource.username)
+ shell_out_compact!("passwd", "-u", new_resource.username)
end
- private
+ private
+
+ # Override the version from {#Useradd} because Solaris doesn't support
+ # system users and therefore has no `-r` option. This also inverts the
+ # logic for manage_home as Solaris defaults to no-manage-home and only
+ # offers `-m`.
+ #
+ # @since 12.15
+ # @api private
+ # @see Useradd#useradd_options
+ # @return [Array<String>]
+ def useradd_options
+ opts = []
+ opts << "-m" if new_resource.manage_home
+ opts
+ end
def manage_password
- if @current_resource.password != @new_resource.password && @new_resource.password
- Chef::Log.debug("#{@new_resource} setting password to #{@new_resource.password}")
- write_shadow_file
- end
+ return unless current_resource.password != new_resource.password && new_resource.password
+ logger.trace("#{new_resource} setting password to #{new_resource.password}")
+ write_shadow_file
end
def write_shadow_file
@@ -84,7 +92,7 @@ class Chef
::File.open(@password_file) do |shadow_file|
shadow_file.each do |entry|
user = entry.split(":").first
- if user == @new_resource.username
+ if user == new_resource.username
buffer.write(updated_password(entry))
else
buffer.write(entry)
@@ -95,7 +103,7 @@ class Chef
# FIXME: mostly duplicates code with file provider deploying a file
s = ::File.stat(@password_file)
- mode = s.mode & 07777
+ mode = s.mode & 0o7777
uid = s.uid
gid = s.gid
@@ -107,7 +115,7 @@ class Chef
def updated_password(entry)
fields = entry.split(":")
- fields[1] = @new_resource.password
+ fields[1] = new_resource.password
fields[2] = days_since_epoch
fields.join(":")
end
diff --git a/lib/chef/provider/user/useradd.rb b/lib/chef/provider/user/useradd.rb
index 3fef8d3642..47c0ece101 100644
--- a/lib/chef/provider/user/useradd.rb
+++ b/lib/chef/provider/user/useradd.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,45 +23,45 @@ class Chef
class Provider
class User
class Useradd < Chef::Provider::User
- provides :user
+ # the linux version of this has been forked off, this is the base class now of solaris and AIX and should be abandoned
+ # and those provider should be rewritten like the linux version.
- UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:password, "-p"], [:shell, "-s"], [:uid, "-u"]]
+ UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:password, "-p"], [:shell, "-s"], [:uid, "-u"]].freeze
def create_user
command = compile_command("useradd") do |useradd|
useradd.concat(universal_options)
useradd.concat(useradd_options)
end
- shell_out!(*command)
+ shell_out_compact!(command)
end
def manage_user
- unless universal_options.empty?
- command = compile_command("usermod") do |u|
- u.concat(universal_options)
- end
- shell_out!(*command)
+ return if universal_options.empty?
+ command = compile_command("usermod") do |u|
+ u.concat(universal_options)
end
+ shell_out_compact!(command)
end
def remove_user
command = [ "userdel" ]
- command << "-r" if managing_home_dir?
+ command << "-r" if new_resource.manage_home
command << "-f" if new_resource.force
command << new_resource.username
- shell_out!(*command)
+ shell_out_compact!(command)
end
def check_lock
# we can get an exit code of 1 even when it's successful on
# rhel/centos (redhat bug 578534). See additional error checks below.
- passwd_s = shell_out!("passwd", "-S", new_resource.username, :returns => [0, 1])
+ passwd_s = shell_out_compact!("passwd", "-S", new_resource.username, returns: [0, 1])
if whyrun_mode? && passwd_s.stdout.empty? && passwd_s.stderr.match(/does not exist/)
# if we're in whyrun mode and the user is not yet created we assume it would be
return false
end
- raise Chef::Exceptions::User, "Cannot determine if #{@new_resource} is locked!" if passwd_s.stdout.empty?
+ raise Chef::Exceptions::User, "Cannot determine if #{new_resource} is locked!" if passwd_s.stdout.empty?
status_line = passwd_s.stdout.split(" ")
case status_line[1]
@@ -76,7 +76,7 @@ class Chef
unless passwd_s.exitstatus == 0
raise_lock_error = false
if %w{redhat centos}.include?(node[:platform])
- passwd_version_check = shell_out!("rpm -q passwd")
+ passwd_version_check = shell_out_compact!("rpm", "-q", "passwd")
passwd_version = passwd_version_check.stdout.chomp
unless passwd_version == "passwd-0.73-1"
@@ -93,11 +93,11 @@ class Chef
end
def lock_user
- shell_out!("usermod", "-L", new_resource.username)
+ shell_out_compact!("usermod", "-L", new_resource.username)
end
def unlock_user
- shell_out!("usermod", "-U", new_resource.username)
+ shell_out_compact!("usermod", "-U", new_resource.username)
end
def compile_command(base_command)
@@ -116,31 +116,30 @@ class Chef
update_options(field, option, opts)
end
if updating_home?
- if managing_home_dir?
- Chef::Log.debug("#{new_resource} managing the users home directory")
- opts << "-d" << new_resource.home << "-m"
+ opts << "-d" << new_resource.home
+ if new_resource.manage_home
+ logger.trace("#{new_resource} managing the users home directory")
+ opts << "-m"
else
- Chef::Log.debug("#{new_resource} setting home to #{new_resource.home}")
- opts << "-d" << new_resource.home
+ logger.trace("#{new_resource} setting home to #{new_resource.home}")
end
end
- opts << "-o" if new_resource.non_unique || new_resource.supports[:non_unique]
+ opts << "-o" if new_resource.non_unique
opts
end
end
def update_options(field, option, opts)
- if @current_resource.send(field).to_s != new_resource.send(field).to_s
- if new_resource.send(field)
- Chef::Log.debug("#{new_resource} setting #{field} to #{new_resource.send(field)}")
- opts << option << new_resource.send(field).to_s
- end
- end
+ return unless current_resource.send(field).to_s != new_resource.send(field).to_s
+ return unless new_resource.send(field)
+ logger.trace("#{new_resource} setting #{field} to #{new_resource.send(field)}")
+ opts << option << new_resource.send(field).to_s
end
def useradd_options
opts = []
opts << "-r" if new_resource.system
+ opts << "-M" unless new_resource.manage_home
opts
end
@@ -149,12 +148,8 @@ class Chef
# Pathname#cleanpath does a better job than ::File::expand_path (on both unix and windows)
# ::File.expand_path("///tmp") == ::File.expand_path("/tmp") => false
# ::File.expand_path("\\tmp") => "C:/tmp"
- return true if @current_resource.home.nil? && new_resource.home
- new_resource.home && Pathname.new(@current_resource.home).cleanpath != Pathname.new(new_resource.home).cleanpath
- end
-
- def managing_home_dir?
- new_resource.manage_home || new_resource.supports[:manage_home]
+ return true if current_resource.home.nil? && new_resource.home
+ new_resource.home && Pathname.new(current_resource.home).cleanpath != Pathname.new(new_resource.home).cleanpath
end
end
diff --git a/lib/chef/provider/user/windows.rb b/lib/chef/provider/user/windows.rb
index 9545b1fd59..994f1a6774 100644
--- a/lib/chef/provider/user/windows.rb
+++ b/lib/chef/provider/user/windows.rb
@@ -26,36 +26,35 @@ class Chef
class Provider
class User
class Windows < Chef::Provider::User
-
+ provides :windows_user
provides :user, os: "windows"
def initialize(new_resource, run_context)
super
- @net_user = Chef::Util::Windows::NetUser.new(@new_resource.username)
+ @net_user = Chef::Util::Windows::NetUser.new(new_resource.username)
end
def load_current_resource
- if @new_resource.gid
- Chef::Log.warn("The 'gid' attribute is not implemented by the Windows platform. Please use the 'group' resource to assign a user to a group.")
+ if new_resource.gid
+ logger.warn("The 'gid' (or 'group') property is not implemented on the Windows platform. Please use the `members` property of the 'group' resource to assign a user to a group.")
end
- @current_resource = Chef::Resource::User.new(@new_resource.name)
- @current_resource.username(@new_resource.username)
- user_info = nil
+ @current_resource = Chef::Resource::User.new(new_resource.name)
+ current_resource.username(new_resource.username)
begin
user_info = @net_user.get_info
- @current_resource.uid(user_info[:user_id])
- @current_resource.comment(user_info[:full_name])
- @current_resource.home(user_info[:home_dir])
- @current_resource.shell(user_info[:script_path])
+ current_resource.uid(user_info[:user_id])
+ current_resource.comment(user_info[:full_name])
+ current_resource.home(user_info[:home_dir])
+ current_resource.shell(user_info[:script_path])
rescue Chef::Exceptions::UserIDNotFound => e
# e.message should be "The user name could not be found" but checking for that could cause a localization bug
@user_exists = false
- Chef::Log.debug("#{@new_resource} does not exist (#{e.message})")
+ logger.trace("#{new_resource} does not exist (#{e.message})")
end
- @current_resource
+ current_resource
end
# Check to see if the user needs any changes
@@ -64,12 +63,12 @@ class Chef
# <true>:: If a change is required
# <false>:: If the users are identical
def compare_user
- unless @net_user.validate_credentials(@new_resource.password)
- Chef::Log.debug("#{@new_resource} password has changed")
+ unless @net_user.validate_credentials(new_resource.password)
+ logger.trace("#{new_resource} password has changed")
return true
end
[ :uid, :comment, :home, :shell ].any? do |user_attrib|
- !@new_resource.send(user_attrib).nil? && @new_resource.send(user_attrib) != @current_resource.send(user_attrib)
+ !new_resource.send(user_attrib).nil? && new_resource.send(user_attrib) != current_resource.send(user_attrib)
end
end
@@ -98,7 +97,7 @@ class Chef
end
def set_options
- opts = { :name => @new_resource.username }
+ opts = { name: new_resource.username }
field_list = {
"comment" => "full_name",
@@ -108,16 +107,14 @@ class Chef
"password" => "password",
}
- field_list.sort { |a, b| a[0] <=> b[0] }.each do |field, option|
+ field_list.sort_by { |a| a[0] }.each do |field, option|
field_symbol = field.to_sym
- if @current_resource.send(field_symbol) != @new_resource.send(field_symbol)
- if @new_resource.send(field_symbol)
- unless field_symbol == :password
- Chef::Log.debug("#{@new_resource} setting #{field} to #{@new_resource.send(field_symbol)}")
- end
- opts[option.to_sym] = @new_resource.send(field_symbol)
- end
+ next unless current_resource.send(field_symbol) != new_resource.send(field_symbol)
+ next unless new_resource.send(field_symbol)
+ unless field_symbol == :password
+ logger.trace("#{new_resource} setting #{field} to #{new_resource.send(field_symbol)}")
end
+ opts[option.to_sym] = new_resource.send(field_symbol)
end
opts
end
diff --git a/lib/chef/provider/whyrun_safe_ruby_block.rb b/lib/chef/provider/whyrun_safe_ruby_block.rb
index 3ea48017b7..ee4a659b00 100644
--- a/lib/chef/provider/whyrun_safe_ruby_block.rb
+++ b/lib/chef/provider/whyrun_safe_ruby_block.rb
@@ -22,10 +22,10 @@ class Chef
provides :whyrun_safe_ruby_block
def action_run
- @new_resource.block.call
- @new_resource.updated_by_last_action(true)
- @run_context.events.resource_update_applied(@new_resource, :create, "execute the whyrun_safe_ruby_block #{@new_resource.name}")
- Chef::Log.info("#{@new_resource} called")
+ new_resource.block.call
+ new_resource.updated_by_last_action(true)
+ @run_context.events.resource_update_applied(new_resource, :create, "execute the whyrun_safe_ruby_block #{new_resource.name}")
+ logger.info("#{new_resource} called")
end
end
end
diff --git a/lib/chef/provider/windows_env.rb b/lib/chef/provider/windows_env.rb
new file mode 100644
index 0000000000..4e7fa34216
--- /dev/null
+++ b/lib/chef/provider/windows_env.rb
@@ -0,0 +1,207 @@
+#
+# Author:: Doug MacEachern (<dougm@vmware.com>)
+# Copyright:: Copyright 2010-2016, VMware, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/provider"
+require "chef/resource/windows_env"
+require "chef/mixin/windows_env_helper"
+
+class Chef
+ class Provider
+ class WindowsEnv < Chef::Provider
+ include Chef::Mixin::WindowsEnvHelper
+ attr_accessor :key_exists
+
+ provides :env
+ provides :windows_env
+
+ def whyrun_supported?
+ false
+ end
+
+ def initialize(new_resource, run_context)
+ super
+ @key_exists = true
+ end
+
+ def load_current_resource
+ @current_resource = Chef::Resource::WindowsEnv.new(new_resource.name)
+ current_resource.key_name(new_resource.key_name)
+
+ if env_key_exists(new_resource.key_name)
+ current_resource.value(env_value(new_resource.key_name))
+ else
+ @key_exists = false
+ logger.trace("#{new_resource} key does not exist")
+ end
+
+ current_resource
+ end
+
+ def env_key_exists(key_name)
+ env_value(key_name) ? true : false
+ end
+
+ # Check to see if value needs any changes
+ #
+ # ==== Returns
+ # <true>:: If a change is required
+ # <false>:: If a change is not required
+ def requires_modify_or_create?
+ if new_resource.delim
+ #e.g. check for existing value within PATH
+ new_values.inject(0) do |index, val|
+ next_index = current_values.find_index val
+ return true if next_index.nil? || next_index < index
+ next_index
+ end
+ false
+ else
+ new_resource.value != current_resource.value
+ end
+ end
+
+ alias_method :compare_value, :requires_modify_or_create?
+
+ def action_create
+ if @key_exists
+ if requires_modify_or_create?
+ modify_env
+ logger.info("#{new_resource} altered")
+ new_resource.updated_by_last_action(true)
+ end
+ else
+ create_env
+ logger.info("#{new_resource} created")
+ new_resource.updated_by_last_action(true)
+ end
+ end
+
+ #e.g. delete a PATH element
+ #
+ # ==== Returns
+ # <true>:: If we handled the element case and caller should not delete the key
+ # <false>:: Caller should delete the key, either no :delim was specific or value was empty
+ # after we removed the element.
+ def delete_element
+ return false unless new_resource.delim #no delim: delete the key
+ needs_delete = new_values.any? { |v| current_values.include?(v) }
+ if !needs_delete
+ logger.trace("#{new_resource} element '#{new_resource.value}' does not exist")
+ return true #do not delete the key
+ else
+ new_value =
+ current_values.select do |item|
+ not new_values.include?(item)
+ end.join(new_resource.delim)
+
+ if new_value.empty?
+ return false #nothing left here, delete the key
+ else
+ old_value = new_resource.value(new_value)
+ create_env
+ logger.trace("#{new_resource} deleted #{old_value} element")
+ new_resource.updated_by_last_action(true)
+ return true #we removed the element and updated; do not delete the key
+ end
+ end
+ end
+
+ def action_delete
+ if ( ENV[new_resource.key_name] || @key_exists ) && !delete_element
+ delete_env
+ logger.info("#{new_resource} deleted")
+ new_resource.updated_by_last_action(true)
+ end
+ end
+
+ def action_modify
+ if @key_exists
+ if requires_modify_or_create?
+ modify_env
+ logger.info("#{new_resource} modified")
+ new_resource.updated_by_last_action(true)
+ end
+ else
+ raise Chef::Exceptions::WindowsEnv, "Cannot modify #{new_resource} - key does not exist!"
+ end
+ end
+
+ def create_env
+ obj = env_obj(@new_resource.key_name)
+ unless obj
+ obj = WIN32OLE.connect("winmgmts://").get("Win32_Environment").spawninstance_
+ obj.name = @new_resource.key_name
+ obj.username = new_resource.user
+ end
+ obj.variablevalue = @new_resource.value
+ obj.put_
+ value = @new_resource.value
+ value = expand_path(value) if @new_resource.key_name.casecmp("PATH") == 0
+ ENV[@new_resource.key_name] = value
+ broadcast_env_change
+ end
+
+ def delete_env
+ obj = env_obj(@new_resource.key_name)
+ if obj
+ obj.delete_
+ broadcast_env_change
+ end
+ if ENV[@new_resource.key_name]
+ ENV.delete(@new_resource.key_name)
+ end
+ end
+
+ def modify_env
+ if new_resource.delim
+ new_resource.value((new_values + current_values).uniq.join(new_resource.delim))
+ end
+ create_env
+ end
+
+ # Returns the current values to split by delimiter
+ def current_values
+ @current_values ||= current_resource.value.split(new_resource.delim)
+ end
+
+ # Returns the new values to split by delimiter
+ def new_values
+ @new_values ||= new_resource.value.split(new_resource.delim)
+ end
+
+ def env_value(key_name)
+ obj = env_obj(key_name)
+ obj.variablevalue if obj
+ end
+
+ def env_obj(key_name)
+ return @env_obj if @env_obj
+ wmi = WmiLite::Wmi.new
+ # Note that by design this query is case insensitive with regard to key_name
+ environment_variables = wmi.query("select * from Win32_Environment where name = '#{key_name}'")
+ if environment_variables && environment_variables.length > 0
+ environment_variables.each do |env|
+ @env_obj = env.wmi_ole_object
+ return @env_obj if @env_obj.username.split('\\').last.casecmp(new_resource.user) == 0
+ end
+ end
+ @env_obj = nil
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/windows_path.rb b/lib/chef/provider/windows_path.rb
new file mode 100644
index 0000000000..1c78e20606
--- /dev/null
+++ b/lib/chef/provider/windows_path.rb
@@ -0,0 +1,61 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/mixin/windows_env_helper" if Chef::Platform.windows?
+require "chef/mixin/wide_string"
+require "chef/exceptions"
+
+class Chef
+ class Provider
+ class WindowsPath < Chef::Provider
+ include Chef::Mixin::WindowsEnvHelper if Chef::Platform.windows?
+
+ provides :windows_path
+
+ def load_current_resource
+ @current_resource = Chef::Resource::WindowsPath.new(new_resource.name)
+ @current_resource.path(new_resource.path)
+ @current_resource
+ end
+
+ action :add do
+ # The windows Env provider does not correctly expand variables in
+ # the PATH environment variable. Ruby expects these to be expanded.
+ #
+ path = expand_path(new_resource.path)
+ declare_resource(:env, "path") do
+ action :modify
+ delim ::File::PATH_SEPARATOR
+ value path.tr("/", '\\')
+ end
+ end
+
+ action :remove do
+ # The windows Env provider does not correctly expand variables in
+ # the PATH environment variable. Ruby expects these to be expanded.
+ #
+ path = expand_path(new_resource.path)
+ declare_resource(:env, "path") do
+ action :delete
+ delim ::File::PATH_SEPARATOR
+ value path.tr("/", '\\')
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/windows_script.rb b/lib/chef/provider/windows_script.rb
index 2de127addf..3b0202790c 100644
--- a/lib/chef/provider/windows_script.rb
+++ b/lib/chef/provider/windows_script.rb
@@ -33,8 +33,11 @@ class Chef
super( new_resource, run_context )
@script_extension = script_extension
- target_architecture = new_resource.architecture.nil? ?
- node_windows_architecture(run_context.node) : new_resource.architecture
+ target_architecture = if new_resource.architecture.nil?
+ node_windows_architecture(run_context.node)
+ else
+ new_resource.architecture
+ end
@is_wow64 = wow64_architecture_override_required?(run_context.node, target_architecture)
diff --git a/lib/chef/provider/windows_task.rb b/lib/chef/provider/windows_task.rb
new file mode 100644
index 0000000000..9a6fd39582
--- /dev/null
+++ b/lib/chef/provider/windows_task.rb
@@ -0,0 +1,587 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/mixin/shell_out"
+require "rexml/document"
+require "iso8601"
+require "chef/mixin/powershell_out"
+require "chef/provider"
+require "win32/taskscheduler" if Chef::Platform.windows?
+
+class Chef
+ class Provider
+ class WindowsTask < Chef::Provider
+ include Chef::Mixin::ShellOut
+ include Chef::Mixin::PowershellOut
+
+ if Chef::Platform.windows?
+ include Win32
+
+ provides :windows_task
+
+ MONTHS = {
+ JAN: TaskScheduler::JANUARY,
+ FEB: TaskScheduler::FEBRUARY,
+ MAR: TaskScheduler::MARCH,
+ APR: TaskScheduler::APRIL,
+ MAY: TaskScheduler::MAY,
+ JUN: TaskScheduler::JUNE,
+ JUL: TaskScheduler::JULY,
+ AUG: TaskScheduler::AUGUST,
+ SEP: TaskScheduler::SEPTEMBER,
+ OCT: TaskScheduler::OCTOBER,
+ NOV: TaskScheduler::NOVEMBER,
+ DEC: TaskScheduler::DECEMBER
+ }
+
+ DAYS_OF_WEEK = { MON: TaskScheduler::MONDAY,
+ TUE: TaskScheduler::TUESDAY,
+ WED: TaskScheduler::WEDNESDAY,
+ THU: TaskScheduler::THURSDAY,
+ FRI: TaskScheduler::FRIDAY,
+ SAT: TaskScheduler::SATURDAY,
+ SUN: TaskScheduler::SUNDAY }
+
+ WEEKS_OF_MONTH = {
+ FIRST: TaskScheduler::FIRST_WEEK,
+ SECOND: TaskScheduler::SECOND_WEEK,
+ THIRD: TaskScheduler::THIRD_WEEK,
+ FOURTH: TaskScheduler::FOURTH_WEEK
+ }
+
+ DAYS_OF_MONTH = {
+ 1 => TaskScheduler::TASK_FIRST,
+ 2 => TaskScheduler::TASK_SECOND,
+ 3 => TaskScheduler::TASK_THIRD,
+ 4 => TaskScheduler::TASK_FOURTH,
+ 5 => TaskScheduler::TASK_FIFTH,
+ 6 => TaskScheduler::TASK_SIXTH,
+ 7 => TaskScheduler::TASK_SEVENTH,
+ 8 => TaskScheduler::TASK_EIGHTH,
+ 9 => TaskScheduler::TASK_NINETH,
+ 10 => TaskScheduler::TASK_TENTH,
+ 11 => TaskScheduler::TASK_ELEVENTH,
+ 12 => TaskScheduler::TASK_TWELFTH,
+ 13 => TaskScheduler::TASK_THIRTEENTH,
+ 14 => TaskScheduler::TASK_FOURTEENTH,
+ 15 => TaskScheduler::TASK_FIFTEENTH,
+ 16 => TaskScheduler::TASK_SIXTEENTH,
+ 17 => TaskScheduler::TASK_SEVENTEENTH,
+ 18 => TaskScheduler::TASK_EIGHTEENTH,
+ 19 => TaskScheduler::TASK_NINETEENTH,
+ 20 => TaskScheduler::TASK_TWENTIETH,
+ 21 => TaskScheduler::TASK_TWENTY_FIRST,
+ 22 => TaskScheduler::TASK_TWENTY_SECOND,
+ 23 => TaskScheduler::TASK_TWENTY_THIRD,
+ 24 => TaskScheduler::TASK_TWENTY_FOURTH,
+ 25 => TaskScheduler::TASK_TWENTY_FIFTH,
+ 26 => TaskScheduler::TASK_TWENTY_SIXTH,
+ 27 => TaskScheduler::TASK_TWENTY_SEVENTH,
+ 28 => TaskScheduler::TASK_TWENTY_EIGHTH,
+ 29 => TaskScheduler::TASK_TWENTY_NINTH,
+ 30 => TaskScheduler::TASK_THIRTYETH,
+ 31 => TaskScheduler::TASK_THIRTY_FIRST
+ }
+
+ def load_current_resource
+ @current_resource = Chef::Resource::WindowsTask.new(new_resource.name)
+ task = TaskScheduler.new
+ if task.exists?(new_resource.task_name)
+ @current_resource.exists = true
+ task.get_task(new_resource.task_name)
+ @current_resource.task = task
+ pathed_task_name = new_resource.task_name.start_with?('\\') ? new_resource.task_name : "\\#{new_resource.task_name}"
+ @current_resource.task_name(pathed_task_name)
+ else
+ @current_resource.exists = false
+ end
+ @current_resource
+ end
+
+ def action_create
+ if current_resource.exists
+ logger.trace "#{new_resource} task exist."
+ unless (task_needs_update?(current_resource.task)) || (new_resource.force)
+ logger.info "#{new_resource} task does not need updating and force is not specified - nothing to do"
+ return
+ end
+
+ # if start_day and start_time is not set by user current date and time will be set while updating any property
+ set_start_day_and_time unless new_resource.frequency == :none
+ update_task(current_resource.task)
+ else
+ basic_validation
+ set_start_day_and_time
+ converge_by("#{new_resource} task created") do
+ task = TaskScheduler.new
+ if new_resource.frequency == :none
+ task.new_work_item(new_resource.task_name, {})
+ task.activate(new_resource.task_name)
+ else
+ task.new_work_item(new_resource.task_name, trigger)
+ end
+ task.application_name = new_resource.command
+ task.working_directory = new_resource.cwd if new_resource.cwd
+ task.configure_settings(config_settings)
+ task.configure_principals(principal_settings)
+ task.set_account_information(new_resource.user, new_resource.password)
+ task.creator = new_resource.user
+ task.activate(new_resource.task_name)
+ end
+ end
+ end
+
+ def action_run
+ if current_resource.exists
+ logger.trace "#{new_resource} task exists"
+ if current_resource.task.status == "running"
+ logger.info "#{new_resource} task is currently running, skipping run"
+ else
+ converge_by("run scheduled task #{new_resource}") do
+ current_resource.task.run
+ end
+ end
+ else
+ logger.warn "#{new_resource} task does not exist - nothing to do"
+ end
+ end
+
+ def action_delete
+ if current_resource.exists
+ logger.trace "#{new_resource} task exists"
+ converge_by("delete scheduled task #{new_resource}") do
+ ts = TaskScheduler.new
+ ts.delete(current_resource.task_name)
+ end
+ else
+ logger.warn "#{new_resource} task does not exist - nothing to do"
+ end
+ end
+
+ def action_end
+ if current_resource.exists
+ logger.trace "#{new_resource} task exists"
+ if current_resource.task.status != "running"
+ logger.trace "#{new_resource} is not running - nothing to do"
+ else
+ converge_by("#{new_resource} task ended") do
+ current_resource.task.stop
+ end
+ end
+ else
+ logger.warn "#{new_resource} task does not exist - nothing to do"
+ end
+ end
+
+ def action_enable
+ if current_resource.exists
+ logger.trace "#{new_resource} task exists"
+ if current_resource.task.status == "not scheduled"
+ converge_by("#{new_resource} task enabled") do
+ #TODO wind32-taskscheduler currently not having any method to handle this so using schtasks.exe here
+ run_schtasks "CHANGE", "ENABLE" => ""
+ end
+ else
+ logger.trace "#{new_resource} already enabled - nothing to do"
+ end
+ else
+ logger.fatal "#{new_resource} task does not exist - nothing to do"
+ raise Errno::ENOENT, "#{new_resource}: task does not exist, cannot enable"
+ end
+ end
+
+ def action_disable
+ if current_resource.exists
+ logger.info "#{new_resource} task exists"
+ if %w{ready running}.include?(current_resource.task.status)
+ converge_by("#{new_resource} task disabled") do
+ #TODO: in win32-taskscheduler there is no method whcih disbales the task so currently calling disable with schtasks.exe
+ run_schtasks "CHANGE", "DISABLE" => ""
+ end
+ else
+ logger.warn "#{new_resource} already disabled - nothing to do"
+ end
+ else
+ logger.warn "#{new_resource} task does not exist - nothing to do"
+ end
+ end
+
+ alias_method :action_change, :action_create
+
+ private
+
+ def set_start_day_and_time
+ new_resource.start_day = Time.now.strftime("%m/%d/%Y") unless new_resource.start_day
+ new_resource.start_time = Time.now.strftime("%H:%M") unless new_resource.start_time
+ end
+
+ def update_task(task)
+ converge_by("#{new_resource} task updated") do
+ task.set_account_information(new_resource.user, new_resource.password)
+ task.application_name = new_resource.command if new_resource.command
+ task.working_directory = new_resource.cwd if new_resource.cwd
+ task.trigger = trigger unless new_resource.frequency == :none
+ task.configure_settings(config_settings)
+ task.creator = new_resource.user
+ task.configure_principals(principal_settings)
+ end
+ end
+
+ def trigger
+ start_month, start_day, start_year = new_resource.start_day.to_s.split("/")
+ start_hour, start_minute = new_resource.start_time.to_s.split(":")
+ #TODO currently end_month, end_year and end_year needs to be set to 0. If not set win32-taskscheduler throwing nil into integer error.
+ trigger_hash = {
+ start_year: start_year.to_i,
+ start_month: start_month.to_i,
+ start_day: start_day.to_i,
+ start_hour: start_hour.to_i,
+ start_minute: start_minute.to_i,
+ end_month: 0,
+ end_day: 0,
+ end_year: 0,
+ trigger_type: trigger_type,
+ type: type,
+ random_minutes_interval: new_resource.random_delay
+ }
+
+ if new_resource.frequency == :minute
+ trigger_hash[:minutes_interval] = new_resource.frequency_modifier
+ end
+
+ if new_resource.frequency == :hourly
+ minutes = convert_hours_in_minutes(new_resource.frequency_modifier.to_i)
+ trigger_hash[:minutes_interval] = minutes
+ end
+
+ if new_resource.minutes_interval
+ trigger_hash[:minutes_interval] = new_resource.minutes_interval
+ end
+
+ if new_resource.minutes_duration
+ trigger_hash[:minutes_duration] = new_resource.minutes_duration
+ end
+
+ if trigger_type == TaskScheduler::MONTHLYDOW && frequency_modifier_contains_last_week?(new_resource.frequency_modifier)
+ trigger_hash[:run_on_last_week_of_month] = true
+ else
+ trigger_hash[:run_on_last_week_of_month] = false
+ end
+
+ if trigger_type == TaskScheduler::MONTHLYDATE && day_includes_last_or_lastday?(new_resource.day)
+ trigger_hash[:run_on_last_day_of_month] = true
+ else
+ trigger_hash[:run_on_last_day_of_month] = false
+ end
+ trigger_hash
+ end
+
+ def frequency_modifier_contains_last_week?(frequency_modifier)
+ frequency_modifier = frequency_modifier.to_s.split(",")
+ frequency_modifier.map! { |value| value.strip.upcase }
+ frequency_modifier.include?("LAST")
+ end
+
+ def day_includes_last_or_lastday?(day)
+ day = day.to_s.split(",")
+ day.map! { |value| value.strip.upcase }
+ day.include?("LAST") || day.include?("LASTDAY")
+ end
+
+ def convert_hours_in_minutes(hours)
+ hours.to_i * 60 if hours
+ end
+
+ #TODO : Try to optimize this method
+ # known issue : Since start_day and time is not mandatory while updating weekly frequency for which start_day is not mentioned by user idempotency
+ # is not gettting maintained as new_resource.start_day is nil and we fetch the day of week from start_day to set and its currently coming as nil and don't match with current_task
+ def task_needs_update?(task)
+ flag = false
+ if new_resource.frequency == :none
+ flag = (task.account_information != new_resource.user ||
+ task.application_name != new_resource.command ||
+ task.principals[:run_level] != run_level)
+ else
+ current_task_trigger = task.trigger(0)
+ new_task_trigger = trigger
+ flag = (ISO8601::Duration.new(task.idle_settings[:idle_duration])) != (ISO8601::Duration.new(new_resource.idle_time * 60)) if new_resource.frequency == :on_idle
+ flag = (ISO8601::Duration.new(task.execution_time_limit)) != (ISO8601::Duration.new(new_resource.execution_time_limit * 60)) unless new_resource.execution_time_limit.nil?
+
+ # if trigger not found updating the task to add the trigger
+ if current_task_trigger.nil?
+ flag = true
+ else
+ flag = true if start_day_updated?(current_task_trigger, new_task_trigger) == true ||
+ start_time_updated?(current_task_trigger, new_task_trigger) == true ||
+ current_task_trigger[:trigger_type] != new_task_trigger[:trigger_type] ||
+ current_task_trigger[:type] != new_task_trigger[:type] ||
+ current_task_trigger[:random_minutes_interval].to_i != new_task_trigger[:random_minutes_interval].to_i ||
+ current_task_trigger[:minutes_interval].to_i != new_task_trigger[:minutes_interval].to_i ||
+ task.account_information != new_resource.user ||
+ task.application_name != new_resource.command ||
+ task.working_directory != new_resource.cwd.to_s ||
+ task.principals[:logon_type] != logon_type ||
+ task.principals[:run_level] != run_level
+
+ if trigger_type == TaskScheduler::MONTHLYDATE
+ flag = true if current_task_trigger[:run_on_last_day_of_month] != new_task_trigger[:run_on_last_day_of_month]
+ end
+
+ if trigger_type == TaskScheduler::MONTHLYDOW
+ flag = true if current_task_trigger[:run_on_last_week_of_month] != new_task_trigger[:run_on_last_week_of_month]
+ end
+ end
+ end
+ flag
+ end
+
+ def start_day_updated?(current_task_trigger, new_task_trigger)
+ ( new_resource.start_day && (current_task_trigger[:start_year].to_i != new_task_trigger[:start_year] ||
+ current_task_trigger[:start_month].to_i != new_task_trigger[:start_month] ||
+ current_task_trigger[:start_day].to_i != new_task_trigger[:start_day]) )
+ end
+
+ def start_time_updated?(current_task_trigger, new_task_trigger)
+ ( new_resource.start_time && ( current_task_trigger[:start_hour].to_i != new_task_trigger[:start_hour] ||
+ current_task_trigger[:start_minute].to_i != new_task_trigger[:start_minute] ) )
+ end
+
+ def trigger_type
+ case new_resource.frequency
+ when :once, :minute, :hourly
+ TaskScheduler::ONCE
+ when :daily
+ TaskScheduler::DAILY
+ when :weekly
+ TaskScheduler::WEEKLY
+ when :monthly
+ # If frequency modifier is set with frequency :monthly we are setting taskscheduler as monthlydow
+ # Ref https://msdn.microsoft.com/en-us/library/windows/desktop/aa382061(v=vs.85).aspx
+ new_resource.frequency_modifier.to_i.between?(1, 12) ? TaskScheduler::MONTHLYDATE : TaskScheduler::MONTHLYDOW
+ when :on_idle
+ TaskScheduler::ON_IDLE
+ when :onstart
+ TaskScheduler::AT_SYSTEMSTART
+ when :on_logon
+ TaskScheduler::AT_LOGON
+ else
+ raise ArgumentError, "Please set frequency"
+ end
+ end
+
+ def type
+ case trigger_type
+ when TaskScheduler::ONCE
+ { once: nil }
+ when TaskScheduler::DAILY
+ { days_interval: new_resource.frequency_modifier.to_i }
+ when TaskScheduler::WEEKLY
+ { weeks_interval: new_resource.frequency_modifier.to_i, days_of_week: days_of_week.to_i }
+ when TaskScheduler::MONTHLYDATE
+ { months: months_of_year.to_i, days: days_of_month.to_i }
+ when TaskScheduler::MONTHLYDOW
+ { months: months_of_year.to_i, days_of_week: days_of_week.to_i, weeks_of_month: weeks_of_month.to_i }
+ when TaskScheduler::ON_IDLE
+ # TODO: handle option for this trigger
+ when TaskScheduler::AT_LOGON
+ # TODO: handle option for this trigger
+ when TaskScheduler::AT_SYSTEMSTART
+ # TODO: handle option for this trigger
+ end
+ end
+
+ # Deleting last from the array of weeks of month since last week is handled in :run_on_last_week_of_month parameter.
+ def weeks_of_month
+ weeks_of_month = []
+ if new_resource.frequency_modifier
+ weeks = new_resource.frequency_modifier.split(",")
+ weeks.map! { |week| week.to_s.strip.upcase }
+ weeks.delete("LAST") if weeks.include?("LAST")
+ weeks_of_month = get_binary_values_from_constants(weeks, WEEKS_OF_MONTH)
+ end
+ weeks_of_month
+ end
+
+ # Deleting the "LAST" and "LASTDAY" from days since last day is handled in :run_on_last_day_of_month parameter.
+ def days_of_month
+ days_of_month = []
+ if new_resource.day
+ days = new_resource.day.split(",")
+ days.map! { |day| day.to_s.strip.upcase }
+ days.delete("LAST") if days.include?("LAST")
+ days.delete("LASTDAY") if days.include?("LASTDAY")
+ if days - (1..31).to_a
+ days.each do |day|
+ days_of_month << DAYS_OF_MONTH[day.to_i]
+ end
+ days_of_month = days_of_month.size > 1 ? days_of_month.inject(:|) : days_of_month[0]
+ end
+ else
+ days_of_month = DAYS_OF_MONTH[1]
+ end
+ days_of_month
+ end
+
+ def days_of_week
+ if new_resource.day
+ #this line of code is just to support backward compatibility of wild card *
+ new_resource.day = "mon, tue, wed, thu, fri, sat, sun" if new_resource.day == "*" && new_resource.frequency == :weekly
+ days = new_resource.day.split(",")
+ days.map! { |day| day.to_s.strip.upcase }
+ weeks_days = get_binary_values_from_constants(days, DAYS_OF_WEEK)
+ else
+ # following condition will make the frequency :weekly idempotent if start_day is not provided by user setting day as the current_resource day
+ if (current_resource) && (current_resource.task) && (current_resource.task.trigger(0)[:type][:days_of_week]) && (new_resource.start_day.nil?)
+ weeks_days = current_resource.task.trigger(0)[:type][:days_of_week]
+ else
+ day = get_day(new_resource.start_day).to_sym if new_resource.start_day
+ DAYS_OF_WEEK[day]
+ end
+ end
+ end
+
+ def months_of_year
+ months_of_year = []
+ if new_resource.frequency_modifier.to_i.between?(1, 12) && !(new_resource.months)
+ new_resource.months = set_months(new_resource.frequency_modifier.to_i)
+ end
+
+ if new_resource.months
+ #this line of code is just to support backward compatibility of wild card *
+ new_resource.months = "jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec" if new_resource.months == "*" && new_resource.frequency == :monthly
+ months = new_resource.months.split(",")
+ months.map! { |month| month.to_s.strip.upcase }
+ months_of_year = get_binary_values_from_constants(months, MONTHS)
+ else
+ MONTHS.each do |key, value|
+ months_of_year << MONTHS[key]
+ end
+ months_of_year = months_of_year.inject(:|)
+ end
+ months_of_year
+ end
+
+ # This values are set for frequency_modifier set as 1-12
+ # This is to give backward compatibility validated this values with earlier code and running schtask.exe
+ # Used this as reference https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/schtasks#d-dayday--
+ def set_months(frequency_modifier)
+ case frequency_modifier
+ when 1
+ "jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec"
+ when 2
+ "feb, apr, jun, aug, oct, dec"
+ when 3
+ "mar, jun, sep, dec"
+ when 4
+ "apr, aug, dec"
+ when 5
+ "may, oct"
+ when 6
+ "jun, dec"
+ when 7
+ "jul"
+ when 8
+ "aug"
+ when 9
+ "sep"
+ when 10
+ "oct"
+ when 11
+ "nov"
+ when 12
+ "dec"
+ end
+ end
+
+ def get_binary_values_from_constants(array_values, constant)
+ data = []
+ array_values.each do |value|
+ value = value.to_sym
+ data << constant[value]
+ end
+ data.size > 1 ? data.inject(:|) : data[0]
+ end
+
+ def run_level
+ case new_resource.run_level
+ when :highest
+ TaskScheduler::TASK_RUNLEVEL_HIGHEST
+ when :limited
+ TaskScheduler::TASK_RUNLEVEL_LUA
+ end
+ end
+
+ #TODO: while creating the configuration settings win32-taskscheduler it accepts execution time limit values in ISO8601 formata
+ def config_settings
+ settings = {
+ execution_time_limit: new_resource.execution_time_limit,
+ enabled: true
+ }
+ settings[:idle_duration] = new_resource.idle_time if new_resource.idle_time
+ settings[:run_only_if_idle] = true if new_resource.idle_time
+ settings
+ end
+
+ def principal_settings
+ settings = {}
+ settings [:run_level] = run_level
+ settings[:logon_type] = logon_type
+ settings
+ end
+
+ def logon_type
+ # Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/aa383566(v=vs.85).aspx
+ # if nothing is passed as logon_type the TASK_LOGON_SERVICE_ACCOUNT is getting set as default so using that for comparision.
+ new_resource.password.nil? ? TaskScheduler::TASK_LOGON_SERVICE_ACCOUNT : TaskScheduler::TASK_LOGON_PASSWORD
+ end
+
+ # This method checks if task and command attributes exist since those two are mandatory attributes to create a schedules task.
+ def basic_validation
+ validate = []
+ validate << "Command" if new_resource.command.nil? || new_resource.command.empty?
+ validate << "Task Name" if new_resource.task_name.nil? || new_resource.task_name.empty?
+ return true if validate.empty?
+ raise Chef::Exceptions::ValidationFailed.new "Value for '#{validate.join(', ')}' option cannot be empty"
+ end
+
+ # rubocop:disable Style/StringLiteralsInInterpolation
+ def run_schtasks(task_action, options = {})
+ cmd = "schtasks /#{task_action} /TN \"#{new_resource.task_name}\" "
+ options.each_key do |option|
+ unless option == "TR"
+ cmd += "/#{option} "
+ cmd += "\"#{options[option].to_s.gsub('"', "\\\"")}\" " unless options[option] == ""
+ end
+ end
+ # Appending Task Run [TR] option at the end since appending causing sometimes to append other options in option["TR"] value
+ if options["TR"]
+ cmd += "/TR \"#{options["TR"]} \" " unless task_action == "DELETE"
+ end
+ logger.trace("running: ")
+ logger.trace(" #{cmd}")
+ shell_out!(cmd, returns: [0])
+ end
+ # rubocop:enable Style/StringLiteralsInInterpolation
+
+ def get_day(date)
+ Date.strptime(date, "%m/%d/%Y").strftime("%a").upcase
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/yum_repository.rb b/lib/chef/provider/yum_repository.rb
new file mode 100644
index 0000000000..20a7a8e3d2
--- /dev/null
+++ b/lib/chef/provider/yum_repository.rb
@@ -0,0 +1,130 @@
+#
+# Author:: Thom May (<thom@chef.io>)
+# Copyright:: Copyright (c) 2016-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+require "chef/dsl/declare_resource"
+require "chef/mixin/which"
+require "chef/provider/noop"
+
+class Chef
+ class Provider
+ class YumRepository < Chef::Provider
+ extend Chef::Mixin::Which
+
+ provides :yum_repository do
+ which "yum"
+ end
+
+ def load_current_resource
+ end
+
+ action :create do
+ declare_resource(:template, "/etc/yum.repos.d/#{new_resource.repositoryid}.repo") do
+ if template_available?(new_resource.source)
+ source new_resource.source
+ else
+ source ::File.expand_path("../support/yum_repo.erb", __FILE__)
+ local true
+ end
+ sensitive new_resource.sensitive
+ variables(config: new_resource)
+ mode new_resource.mode
+ if new_resource.make_cache
+ notifies :run, "execute[yum clean metadata #{new_resource.repositoryid}]", :immediately if new_resource.clean_metadata || new_resource.clean_headers
+ notifies :run, "execute[yum-makecache-#{new_resource.repositoryid}]", :immediately
+ notifies :create, "ruby_block[package-cache-reload-#{new_resource.repositoryid}]", :immediately
+ end
+ end
+
+ declare_resource(:execute, "yum clean metadata #{new_resource.repositoryid}") do
+ command "yum clean metadata --disablerepo=* --enablerepo=#{new_resource.repositoryid}"
+ action :nothing
+ end
+
+ # get the metadata for this repo only
+ declare_resource(:execute, "yum-makecache-#{new_resource.repositoryid}") do
+ command "yum -q -y makecache --disablerepo=* --enablerepo=#{new_resource.repositoryid}"
+ action :nothing
+ only_if { new_resource.enabled }
+ end
+
+ # reload internal Chef yum/dnf cache
+ declare_resource(:ruby_block, "package-cache-reload-#{new_resource.repositoryid}") do
+ if ( platform?("fedora") && node["platform_version"].to_i >= 22 ) ||
+ ( platform_family?("rhel") && node["platform_version"].to_i >= 8 )
+ block { Chef::Provider::Package::Dnf::PythonHelper.instance.restart }
+ else
+ block { Chef::Provider::Package::Yum::YumCache.instance.reload }
+ end
+ action :nothing
+ end
+ end
+
+ action :delete do
+ # clean the repo cache first
+ declare_resource(:execute, "yum clean all #{new_resource.repositoryid}") do
+ command "yum clean all --disablerepo=* --enablerepo=#{new_resource.repositoryid}"
+ only_if "yum repolist all | grep -P '^#{new_resource.repositoryid}([ \t]|$)'"
+ end
+
+ declare_resource(:file, "/etc/yum.repos.d/#{new_resource.repositoryid}.repo") do
+ action :delete
+ notifies :create, "ruby_block[package-cache-reload-#{new_resource.repositoryid}]", :immediately
+ end
+
+ declare_resource(:ruby_block, "package-cache-reload-#{new_resource.repositoryid}") do
+ if ( platform?("fedora") && node["platform_version"].to_i >= 22 ) ||
+ ( platform_family?("rhel") && node["platform_version"].to_i >= 8 )
+ block { Chef::Provider::Package::Dnf::PythonHelper.instance.restart }
+ else
+ block { Chef::Provider::Package::Yum::YumCache.instance.reload }
+ end
+ action :nothing
+ end
+ end
+
+ action :makecache do
+ declare_resource(:execute, "yum-makecache-#{new_resource.repositoryid}") do
+ command "yum -q -y makecache --disablerepo=* --enablerepo=#{new_resource.repositoryid}"
+ action :run
+ only_if { new_resource.enabled }
+ end
+
+ declare_resource(:ruby_block, "package-cache-reload-#{new_resource.repositoryid}") do
+ if ( platform?("fedora") && node["platform_version"].to_i >= 22 ) ||
+ ( platform_family?("rhel") && node["platform_version"].to_i >= 8 )
+ block { Chef::Provider::Package::Dnf::PythonHelper.instance.restart }
+ else
+ block { Chef::Provider::Package::Yum::YumCache.instance.reload }
+ end
+ action :run
+ end
+ end
+
+ alias_method :action_add, :action_create
+ alias_method :action_remove, :action_delete
+
+ def template_available?(path)
+ !path.nil? && run_context.has_template_in_cookbook?(new_resource.cookbook_name, path)
+ end
+
+ end
+ end
+end
+
+Chef::Provider::Noop.provides :yum_repository
diff --git a/lib/chef/provider/zypper_repository.rb b/lib/chef/provider/zypper_repository.rb
new file mode 100644
index 0000000000..369d23a396
--- /dev/null
+++ b/lib/chef/provider/zypper_repository.rb
@@ -0,0 +1,169 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+require "chef/dsl/declare_resource"
+require "chef/provider/noop"
+require "chef/mixin/shell_out"
+require "shellwords"
+
+class Chef
+ class Provider
+ class ZypperRepository < Chef::Provider
+ provides :zypper_repository, platform_family: "suse"
+
+ def load_current_resource
+ end
+
+ action :create do
+ if new_resource.gpgautoimportkeys
+ install_gpg_key(new_resource.gpgkey)
+ else
+ logger.trace("'gpgautoimportkeys' property is set to false. Skipping key import.")
+ end
+
+ declare_resource(:template, "/etc/zypp/repos.d/#{escaped_repo_name}.repo") do
+ if template_available?(new_resource.source)
+ source new_resource.source
+ else
+ source ::File.expand_path("../support/zypper_repo.erb", __FILE__)
+ local true
+ end
+ sensitive new_resource.sensitive
+ variables(config: new_resource)
+ mode new_resource.mode
+ notifies :refresh, new_resource, :immediately if new_resource.refresh_cache
+ end
+ end
+
+ action :delete do
+ declare_resource(:execute, "zypper --quiet --non-interactive removerepo #{escaped_repo_name}") do
+ only_if "zypper --quiet lr #{escaped_repo_name}"
+ end
+ end
+
+ action :refresh do
+ declare_resource(:execute, "zypper --quiet --non-interactive refresh --force #{escaped_repo_name}") do
+ only_if "zypper --quiet lr #{escaped_repo_name}"
+ end
+ end
+
+ alias_method :action_add, :action_create
+ alias_method :action_remove, :action_delete
+
+ # zypper repos are allowed to have spaces in the names
+ # @return [String] escaped repo string
+ def escaped_repo_name
+ Shellwords.escape(new_resource.repo_name)
+ end
+
+ # return the specified cookbook name or the cookbook containing the
+ # resource.
+ #
+ # @return [String] name of the cookbook
+ def cookbook_name
+ new_resource.cookbook || new_resource.cookbook_name
+ end
+
+ # determine if a template file is available in the current run
+ # @param [String] path the path to the template file
+ #
+ # @return [Boolean] template file exists or doesn't
+ def template_available?(path)
+ !path.nil? && run_context.has_template_in_cookbook?(cookbook_name, path)
+ end
+
+ # determine if a cookbook file is available in the run
+ # @param [String] fn the path to the template file
+ #
+ # @return [Boolean] cookbook file exists or doesn't
+ def has_cookbook_file?(fn)
+ run_context.has_cookbook_file_in_cookbook?(cookbook_name, fn)
+ end
+
+ # Given the provided key URI determine what kind of chef resource we need
+ # to fetch the key
+ # @param [String] uri the uri of the gpg key (local path or http URL)
+ #
+ # @raise [Chef::Exceptions::FileNotFound] Key isn't remote or found in the current run
+ #
+ # @return [Symbol] :remote_file or :cookbook_file
+ def key_type(uri)
+ if uri.start_with?("http")
+ logger.trace("Will use :remote_file resource to cache the gpg key locally")
+ :remote_file
+ elsif has_cookbook_file?(uri)
+ logger.trace("Will use :cookbook_file resource to cache the gpg key locally")
+ :cookbook_file
+ else
+ raise Chef::Exceptions::FileNotFound, "Cannot determine location of gpgkey. Must start with 'http' or be a file managed by Chef."
+ end
+ end
+
+ # is the provided key already installed
+ # @param [String] key_path the path to the key on the local filesystem
+ #
+ # @return [boolean] is the key already known by rpm
+ def key_installed?(key_path)
+ so = shell_out("rpm -qa gpg-pubkey*")
+ # expected output & match: http://rubular.com/r/RdF7EcXEtb
+ status = /gpg-pubkey-#{key_fingerprint(key_path)}/.match(so.stdout)
+ logger.trace("GPG key at #{key_path} is known by rpm? #{status ? "true" : "false"}")
+ status
+ end
+
+ # extract the gpg key fingerprint from a local file
+ # @param [String] key_path the path to the key on the local filesystem
+ #
+ # @return [String] the fingerprint of the key
+ def key_fingerprint(key_path)
+ so = shell_out!("gpg --with-fingerprint #{key_path}")
+ # expected output and match: http://rubular.com/r/BpfMjxySQM
+ fingerprint = /pub\s*\S*\/(\S*)/.match(so.stdout)[1].downcase
+ logger.trace("GPG fingerprint of key at #{key_path} is #{fingerprint}")
+ fingerprint
+ end
+
+ # install the provided gpg key
+ # @param [String] uri the uri of the local or remote gpg key
+ def install_gpg_key(uri)
+ unless uri
+ logger.trace("'gpgkey' property not provided or set to nil. Skipping key import.")
+ return
+ end
+
+ cached_keyfile = ::File.join(Chef::Config[:file_cache_path], uri.split("/")[-1])
+
+ declare_resource(key_type(new_resource.gpgkey), cached_keyfile) do
+ source uri
+ mode "0644"
+ sensitive new_resource.sensitive
+ action :create
+ end
+
+ declare_resource(:execute, "import gpg key from #{new_resource.gpgkey}") do
+ command "/bin/rpm --import #{cached_keyfile}"
+ not_if { key_installed?(cached_keyfile) }
+ action :run
+ end
+ end
+ end
+ end
+end
+
+Chef::Provider::Noop.provides :zypper_repository
diff --git a/lib/chef/provider_resolver.rb b/lib/chef/provider_resolver.rb
index 1df473abbd..c7779e52a7 100644
--- a/lib/chef/provider_resolver.rb
+++ b/lib/chef/provider_resolver.rb
@@ -58,8 +58,9 @@ class Chef
def resolve
maybe_explicit_provider(resource) ||
+ maybe_custom_resource(resource) ||
maybe_dynamic_provider_resolution(resource, action) ||
- maybe_chef_platform_lookup(resource)
+ raise(Chef::Exceptions::ProviderNotFound, "Cannot find a provider for #{resource} on #{node["platform"]} version #{node["platform_version"]}")
end
# Does NOT call provides? on the resource (it is assumed this is being
@@ -90,9 +91,11 @@ class Chef
@prioritized_handlers ||= begin
supported_handlers = self.supported_handlers
if supported_handlers.empty?
- # if none of the providers specifically support the resource, we still need to pick one of the providers that are
- # enabled on the node to handle the why-run use case. FIXME we should only do this in why-run mode then.
- Chef::Log.debug "No providers responded true to `supports?` for action #{action} on resource #{resource}, falling back to enabled handlers so we can return something anyway."
+ # We always require a provider to be able to call define_resource_requirements on. In the why-run case we need
+ # a provider to say "assuming /etc/init.d/whatever would have been installed" and in the non-why-run case we
+ # need to make a best guess at "cannot find /etc/init.d/whatever". We are essentially defining a "default" provider
+ # for the platform, which is the best we can do, but which might give misleading errors, but we cannot read minds.
+ Chef::Log.trace "No providers responded true to `supports?` for action #{action} on resource #{resource}, falling back to enabled handlers so we can return something anyway."
supported_handlers = enabled_handlers
end
@@ -103,32 +106,31 @@ class Chef
end
end
+ # if its a custom resource, just grab the action class
+ def maybe_custom_resource(resource)
+ resource.class.action_class if resource.class.custom_resource?
+ end
+
# if resource.provider is set, just return one of those objects
def maybe_explicit_provider(resource)
- return nil unless resource.provider
- resource.provider
+ resource.provider if resource.provider
end
# try dynamically finding a provider based on querying the providers to see what they support
def maybe_dynamic_provider_resolution(resource, action)
- Chef::Log.debug "Providers for generic #{resource.resource_name} resource enabled on node include: #{enabled_handlers}"
+ Chef::Log.trace "Providers for generic #{resource.resource_name} resource enabled on node include: #{enabled_handlers}"
handler = prioritized_handlers.first
if handler
- Chef::Log.debug "Provider for action #{action} on resource #{resource} is #{handler}"
+ Chef::Log.trace "Provider for action #{action} on resource #{resource} is #{handler}"
else
- Chef::Log.debug "Dynamic provider resolver FAILED to resolve a provider for action #{action} on resource #{resource}"
+ Chef::Log.trace "Dynamic provider resolver FAILED to resolve a provider for action #{action} on resource #{resource}"
end
handler
end
- # try the old static lookup of providers by platform
- def maybe_chef_platform_lookup(resource)
- Chef::Platform.find_provider_for_node(node, resource)
- end
-
def priority_map
Chef.provider_priority_map
end
@@ -140,31 +142,5 @@ class Chef
def overrode_provides?(handler)
handler.method(:provides?).owner != Chef::Provider.method(:provides?).owner
end
-
- module Deprecated
- # return a deterministically sorted list of Chef::Provider subclasses
- def providers
- @providers ||= Chef::Provider.descendants
- end
-
- def enabled_handlers
- @enabled_handlers ||= begin
- handlers = super
- if handlers.empty?
- # Look through all providers, and find ones that return true to provides.
- # Don't bother with ones that don't override provides?, since they
- # would have been in enabled_handlers already if that were so. (It's a
- # perf concern otherwise.)
- handlers = providers.select { |handler| overrode_provides?(handler) && handler.provides?(node, resource) }
- handlers.each do |handler|
- Chef.log_deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource.resource_name}, but provides #{resource.resource_name.inspect} was never called!")
- Chef.log_deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
- end
- end
- handlers
- end
- end
- end
- prepend Deprecated
end
end
diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb
index 14c47df939..1b9aa84697 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -17,19 +17,16 @@
#
require "chef/provider/apt_update"
+require "chef/provider/apt_preference"
require "chef/provider/apt_repository"
require "chef/provider/batch"
-require "chef/provider/breakpoint"
require "chef/provider/cookbook_file"
require "chef/provider/cron"
require "chef/provider/cron/solaris"
require "chef/provider/cron/aix"
-require "chef/provider/deploy"
require "chef/provider/directory"
require "chef/provider/dsc_script"
require "chef/provider/dsc_resource"
-require "chef/provider/env"
-require "chef/provider/erl_call"
require "chef/provider/execute"
require "chef/provider/file"
require "chef/provider/git"
@@ -58,13 +55,16 @@ require "chef/provider/systemd_unit"
require "chef/provider/template"
require "chef/provider/user"
require "chef/provider/whyrun_safe_ruby_block"
-
-require "chef/provider/env/windows"
+require "chef/provider/windows_env"
+require "chef/provider/yum_repository"
+require "chef/provider/windows_task"
+require "chef/provider/zypper_repository"
+require "chef/provider/windows_path"
require "chef/provider/package/apt"
require "chef/provider/package/chocolatey"
require "chef/provider/package/dpkg"
-require "chef/provider/package/easy_install"
+require "chef/provider/package/dnf"
require "chef/provider/package/freebsd/port"
require "chef/provider/package/freebsd/pkg"
require "chef/provider/package/freebsd/pkgng"
@@ -81,7 +81,10 @@ require "chef/provider/package/yum"
require "chef/provider/package/zypper"
require "chef/provider/package/solaris"
require "chef/provider/package/smartos"
-require "chef/provider/package/aix"
+require "chef/provider/package/bff"
+require "chef/provider/package/cab"
+require "chef/provider/package/powershell"
+require "chef/provider/package/msu"
require "chef/provider/service/arch"
require "chef/provider/service/freebsd"
@@ -101,12 +104,13 @@ require "chef/provider/service/macosx"
require "chef/provider/service/aixinit"
require "chef/provider/service/aix"
+require "chef/provider/user/aix"
require "chef/provider/user/dscl"
+require "chef/provider/user/linux"
require "chef/provider/user/pw"
+require "chef/provider/user/solaris"
require "chef/provider/user/useradd"
require "chef/provider/user/windows"
-require "chef/provider/user/solaris"
-require "chef/provider/user/aix"
require "chef/provider/group/aix"
require "chef/provider/group/dscl"
@@ -123,9 +127,6 @@ require "chef/provider/mount/aix"
require "chef/provider/mount/solaris"
require "chef/provider/mount/windows"
-require "chef/provider/deploy/revision"
-require "chef/provider/deploy/timestamped"
-
require "chef/provider/remote_file/ftp"
require "chef/provider/remote_file/sftp"
require "chef/provider/remote_file/http"
diff --git a/lib/chef/recipe.rb b/lib/chef/recipe.rb
index 3cc5634dc8..f00b211630 100644
--- a/lib/chef/recipe.rb
+++ b/lib/chef/recipe.rb
@@ -1,7 +1,7 @@
#--
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,16 +18,7 @@
#
require "chef/dsl/recipe"
-require "chef/dsl/data_query"
-require "chef/dsl/platform_introspection"
-require "chef/dsl/include_recipe"
-require "chef/dsl/registry_helper"
-require "chef/dsl/reboot_pending"
-require "chef/dsl/audit"
-require "chef/dsl/powershell"
-
require "chef/mixin/from_file"
-
require "chef/mixin/deprecation"
class Chef
@@ -75,12 +66,6 @@ class Chef
run_context.node
end
- # Used by the DSL to look up resources when executing in the context of a
- # recipe.
- def resources(*args)
- run_context.resource_collection.find(*args)
- end
-
# This was moved to Chef::Node#tag, redirecting here for compatibility
def tag(*tags)
run_context.node.tag(*tags)
@@ -113,5 +98,13 @@ class Chef
run_context.node.tags.delete(tag)
end
end
+
+ def to_s
+ "cookbook: #{cookbook_name ? cookbook_name : "(none)"}, recipe: #{recipe_name ? recipe_name : "(none)"} "
+ end
+
+ def inspect
+ to_s
+ end
end
end
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index 21b2308688..05349b80e7 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -19,11 +19,12 @@
#
require "chef/exceptions"
-require "chef/dsl/platform_introspection"
require "chef/dsl/data_query"
require "chef/dsl/registry_helper"
require "chef/dsl/reboot_pending"
require "chef/dsl/resources"
+require "chef/dsl/declare_resource"
+require "chef/json_compat"
require "chef/mixin/convert_to_class_name"
require "chef/guard_interpreter/resource_guard_interpreter"
require "chef/resource/conditional"
@@ -42,8 +43,7 @@ require "set"
require "chef/mixin/deprecation"
require "chef/mixin/properties"
require "chef/mixin/provides"
-require "chef/mixin/shell_out"
-require "chef/mixin/powershell_out"
+require "chef/dsl/universal"
class Chef
class Resource
@@ -52,15 +52,13 @@ class Chef
# Generic User DSL (not resource-specific)
#
+ include Chef::DSL::DeclareResource
include Chef::DSL::DataQuery
- include Chef::DSL::PlatformIntrospection
include Chef::DSL::RegistryHelper
include Chef::DSL::RebootPending
extend Chef::Mixin::Provides
- # This lets user code do things like `not_if { shell_out!("command") }`
- include Chef::Mixin::ShellOut
- include Chef::Mixin::PowershellOut
+ include Chef::DSL::Universal
# Bring in `property` and `property_type`
include Chef::Mixin::Properties
@@ -88,7 +86,7 @@ class Chef
# @param name [Object] The name to set, typically a String or Array
# @return [String] The name of this Resource.
#
- property :name, String, coerce: proc { |v| v.is_a?(Array) ? v.join(", ") : v.to_s }, desired_state: false
+ property :name, String, coerce: proc { |v| v.is_a?(Array) ? v.join(", ") : v.to_s }, desired_state: false, required: true
#
# The node the current Chef run is using.
@@ -102,26 +100,6 @@ class Chef
end
#
- # Find existing resources by searching the list of existing resources. Possible
- # forms are:
- #
- # find(:file => "foobar")
- # find(:file => [ "foobar", "baz" ])
- # find("file[foobar]", "file[baz]")
- # find("file[foobar,baz]")
- #
- # Calls `run_context.resource_collection.find(*args)`
- #
- # @return the matching resource, or an Array of matching resources.
- #
- # @raise ArgumentError if you feed it bad lookup information
- # @raise RuntimeError if it can't find the resources you are looking for.
- #
- def resources(*args)
- run_context.resource_collection.find(*args)
- end
-
- #
# Resource User Interface (for users)
#
@@ -135,7 +113,13 @@ class Chef
def initialize(name, run_context = nil)
name(name) unless name.nil?
@run_context = run_context
- @noop = nil
+
+ @logger = if run_context
+ run_context.logger.with_child({ name: name, resource: resource_name })
+ else
+ Chef::Log.with_child({ name: name, resource: resource_name })
+ end
+
@before = nil
@params = Hash.new
@provider = nil
@@ -143,13 +127,10 @@ class Chef
@action = self.class.default_action
@updated = false
@updated_by_last_action = false
- @supports = {}
- @ignore_failure = false
- @retries = 0
- @retry_delay = 2
@not_if = []
@only_if = []
@source_line = nil
+ @deprecated = false
# We would like to raise an error when the user gives us a guard
# interpreter and a ruby_block to the guard. In order to achieve this
# we need to understand when the user overrides the default guard
@@ -158,7 +139,6 @@ class Chef
@guard_interpreter = nil
@default_guard_interpreter = :default
@elapsed_time = 0
- @sensitive = false
end
#
@@ -186,6 +166,25 @@ class Chef
alias_method :action=, :action
#
+ # Force a delayed notification into this resource's run_context.
+ #
+ # This should most likely be paired with action :nothing
+ #
+ # @param arg [Array[Symbol], Symbol] A list of actions (e.g. `:create`)
+ #
+ def delayed_action(arg)
+ arg = Array(arg).map(&:to_sym)
+ arg.map do |action|
+ validate(
+ { action: action },
+ { action: { kind_of: Symbol, equal_to: allowed_actions } }
+ )
+ # the resource effectively sends a delayed notification to itself
+ run_context.add_delayed_action(Notification.new(self, action, self))
+ end
+ end
+
+ #
# Sets up a notification that will run a particular action on another resource
# if and when *this* resource is updated by an action.
#
@@ -265,6 +264,18 @@ class Chef
end
#
+ # Token class to hold an unresolved subscribes call with an associated
+ # run context.
+ #
+ # @api private
+ # @see Resource#subscribes
+ class UnresolvedSubscribes < self
+ # The full key ise given as the name in {Resource#subscribes}
+ alias_method :to_s, :name
+ alias_method :declared_key, :name
+ end
+
+ #
# Subscribes to updates from other resources, causing a particular action to
# run on *this* resource when the other resource is updated.
#
@@ -331,7 +342,7 @@ class Chef
resources = [resources].flatten
resources.each do |resource|
if resource.is_a?(String)
- resource = Chef::Resource.new(resource, run_context)
+ resource = UnresolvedSubscribes.new(resource, run_context)
end
if resource.run_context.nil?
resource.run_context = run_context
@@ -411,10 +422,7 @@ class Chef
# @param arg [Integer] The number of retries.
# @return [Integer] The number of retries.
#
- def retries(arg = nil)
- set_or_return(:retries, arg, kind_of: Integer)
- end
- attr_writer :retries
+ property :retries, Integer, default: 0, desired_state: false
#
# The number of seconds to wait between retries. Default: 2.
@@ -422,10 +430,7 @@ class Chef
# @param arg [Integer] The number of seconds to wait between retries.
# @return [Integer] The number of seconds to wait between retries.
#
- def retry_delay(arg = nil)
- set_or_return(:retry_delay, arg, kind_of: Integer)
- end
- attr_writer :retry_delay
+ property :retry_delay, Integer, default: 2, desired_state: false
#
# Whether to treat this resource's data as sensitive. If set, no resource
@@ -434,15 +439,7 @@ class Chef
# @param arg [Boolean] Whether this resource is sensitive or not.
# @return [Boolean] Whether this resource is sensitive or not.
#
- def sensitive(arg = nil)
- set_or_return(:sensitive, arg, :kind_of => [ TrueClass, FalseClass ])
- end
- attr_writer :sensitive
-
- # ??? TODO unreferenced. Delete?
- attr_reader :not_if_args
- # ??? TODO unreferenced. Delete?
- attr_reader :only_if_args
+ property :sensitive, [ TrueClass, FalseClass ], default: false, desired_state: false
#
# The time it took (in seconds) to run the most recently-run action. Not
@@ -490,22 +487,13 @@ class Chef
state_properties = self.class.state_properties
state_properties.each do |property|
if property.identity? || property.is_set?(self)
- state[property.name] = send(property.name)
+ state[property.name] = property.sensitive? ? "*sensitive value suppressed*" : send(property.name)
end
end
state
end
#
- # Since there are collisions with LWRP parameters named 'state' this
- # method is not used by the resource_reporter and is most likely unused.
- # It certainly cannot be relied upon and cannot be fixed.
- #
- # @deprecated
- #
- alias_method :state, :state_for_resource_reporter
-
- #
# The value of the identity of this resource.
#
# - If there are no identity properties on the resource, `name` is returned.
@@ -527,23 +515,16 @@ class Chef
#
# Whether to ignore failures. If set to `true`, and this resource when an
# action is run, the resource will be marked as failed but no exception will
- # be thrown (and no error will be output). Defaults to `false`.
+ # be thrown (and no error will be output). Defaults to `false`. If set to
+ # `:quiet` or `'quiet'`, the normal error trace will be suppressed.
#
# TODO ignore_failure and retries seem to be mutually exclusive; I doubt
# that was intended.
#
- # @param arg [Boolean] Whether to ignore failures.
+ # @param arg [Boolean, String, Symbol] Whether to ignore failures.
# @return Whether this resource will ignore failures.
#
- def ignore_failure(arg = nil)
- set_or_return(:ignore_failure, arg, kind_of: [ TrueClass, FalseClass ])
- end
- attr_writer :ignore_failure
-
- #
- # Equivalent to #ignore_failure.
- #
- alias :epic_fail :ignore_failure
+ property :ignore_failure, [ true, false, :quiet, "quiet" ], default: false, desired_state: false
#
# Make this resource into an exact (shallow) copy of the other resource.
@@ -553,7 +534,7 @@ class Chef
def load_from(resource)
resource.instance_variables.each do |iv|
unless iv == :@source_line || iv == :@action || iv == :@not_if || iv == :@only_if
- self.instance_variable_set(iv, resource.instance_variable_get(iv))
+ instance_variable_set(iv, resource.instance_variable_get(iv))
end
end
end
@@ -577,9 +558,9 @@ class Chef
resolve_notification_references
validate_action(action)
- if Chef::Config[:verbose_logging] || Chef::Log.level == :debug
+ if Chef::Config[:verbose_logging] || logger.level == :debug
# This can be noisy
- Chef::Log.info("Processing #{self} action #{action} (#{defined_at})")
+ logger.info("Processing #{self} action #{action} (#{defined_at})")
end
# ensure that we don't leave @updated_by_last_action set to true
@@ -594,14 +575,14 @@ class Chef
begin
return if should_skip?(action)
provider_for_action(action).run_action
- rescue Exception => e
+ rescue StandardError => e
if ignore_failure
- Chef::Log.error("#{custom_exception_message(e)}; ignore_failure is set, continuing")
+ logger.error("#{custom_exception_message(e)}; ignore_failure is set, continuing")
events.resource_failed(self, action, e)
elsif remaining_retries > 0
events.resource_failed_retriable(self, action, remaining_retries, e)
remaining_retries -= 1
- Chef::Log.info("Retrying execution of #{self}, #{remaining_retries} attempt(s) left")
+ logger.info("Retrying execution of #{self}, #{remaining_retries} attempt#{"s" if remaining_retries > 1} left")
sleep retry_delay
retry
else
@@ -641,21 +622,38 @@ class Chef
def to_text
return "suppressed sensitive resource output" if sensitive
- ivars = instance_variables.map { |ivar| ivar.to_sym } - HIDDEN_IVARS
text = "# Declared in #{@source_line}\n\n"
text << "#{resource_name}(\"#{name}\") do\n"
+
+ all_props = {}
+ self.class.state_properties.map do |p|
+ begin
+ all_props[p.name.to_s] = p.sensitive? ? '"*sensitive value suppressed*"' : value_to_text(p.get(self))
+ rescue Chef::Exceptions::ValidationFailed
+ # This space left intentionally blank, the property was probably required or had an invalid default.
+ end
+ end
+
+ ivars = instance_variables.map { |ivar| ivar.to_sym } - HIDDEN_IVARS
ivars.each do |ivar|
- if (value = instance_variable_get(ivar)) && !(value.respond_to?(:empty?) && value.empty?)
- value_string = value.respond_to?(:to_text) ? value.to_text : value.inspect
- text << " #{ivar.to_s.sub(/^@/, '')} #{value_string}\n"
+ iv = ivar.to_s.sub(/^@/, "")
+ if all_props.keys.include?(iv)
+ text << " #{iv} #{all_props[iv]}\n"
+ elsif (value = instance_variable_get(ivar)) && !(value.respond_to?(:empty?) && value.empty?)
+ text << " #{iv} #{value_to_text(value)}\n"
end
end
+
[@not_if, @only_if].flatten.each do |conditional|
text << " #{conditional.to_text}\n"
end
text << "end\n"
end
+ def value_to_text(value)
+ value.respond_to?(:to_text) ? value.to_text : value.inspect
+ end
+
def inspect
ivars = instance_variables.map { |ivar| ivar.to_sym } - FORBIDDEN_IVARS
ivars.inject("<#{self}") do |str, ivar|
@@ -699,14 +697,22 @@ class Chef
result
end
- def self.json_create(o)
- resource = self.new(o["instance_vars"]["@name"])
+ def self.from_hash(o)
+ resource = new(o["instance_vars"]["@name"])
o["instance_vars"].each do |k, v|
resource.instance_variable_set("@#{k}".to_sym, v)
end
resource
end
+ def self.json_create(o)
+ from_hash(o)
+ end
+
+ def self.from_json(j)
+ from_hash(Chef::JSONCompat.parse(j))
+ end
+
#
# Resource Definition Interface (for resource developers)
#
@@ -736,8 +742,7 @@ class Chef
else
arg
end
- set_or_return(:provider, klass, kind_of: [ Class ]) ||
- self.class.action_class
+ set_or_return(:provider, klass, kind_of: [ Class ])
end
def provider=(arg)
@@ -842,7 +847,7 @@ class Chef
# @return [Array<Symbol>] The list of actions this Resource is allowed to
# have.
#
- attr_accessor :allowed_actions
+ attr_writer :allowed_actions
def allowed_actions(value = NOT_PASSED)
if value != NOT_PASSED
self.allowed_actions = value
@@ -894,19 +899,6 @@ class Chef
end
#
- # Set whether this class was updated during an action.
- #
- # @deprecated Multiple actions are supported by resources. Please call {}#updated_by_last_action} instead.
- #
- def updated=(true_or_false)
- Chef::Log.warn("Chef::Resource#updated=(true|false) is deprecated. Please call #updated_by_last_action(true|false) instead.")
- Chef::Log.warn("Called from:")
- caller[0..3].each { |line| Chef::Log.warn(line) }
- updated_by_last_action(true_or_false)
- @updated = true_or_false
- end
-
- #
# The display name of this resource type, for printing purposes.
#
# Will be used to print out the resource in messages, e.g. resource_name[name]
@@ -918,29 +910,6 @@ class Chef
end
#
- # Sets a list of capabilities of the real resource. For example, `:remount`
- # (for filesystems) and `:restart` (for services).
- #
- # TODO Calling resource.supports({}) will not set this to empty; it will do
- # a get instead. That's wrong.
- #
- # @param args Hash{Symbol=>Boolean} If non-empty, sets the capabilities of
- # this resource. Default: {}
- # @return Hash{Symbol=>Boolean} An array of things this resource supports.
- #
- def supports(args = {})
- if args.any?
- @supports = args
- else
- @supports
- end
- end
-
- def supports=(args)
- supports(args)
- end
-
- #
# A hook called after a resource is created. Meant to be overriden by
# subclasses.
#
@@ -949,21 +918,6 @@ class Chef
end
#
- # The DSL name of this resource (e.g. `package` or `yum_package`)
- #
- # @return [String] The DSL name of this resource.
- #
- # @deprecated Use resource_name instead.
- #
- def self.dsl_name
- Chef.log_deprecation "Resource.dsl_name is deprecated and will be removed in Chef 13. Use resource_name instead."
- if name
- name = self.name.split("::")[-1]
- convert_to_snake_case(name)
- end
- end
-
- #
# The display name of this resource type, for printing purposes.
#
# This also automatically calls "provides" to provide DSL with the given
@@ -1012,34 +966,11 @@ class Chef
# A::B::BlahDBlah -> blah_d_blah
#
def self.use_automatic_resource_name
- automatic_name = convert_to_snake_case(self.name.split("::")[-1])
+ automatic_name = convert_to_snake_case(name.split("::")[-1])
resource_name automatic_name
end
#
- # The module where Chef should look for providers for this resource.
- # The provider for `MyResource` will be looked up using
- # `provider_base::MyResource`. Defaults to `Chef::Provider`.
- #
- # @param arg [Module] The module containing providers for this resource
- # @return [Module] The module containing providers for this resource
- #
- # @example
- # class MyResource < Chef::Resource
- # provider_base Chef::Provider::Deploy
- # # ...other stuff
- # end
- #
- # @deprecated Use `provides` on the provider, or `provider` on the resource, instead.
- #
- def self.provider_base(arg = nil)
- if arg
- Chef.log_deprecation("Resource.provider_base is deprecated and will be removed in Chef 13. Use provides on the provider, or provider on the resource, instead.")
- end
- @provider_base ||= arg || Chef::Provider
- end
-
- #
# The list of allowed actions for the resource.
#
# @param actions [Array<Symbol>] The list of actions to add to allowed_actions.
@@ -1169,61 +1100,73 @@ class Chef
end
#
- # The action class is an automatic `Provider` created to handle
- # actions declared by `action :x do ... end`.
+ # The action class is a `Chef::Provider` which is created at Resource
+ # class evaluation time when the Custom Resource is being constructed.
#
- # This class will be returned by `resource.provider` if `resource.provider`
- # is not set. `provider_for_action` will also use this instead of calling
- # out to `Chef::ProviderResolver`.
- #
- # If the user has not declared actions on this class or its superclasses
- # using `action :x do ... end`, then there is no need for this class and
- # `action_class` will be `nil`.
+ # This happens the first time the ruby parser hits an `action` or an
+ # `action_class` method, the presence of either indiates that this is
+ # going to be a Chef-12.5 custom resource. If we never see one of these
+ # directives then we are constructing an old-style Resource+Provider or
+ # LWRP or whatevs.
#
# If a block is passed, the action_class is always created and the block is
# run inside it.
#
- # @api private
- #
def self.action_class(&block)
- return @action_class if @action_class && !block
- # If the superclass needed one, then we need one as well.
- if block || (superclass.respond_to?(:action_class) && superclass.action_class)
- @action_class = declare_action_class(&block)
- end
+ @action_class ||= declare_action_class
+ @action_class.class_eval(&block) if block
@action_class
end
+ # Returns true or false based on if the resource is a custom resource. The
+ # top-level Chef::Resource is not a chef resource. This value is inherited.
+ #
+ # @return [Boolean] if the resource is a custom_resource
+ def self.custom_resource?
+ false
+ end
+
+ # This sets the resource to being a custom resource, and does so in a way
+ # that automatically inherits to all subclasses via defining a method on
+ # the class (class variables and class instance variables don't have the
+ # correct semantics here, this is a poor man's activesupport class_attribute)
+ #
+ # @api private
+ def self.is_custom_resource!
+ define_singleton_method :custom_resource? do
+ true
+ end
+ end
+
#
# Ensure the action class actually gets created. This is called
# when the user does `action :x do ... end`.
#
- # If a block is passed, it is run inside the action_class.
- #
# @api private
- def self.declare_action_class(&block)
- @action_class ||= begin
- if superclass.respond_to?(:action_class)
- base_provider = superclass.action_class
- end
- base_provider ||= Chef::Provider
-
- resource_class = self
- Class.new(base_provider) do
- include ActionClass
- self.resource_class = resource_class
- end
- end
- @action_class.class_eval(&block) if block
- @action_class
+ def self.declare_action_class
+ @action_class ||=
+ begin
+ is_custom_resource!
+ base_provider =
+ if superclass.custom_resource?
+ superclass.action_class
+ else
+ ActionClass
+ end
+
+ resource_class = self
+ Class.new(base_provider) do
+ self.resource_class = resource_class
+ end
+ end
end
#
# Internal Resource Interface (for Chef)
#
- FORBIDDEN_IVARS = [:@run_context, :@not_if, :@only_if, :@enclosing_provider]
- HIDDEN_IVARS = [:@allowed_actions, :@resource_name, :@source_line, :@run_context, :@name, :@not_if, :@only_if, :@elapsed_time, :@enclosing_provider]
+ FORBIDDEN_IVARS = [:@run_context, :@logger, :@not_if, :@only_if, :@enclosing_provider, :@description, :@introduced, :@examples, :@validation_message, :@deprecated]
+ HIDDEN_IVARS = [:@allowed_actions, :@resource_name, :@source_line, :@run_context, :@logger, :@name, :@not_if, :@only_if, :@elapsed_time, :@enclosing_provider, :@description, :@introduced, :@examples, :@validation_message, :@deprecated]
include Chef::Mixin::ConvertToClassName
extend Chef::Mixin::ConvertToClassName
@@ -1237,6 +1180,10 @@ class Chef
# and the resource collection.
attr_accessor :run_context
+ # @return [Mixlib::Log::Child] The logger for this resources. This is a child
+ # of the run context's logger, if one exists.
+ attr_reader :logger
+
# @return [String] The cookbook this resource was declared in.
attr_accessor :cookbook_name
@@ -1269,15 +1216,15 @@ class Chef
# resolve_resource_reference on each in turn, causing them to
# resolve lazy/forward references.
def resolve_notification_references
- run_context.before_notifications(self).each { |n|
+ run_context.before_notifications(self).each do |n|
n.resolve_resource_reference(run_context.resource_collection)
- }
- run_context.immediate_notifications(self).each { |n|
+ end
+ run_context.immediate_notifications(self).each do |n|
n.resolve_resource_reference(run_context.resource_collection)
- }
- run_context.delayed_notifications(self).each {|n|
+ end
+ run_context.delayed_notifications(self).each do |n|
n.resolve_resource_reference(run_context.resource_collection)
- }
+ end
end
# Helper for #notifies
@@ -1422,6 +1369,35 @@ class Chef
end
end
+ def self.description(description = "NOT_PASSED")
+ if description != "NOT_PASSED"
+ @description = description
+ end
+ @description
+ end
+
+ def self.introduced(introduced = "NOT_PASSED")
+ if introduced != "NOT_PASSED"
+ @introduced = introduced
+ end
+ @introduced
+ end
+
+ def self.examples(examples = "NOT_PASSED")
+ if examples != "NOT_PASSED"
+ @examples = examples
+ end
+ @examples
+ end
+
+ def self.deprecated(deprecated = "NOT_PASSED")
+ if deprecated != "NOT_PASSED"
+ @deprecated = true
+ @deprecated_message = deprecated
+ end
+ @deprecated
+ end
+
#
# The cookbook in which this Resource was defined (if any).
#
@@ -1448,24 +1424,6 @@ class Chef
provider
end
- # ??? TODO Seems unused. Delete?
- def noop(tf = nil)
- if !tf.nil?
- raise ArgumentError, "noop must be true or false!" unless tf == true || tf == false
- @noop = tf
- end
- @noop
- end
-
- # TODO Seems unused. Delete?
- def is(*args)
- if args.size == 1
- args.first
- else
- return *args
- end
- end
-
#
# Preface an exception message with generic Resource information.
#
@@ -1501,7 +1459,7 @@ class Chef
false
else
events.resource_skipped(self, action, conditional)
- Chef::Log.debug("Skipping #{self} due to #{conditional.description}")
+ logger.debug("Skipping #{self} due to #{conditional.description}")
true
end
end
@@ -1524,6 +1482,13 @@ class Chef
#
# Returns the class with the given resource_name.
#
+ # NOTE: Chef::Resource.resource_matching_short_name(:package) returns
+ # Chef::Resource::Package, while on rhel the API call
+ # Chef::Resource.resource_for_node(:package, node) will return
+ # Chef::Resource::YumPackage -- which is probably what you really
+ # want. This API should most likely be removed or changed to call
+ # resource_for_node.
+ #
# ==== Parameters
# short_name<Symbol>:: short_name of the resource (ie :directory)
#
@@ -1535,37 +1500,17 @@ class Chef
end
# @api private
- def self.register_deprecated_lwrp_class(resource_class, class_name)
- if Chef::Resource.const_defined?(class_name, false)
- Chef::Log.warn "#{class_name} already exists! Deprecation class overwrites #{resource_class}"
- Chef::Resource.send(:remove_const, class_name)
- end
-
- if !Chef::Config[:treat_deprecation_warnings_as_errors]
- Chef::Resource.const_set(class_name, resource_class)
- deprecated_constants[class_name.to_sym] = resource_class
- end
- end
-
- def self.deprecated_constants
- @deprecated_constants ||= {}
- end
-
- # @api private
def lookup_provider_constant(name, action = :nothing)
- begin
- self.class.provider_base.const_get(convert_to_class_name(name.to_s))
- rescue NameError => e
- if e.to_s =~ /#{Regexp.escape(self.class.provider_base.to_s)}/
- raise ArgumentError, "No provider found to match '#{name}'"
- else
- raise e
- end
- end
+ # XXX: "name" is probably a poor choice of name here, ideally this would be nil, but we need to
+ # fix resources so that nil or empty names work (also solving the apt_update "doesn't matter one bit"
+ # problem). WARNING: this string is not a public API and should not be referenced (e.g. in provides blocks)
+ # and may change at any time. If you've found this comment you're also probably very lost and should maybe
+ # consider using `declare_resource :whatever` instead of trying to set `provider :whatever` on a resource, or in some
+ # other way reconsider what you're trying to do, since you're likely trying to force a bad design that we
+ # can't/won't support.
+ self.class.resource_for_node(name, node).new("name", run_context).provider_for_action(action).class
end
- private
-
def self.remove_canonical_dsl
if @resource_name
remaining = Chef.resource_handler_map.delete_canonical(@resource_name, self)
diff --git a/lib/chef/resource/action_class.rb b/lib/chef/resource/action_class.rb
index 89b23499d0..1bb3decb75 100644
--- a/lib/chef/resource/action_class.rb
+++ b/lib/chef/resource/action_class.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# Copyright:: Copyright 2015-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,12 +16,13 @@
# limitations under the License.
#
+require "chef/provider"
require "chef/exceptions"
require "chef/dsl/recipe"
class Chef
class Resource
- module ActionClass
+ class ActionClass < Chef::Provider
include Chef::DSL::Recipe
def to_s
@@ -39,7 +40,7 @@ class Chef
# We clear desired state in the copy, because it is supposed to be actual state.
# We keep identity properties and non-desired-state, which are assumed to be
# "control" values like `recurse: true`
- current_resource.class.properties.each do |name, property|
+ current_resource.class.properties.each_value do |property|
if property.desired_state? && !property.identity? && !property.name_property?
property.reset(current_resource)
end
@@ -63,27 +64,26 @@ class Chef
@current_resource = current_resource
end
- def self.included(other)
- other.extend(ClassMethods)
- other.use_inline_resources
- other.include_resource_dsl true
+ # XXX: remove in Chef-14
+ def self.include_resource_dsl?
+ true
end
- module ClassMethods
+ class << self
#
# The Chef::Resource class this ActionClass was declared against.
#
# @return [Class] The Chef::Resource class this ActionClass was declared against.
#
attr_accessor :resource_class
+ end
- def to_s
- "#{resource_class} action provider"
- end
+ def self.to_s
+ "#{resource_class} action provider"
+ end
- def inspect
- to_s
- end
+ def self.inspect
+ to_s
end
end
end
diff --git a/lib/chef/resource/apt_package.rb b/lib/chef/resource/apt_package.rb
index 069fefcb2b..4738d0cbbe 100644
--- a/lib/chef/resource/apt_package.rb
+++ b/lib/chef/resource/apt_package.rb
@@ -17,15 +17,22 @@
#
require "chef/resource/package"
-require "chef/provider/package/apt"
class Chef
class Resource
class AptPackage < Chef::Resource::Package
resource_name :apt_package
- provides :package, os: "linux", platform_family: [ "debian" ]
+ provides :package, platform_family: "debian"
- property :default_release, String, desired_state: false
+ description "Use the apt_package resource to manage packages on Debian and Ubuntu platforms."
+
+ property :default_release, String,
+ description: "The default release. For example: stable.",
+ desired_state: false
+
+ property :overwrite_config_files, [TrueClass, FalseClass],
+ description: "Overwrite existing config files with those in the package if prompted by apt.",
+ default: false
end
end
diff --git a/lib/chef/resource/apt_preference.rb b/lib/chef/resource/apt_preference.rb
new file mode 100644
index 0000000000..0169fb73c2
--- /dev/null
+++ b/lib/chef/resource/apt_preference.rb
@@ -0,0 +1,51 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: 2016-2017, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ # @since 13.3
+ class AptPreference < Chef::Resource
+ resource_name :apt_preference
+ provides(:apt_preference) { true }
+
+ description "The apt_preference resource allows for the creation of APT preference files. Preference files are used to control which package versions and sources are prioritized during installation."
+ introduced "13.3"
+
+ property :package_name, String,
+ name_property: true,
+ description: "The name of the package.",
+ regex: [/^([a-z]|[A-Z]|[0-9]|_|-|\.|\*|\+)+$/]
+
+ property :glob, String,
+ description: "Pin by glob() expression or with regular expressions surrounded by /."
+
+ property :pin, String,
+ description: "The package version or repository to pin.",
+ required: true
+
+ property :pin_priority, [String, Integer],
+ description: "Sets the Pin-Priority for a package.",
+ required: true
+
+ default_action :add
+ allowed_actions :add, :remove
+ end
+ end
+end
diff --git a/lib/chef/resource/apt_repository.rb b/lib/chef/resource/apt_repository.rb
index 8b87371824..0ed38e61d3 100644
--- a/lib/chef/resource/apt_repository.rb
+++ b/lib/chef/resource/apt_repository.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: 2016-2017, Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,23 +22,62 @@ class Chef
class Resource
class AptRepository < Chef::Resource
resource_name :apt_repository
- provides :apt_repository
-
- property :repo_name, String, name_property: true
- property :uri, String
- property :distribution, [ String, nil, false ], default: lazy { node["lsb"]["codename"] }, nillable: true, coerce: proc { |x| x ? x : nil }
- property :components, Array, default: []
- property :arch, [String, nil, false], default: nil, nillable: true, coerce: proc { |x| x ? x : nil }
- property :trusted, [TrueClass, FalseClass], default: false
- # whether or not to add the repository as a source repo, too
- property :deb_src, [TrueClass, FalseClass], default: false
- property :keyserver, [String, nil, false], default: "keyserver.ubuntu.com", nillable: true, coerce: proc { |x| x ? x : nil }
- property :key, [String, nil, false], default: nil, nillable: true, coerce: proc { |x| x ? x : nil }
- property :key_proxy, [String, nil, false], default: nil, nillable: true, coerce: proc { |x| x ? x : nil }
-
- property :cookbook, [String, nil, false], default: nil, desired_state: false, nillable: true, coerce: proc { |x| x ? x : nil }
- property :cache_rebuild, [TrueClass, FalseClass], default: true, desired_state: false
- property :sensitive, [TrueClass, FalseClass], default: false, desired_state: false
+ provides(:apt_repository) { true }
+
+ description "Use the apt_repository resource to specify additional APT repositories. Adding a new repository will update the APT package cache immediately."
+ introduced "12.9"
+
+ # There's a pile of [ String, nil, FalseClass ] types in these properties.
+ # This goes back to Chef 12 where String didn't default to nil and we had to do
+ # it ourself, which required allowing that type as well. We've cleaned up the
+ # defaults, but since we allowed users to pass nil here we need to continue
+ # to allow that so don't refactor this however tempting it is
+ property :repo_name, String,
+ regex: [/^[^\/]+$/],
+ description: "The name of the repository to configure, if it differs from the name of the resource block. The value of this setting must not contain spaces.",
+ validation_message: "repo_name property cannot contain a forward slash '/'",
+ name_property: true
+
+ property :uri, String,
+ description: "The base of the Debian distribution."
+
+ property :distribution, [ String, nil, FalseClass ],
+ description: "Usually a distribution’s codename, such as trusty, xenial or bionic. Default value: the codename of the node’s distro.",
+ default: lazy { node["lsb"]["codename"] }
+
+ property :components, Array,
+ description: "Package groupings, such as ‘main’ and ‘stable’.",
+ default: lazy { [] }
+
+ property :arch, [String, nil, FalseClass],
+ description: "Constrain packages to a particular CPU architecture such as 'i386' or 'amd64'."
+
+ property :trusted, [TrueClass, FalseClass],
+ description: "Determines whether you should treat all packages from this repository as authenticated regardless of signature.",
+ default: false
+
+ property :deb_src, [TrueClass, FalseClass],
+ description: "Determines whether or not to add the repository as a source repo as well.",
+ default: false
+
+ property :keyserver, [String, nil, FalseClass],
+ description: "The GPG keyserver where the key for the repo should be retrieved.",
+ default: "keyserver.ubuntu.com"
+
+ property :key, [String, Array, nil, FalseClass],
+ description: "If a keyserver is provided, this is assumed to be the fingerprint; otherwise it can be either the URI of GPG key for the repo, or a cookbook_file.",
+ default: lazy { [] }, coerce: proc { |x| x ? Array(x) : x }
+
+ property :key_proxy, [String, nil, FalseClass],
+ description: "If set, a specified proxy is passed to GPG via http-proxy=."
+
+ property :cookbook, [String, nil, FalseClass],
+ description: "If key should be a cookbook_file, specify a cookbook where the key is located for files/default. Default value is nil, so it will use the cookbook where the resource is used.",
+ desired_state: false
+
+ property :cache_rebuild, [TrueClass, FalseClass],
+ description: "Determines whether to rebuild the APT package cache.",
+ default: true, desired_state: false
default_action :add
allowed_actions :add, :remove
diff --git a/lib/chef/resource/apt_update.rb b/lib/chef/resource/apt_update.rb
index df2033b063..04a9407813 100644
--- a/lib/chef/resource/apt_update.rb
+++ b/lib/chef/resource/apt_update.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) 2016-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,9 +22,16 @@ class Chef
class Resource
class AptUpdate < Chef::Resource
resource_name :apt_update
- provides :apt_update, os: "linux"
+ provides(:apt_update) { true }
- property :frequency, Integer, default: 86_400
+ description "Use the apt_update resource to manage APT repository updates on Debian and Ubuntu platforms."
+ introduced "12.7"
+
+ # allow bare apt_update with no name
+ property :name, String, default: ""
+ property :frequency, Integer,
+ description: "Determines how frequently (in seconds) APT repository updates are made. Use this property when the :periodic action is specified.",
+ default: 86_400
default_action :periodic
allowed_actions :update, :periodic
diff --git a/lib/chef/resource/bash.rb b/lib/chef/resource/bash.rb
index 1238eedc42..22a84f4134 100644
--- a/lib/chef/resource/bash.rb
+++ b/lib/chef/resource/bash.rb
@@ -22,6 +22,7 @@ require "chef/provider/script"
class Chef
class Resource
class Bash < Chef::Resource::Script
+ description "Use the bash resource to execute scripts using the Bash interpreter. This resource may also use any of the actions and properties that are available to the execute resource. Commands that are executed with this resource are (by their nature) not idempotent, as they are typically unique to the environment in which they are run. Use not_if and only_if to guard this resource for idempotence."
def initialize(name, run_context = nil)
super
diff --git a/lib/chef/resource/batch.rb b/lib/chef/resource/batch.rb
index 10e96839fb..e3bff8145c 100644
--- a/lib/chef/resource/batch.rb
+++ b/lib/chef/resource/batch.rb
@@ -21,8 +21,9 @@ require "chef/resource/windows_script"
class Chef
class Resource
class Batch < Chef::Resource::WindowsScript
+ provides :batch
- provides :batch, os: "windows"
+ description "Use the batch resource to execute a batch script using the cmd.exe interpreter on Windows. The batch resource creates and executes a temporary file (similar to how the script resource behaves), rather than running the command inline. Commands that are executed with this resource are (by their nature) not idempotent, as they are typically unique to the environment in which they are run. Use not_if and only_if to guard this resource for idempotence."
def initialize(name, run_context = nil)
super(name, run_context, nil, "cmd.exe")
diff --git a/lib/chef/resource/bff_package.rb b/lib/chef/resource/bff_package.rb
index b14591876a..8750cb0b2b 100644
--- a/lib/chef/resource/bff_package.rb
+++ b/lib/chef/resource/bff_package.rb
@@ -17,11 +17,15 @@
#
require "chef/resource/package"
-require "chef/provider/package/aix"
class Chef
class Resource
class BffPackage < Chef::Resource::Package
+ resource_name :bff_package
+ provides :bff_package
+
+ description "Use the bff_package resource to manage packages for the AIX platform using the installp utility. When a package is installed from a local file, it must be added to the node using the remote_file or cookbook_file resources."
+ introduced "12.0"
end
end
end
diff --git a/lib/chef/resource/breakpoint.rb b/lib/chef/resource/breakpoint.rb
index a5eed0da94..8af3edbe22 100644
--- a/lib/chef/resource/breakpoint.rb
+++ b/lib/chef/resource/breakpoint.rb
@@ -21,12 +21,27 @@ require "chef/resource"
class Chef
class Resource
class Breakpoint < Chef::Resource
+ provides :breakpoint
+ resource_name :breakpoint
+
+ description "Use the breakpoint resource to add breakpoints to recipes. Run the chef-shell in chef-client mode, and then use those breakpoints to debug recipes. Breakpoints are ignored by the chef-client during an actual chef-client run. That said, breakpoints are typically used to debug recipes only when running them in a non-production environment, after which they are removed from those recipes before the parent cookbook is uploaded to the Chef server."
+ introduced "12.0"
+
default_action :break
def initialize(action = "break", *args)
super(caller.first, *args)
end
+ action :break do
+ if defined?(Shell) && Shell.running?
+ with_run_context :parent do
+ run_context.resource_collection.iterator.pause
+ new_resource.updated_by_last_action(true)
+ run_context.resource_collection.iterator
+ end
+ end
+ end
end
end
end
diff --git a/lib/chef/resource/build_essential.rb b/lib/chef/resource/build_essential.rb
new file mode 100644
index 0000000000..7c549fb236
--- /dev/null
+++ b/lib/chef/resource/build_essential.rb
@@ -0,0 +1,142 @@
+#
+# Copyright:: 2008-2018, Chef Software, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class BuildEssential < Chef::Resource
+ resource_name :build_essential
+ provides(:build_essential) { true }
+
+ description "Use the build_essential resource to install packages required for compiling C software from source."
+ introduced "14.0"
+
+ # this allows us to use build_essential without setting a name
+ property :name, String, default: ""
+
+ property :compile_time, [TrueClass, FalseClass],
+ description: "Install build essential packages at compile time.",
+ default: false
+
+ action :install do
+
+ description "Install build essential packages"
+
+ case node["platform_family"]
+ when "debian"
+ declare_resource(:package, %w{ autoconf binutils-doc bison build-essential flex gettext ncurses-dev })
+ when "amazon", "fedora", "rhel"
+ declare_resource(:package, %w{ autoconf bison flex gcc gcc-c++ gettext kernel-devel make m4 ncurses-devel patch })
+
+ # Ensure GCC 4 is available on older pre-6 EL
+ declare_resource(:package, %w{ gcc44 gcc44-c++ }) if platform_family?("rhel") && node["platform_version"].to_i < 6
+ when "freebsd"
+ declare_resource(:package, "devel/gmake")
+ declare_resource(:package, "devel/autoconf")
+ declare_resource(:package, "devel/m4")
+ declare_resource(:package, "devel/gettext")
+ when "mac_os_x"
+ unless xcode_cli_installed?
+ # This script was graciously borrowed and modified from Tim Sutton's
+ # osx-vm-templates at https://github.com/timsutton/osx-vm-templates/blob/b001475df54a9808d3d56d06e71b8fa3001fff42/scripts/xcode-cli-tools.sh
+ declare_resource(:execute, "install XCode Command Line tools") do
+ command <<-EOH.gsub(/^ {14}/, "")
+ # create the placeholder file that's checked by CLI updates' .dist code
+ # in Apple's SUS catalog
+ touch /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress
+ # find the CLI Tools update. We tail here because sometimes there's 2 and newest is last
+ PROD=$(softwareupdate -l | grep "\*.*Command Line" | tail -n 1 | awk -F"*" '{print $2}' | sed -e 's/^ *//' | tr -d '\n')
+ # install it
+ softwareupdate -i "$PROD" --verbose
+ # Remove the placeholder to prevent perpetual appearance in the update utility
+ rm -f /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress
+ EOH
+ end
+ end
+ when "omnios"
+ declare_resource(:package, "developer/gcc48")
+ declare_resource(:package, "developer/object-file")
+ declare_resource(:package, "developer/linker")
+ declare_resource(:package, "developer/library/lint")
+ declare_resource(:package, "developer/build/gnu-make")
+ declare_resource(:package, "system/header")
+ declare_resource(:package, "system/library/math/header-math")
+
+ # Per OmniOS documentation, the gcc bin dir isn't in the default
+ # $PATH, so add it to the running process environment
+ # http://omnios.omniti.com/wiki.php/DevEnv
+ ENV["PATH"] = "#{ENV['PATH']}:/opt/gcc-4.7.2/bin"
+ when "solaris2"
+ declare_resource(:package, "autoconf")
+ declare_resource(:package, "automake")
+ declare_resource(:package, "bison")
+ declare_resource(:package, "gnu-coreutils")
+ declare_resource(:package, "flex")
+ declare_resource(:package, "gcc") do
+ # lock because we don't use 5 yet
+ version "4.8.2"
+ end
+ declare_resource(:package, "gcc-3")
+ declare_resource(:package, "gnu-grep")
+ declare_resource(:package, "gnu-make")
+ declare_resource(:package, "gnu-patch")
+ declare_resource(:package, "gnu-tar")
+ declare_resource(:package, "make")
+ declare_resource(:package, "pkg-config")
+ declare_resource(:package, "ucb")
+ when "smartos"
+ declare_resource(:package, "autoconf")
+ declare_resource(:package, "binutils")
+ declare_resource(:package, "build-essential")
+ declare_resource(:package, "gcc47")
+ declare_resource(:package, "gmake")
+ declare_resource(:package, "pkg-config")
+ when "suse"
+ declare_resource(:package, %w{ autoconf bison flex gcc gcc-c++ kernel-default-devel make m4 })
+ declare_resource(:package, %w{ gcc48 gcc48-c++ }) if node["platform_version"].to_i < 12
+ else
+ Chef::Log.warn <<-EOH
+ The build_essential resource does not currently support the '#{node['platform_family']}'
+ platform family. Skipping...
+ EOH
+ end
+ end
+
+ action_class do
+ #
+ # Determine if the XCode Command Line Tools are installed
+ #
+ # @return [true, false]
+ #
+ def xcode_cli_installed?
+ cmd = Mixlib::ShellOut.new("pkgutil --pkgs=com.apple.pkg.CLTools_Executables")
+ cmd.run_command
+ # pkgutil returns an error if the package isn't found aka not installed
+ cmd.error? ? false : true
+ end
+ end
+
+ # this resource forces itself to run at compile_time
+ def after_created
+ return unless compile_time
+ Array(action).each do |action|
+ run_action(action)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/cab_package.rb b/lib/chef/resource/cab_package.rb
new file mode 100644
index 0000000000..a90ddc6891
--- /dev/null
+++ b/lib/chef/resource/cab_package.rb
@@ -0,0 +1,45 @@
+#
+# Author:: Vasundhara Jagdale (<vasundhara.jagdale@msystechnologies.com>)
+# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource/package"
+require "chef/mixin/uris"
+
+class Chef
+ class Resource
+ class CabPackage < Chef::Resource::Package
+ include Chef::Mixin::Uris
+
+ resource_name :cab_package
+ provides :cab_package
+
+ description "Use the cab_package resource to install or remove Microsoft Windows cabinet (.cab) packages."
+ introduced "12.15"
+
+ allowed_actions :install, :remove
+
+ property :source, String,
+ description: "The local file path or URL for the CAB package.",
+ coerce: (proc do |s|
+ unless s.nil?
+ uri_scheme?(s) ? s : Chef::Util::PathHelper.canonical_path(s, false)
+ end
+ end),
+ default: lazy { |r| r.package_name }
+ end
+ end
+end
diff --git a/lib/chef/resource/chef_gem.rb b/lib/chef/resource/chef_gem.rb
index 4445bf0f89..3304690901 100644
--- a/lib/chef/resource/chef_gem.rb
+++ b/lib/chef/resource/chef_gem.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright 2012-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,6 +21,18 @@ require "chef/resource/gem_package"
class Chef
class Resource
+ # Use the chef_gem resource to install a gem only for the instance of Ruby that is dedicated to the chef-client.
+ # When a gem is installed from a local file, it must be added to the node using the remote_file or cookbook_file
+ # resources.
+ #
+ # The chef_gem resource works with all of the same properties and options as the gem_package resource, but does not
+ # accept the gem_binary property because it always uses the CurrentGemEnvironment under which the chef-client is
+ # running. In addition to performing actions similar to the gem_package resource, the chef_gem resource does the
+ # following:
+ # - Runs its actions immediately, before convergence, allowing a gem to be used in a recipe immediately after it is
+ # installed
+ # - Runs Gem.clear_paths after the action, ensuring that gem is aware of changes so that it can be required
+ # immediately after it is installed
class ChefGem < Chef::Resource::Package::GemPackage
resource_name :chef_gem
@@ -28,20 +40,13 @@ class Chef
callbacks: {
"The chef_gem resource is restricted to the current gem environment, use gem_package to install to other environments." => proc { |v| v == "#{RbConfig::CONFIG['bindir']}/gem" },
}
- property :compile_time, [ true, false, nil ], default: lazy { Chef::Config[:chef_gem_compile_time] }, desired_state: false
+ property :compile_time, [TrueClass, FalseClass], default: false, desired_state: false
+ # force the resource to compile time if the compile time property has been set
def after_created
- # Chef::Resource.run_action: Caveat: this skips Chef::Runner.run_action, where notifications are handled
- # Action could be an array of symbols, but probably won't (think install + enable for a package)
- if compile_time.nil?
- Chef.log_deprecation "#{self} chef_gem compile_time installation is deprecated"
- Chef.log_deprecation "#{self} Please set `compile_time false` on the resource to use the new behavior."
- Chef.log_deprecation "#{self} or set `compile_time true` on the resource if compile_time behavior is required."
- end
-
- if compile_time || compile_time.nil?
+ if compile_time
Array(action).each do |action|
- self.run_action(action)
+ run_action(action)
end
Gem.clear_paths
end
diff --git a/lib/chef/resource/chef_handler.rb b/lib/chef/resource/chef_handler.rb
new file mode 100644
index 0000000000..ceed235840
--- /dev/null
+++ b/lib/chef/resource/chef_handler.rb
@@ -0,0 +1,130 @@
+#
+# Author:: Seth Chisamore <schisamo@chef.io>
+# Copyright:: 2011-2018, Chef Software, Inc <legal@chef.io>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ class Resource
+ class ChefHandler < Chef::Resource
+ resource_name :chef_handler
+ provides(:chef_handler) { true }
+
+ description "Use the chef_handler resource to install or uninstall Chef reporting/exception handlers."
+ introduced "14.0"
+
+ property :class_name, String,
+ description: "The name of the handler class (can be module name-spaced).",
+ name_property: true
+
+ property :source, String,
+ description: "The full path to the handler file. Can also be a gem path if the handler ships as part of a Ruby gem."
+
+ property :arguments, [Array, Hash],
+ description: "Arguments to pass the handler's class initializer.",
+ default: lazy { [] }
+
+ property :type, Hash,
+ description: "The type of Chef Handler to register as, i.e. :report, :exception or both.",
+ default: { report: true, exception: true }
+
+ # supports means a different thing in chef-land so we renamed it but
+ # wanted to make sure we didn't break the world
+ alias_method :supports, :type
+
+ # This action needs to find an rb file that presumably contains the indicated class in it and the
+ # load that file. It then instantiates that class by name and registers it as a handler.
+ action :enable do
+ description "Enables the Chef handler for the current Chef run on the current node"
+
+ class_name = new_resource.class_name
+ new_resource.type.each do |type, enable|
+ next unless enable
+
+ unregister_handler(type, class_name)
+ end
+
+ handler = nil
+
+ require new_resource.source unless new_resource.source.nil?
+
+ _, klass = get_class(class_name)
+ handler = klass.send(:new, *collect_args(new_resource.arguments))
+
+ new_resource.type.each do |type, enable|
+ next unless enable
+ register_handler(type, handler)
+ end
+ end
+
+ action :disable do
+ description "Disables the Chef handler for the current Chef run on the current node"
+
+ new_resource.type.each_key do |type|
+ unregister_handler(type, new_resource.class_name)
+ end
+ end
+
+ action_class do
+ # Registers a handler in Chef::Config.
+ #
+ # @param handler_type [Symbol] such as :report or :exception.
+ # @param handler [Chef::Handler] handler to register.
+ def register_handler(handler_type, handler)
+ Chef::Log.info("Enabling #{handler.class.name} as a #{handler_type} handler.")
+ Chef::Config.send("#{handler_type}_handlers") << handler
+ end
+
+ # Removes all handlers that match the given class name in Chef::Config.
+ #
+ # @param handler_type [Symbol] such as :report or :exception.
+ # @param class_full_name [String] such as 'Chef::Handler::ErrorReport'.
+ def unregister_handler(handler_type, class_full_name)
+ Chef::Config.send("#{handler_type}_handlers").delete_if do |v|
+ # avoid a bit of log spam
+ if v.class.name == class_full_name
+ Chef::Log.info("Disabling #{class_full_name} as a #{handler_type} handler.")
+ true
+ end
+ end
+ end
+
+ # Walks down the namespace heirarchy to return the class object for the given class name.
+ # If the class is not available, NameError is thrown.
+ #
+ # @param class_full_name [String] full class name such as 'Chef::Handler::Foo' or 'MyHandler'.
+ # @return [Array] parent class and child class.
+ def get_class(class_full_name)
+ ancestors = class_full_name.split("::")
+ class_name = ancestors.pop
+
+ # We need to search the ancestors only for the first/uppermost namespace of the class, so we
+ # need to enable the #const_get inherit paramenter only when we are searching in Kernel scope
+ # (see COOK-4117).
+ parent = ancestors.inject(Kernel) { |scope, const_name| scope.const_get(const_name, scope === Kernel) }
+ child = parent.const_get(class_name, parent === Kernel)
+ [parent, child]
+ end
+
+ def collect_args(resource_args = [])
+ if resource_args.is_a? Array
+ resource_args
+ else
+ [resource_args]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/chocolatey_package.rb b/lib/chef/resource/chocolatey_package.rb
index 805d3a3121..5f63f4b5b5 100644
--- a/lib/chef/resource/chocolatey_package.rb
+++ b/lib/chef/resource/chocolatey_package.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,19 +21,29 @@ require "chef/resource/package"
class Chef
class Resource
class ChocolateyPackage < Chef::Resource::Package
+ resource_name :chocolatey_package
+ provides :chocolatey_package
- provides :chocolatey_package, os: "windows"
+ description "Use the chocolatey_package resource to manage packages using Chocolatey on the Microsoft Windows platform."
+ introduced "12.7"
- allowed_actions :install, :upgrade, :remove, :uninstall, :purge, :reconfig
+ allowed_actions :install, :upgrade, :remove, :purge, :reconfig
- def initialize(name, run_context = nil)
- super
- @resource_name = :chocolatey_package
- end
+ # windows can't take Array options yet
+ property :options, String,
+ description: "One (or more) additional options that are passed to the command."
- property :package_name, [String, Array], coerce: proc { |x| [x].flatten }
+ property :package_name, [String, Array],
+ description: "The name of the package. Default value: the name of the resource block See “Syntax” section above for more information.",
+ coerce: proc { |x| [x].flatten }
- property :version, [String, Array], coerce: proc { |x| [x].flatten }
+ property :version, [String, Array],
+ description: "The version of a package to be installed or upgraded.",
+ coerce: proc { |x| [x].flatten }
+
+ property :returns, [Integer, Array],
+ description: "The exit code(s) returned a chocolatey package that indicate success.",
+ default: [ 0 ], desired_state: false
end
end
end
diff --git a/lib/chef/resource/conditional.rb b/lib/chef/resource/conditional.rb
index cdb9f13c45..452718cae8 100644
--- a/lib/chef/resource/conditional.rb
+++ b/lib/chef/resource/conditional.rb
@@ -103,7 +103,15 @@ class Chef
end
def evaluate_block
- @block.call
+ @block.call.tap do |rv|
+ if rv.is_a?(String) && !rv.empty?
+ # This is probably a mistake:
+ # not_if { "command" }
+ sanitized_rv = @parent_resource.sensitive ? "a string" : rv.inspect
+ Chef::Log.warn("#{@positivity} block for #{@parent_resource} returned #{sanitized_rv}, did you mean to run a command?" +
+ (@parent_resource.sensitive ? "" : " If so use '#{@positivity} #{sanitized_rv}' in your code."))
+ end
+ end
end
def short_description
diff --git a/lib/chef/resource/cookbook_file.rb b/lib/chef/resource/cookbook_file.rb
index 785cf693be..0caa9d0553 100644
--- a/lib/chef/resource/cookbook_file.rb
+++ b/lib/chef/resource/cookbook_file.rb
@@ -27,23 +27,18 @@ class Chef
class CookbookFile < Chef::Resource::File
include Chef::Mixin::Securable
- default_action :create
+ resource_name :cookbook_file
- def initialize(name, run_context = nil)
- super
- @provider = Chef::Provider::CookbookFile
- @source = ::File.basename(name)
- @cookbook = nil
- end
+ description "Use the cookbook_file resource to transfer files from a sub-directory of COOKBOOK_NAME/files/ to a specified path located on a host that is running the chef-client. The file is selected according to file specificity, which allows different source files to be used based on the hostname, host platform (operating system, distro, or as appropriate), or platform version. Files that are located in the COOKBOOK_NAME/files/default sub-directory may be used on any platform.\n\nDuring a chef-client run, the checksum for each local file is calculated and then compared against the checksum for the same file as it currently exists in the cookbook on the Chef server. A file is not transferred when the checksums match. Only files that require an update are transferred from the Chef server to a node."
- def source(source_filename = nil)
- set_or_return(:source, source_filename, :kind_of => [ String, Array ])
- end
+ property :source, [ String, Array ],
+ description: "The name of the file in COOKBOOK_NAME/files/default or the path to a file located in COOKBOOK_NAME/files. The path must include the file name and its extension. This can be used to distribute specific files depending upon the platform used.",
+ default: lazy { ::File.basename(name) }
- def cookbook(cookbook_name = nil)
- set_or_return(:cookbook, cookbook_name, :kind_of => String)
- end
+ property :cookbook, String,
+ description: "The cookbook in which a file is located (if it is not located in the current cookbook)."
+ default_action :create
end
end
end
diff --git a/lib/chef/resource/cron.rb b/lib/chef/resource/cron.rb
index a76d454bf0..ea6d692709 100644
--- a/lib/chef/resource/cron.rb
+++ b/lib/chef/resource/cron.rb
@@ -18,12 +18,15 @@
#
require "chef/resource"
+require "chef/provider/cron" # do not remove. we actually need this below
class Chef
class Resource
class Cron < Chef::Resource
+ resource_name :cron
+ provides :cron
- identity_attr :command
+ description "Use the cron resource to manage cron entries for time-based job scheduling. Properties for a schedule will default to * if not provided. The cron resource requires access to a crontab program, typically cron."
state_attrs :minute, :hour, :day, :month, :weekday, :user
@@ -37,14 +40,6 @@ class Chef
@day = "*"
@month = "*"
@weekday = "*"
- @command = nil
- @user = "root"
- @mailto = nil
- @path = nil
- @shell = nil
- @home = nil
- @time = nil
- @environment = {}
end
def minute(arg = nil)
@@ -139,73 +134,17 @@ class Chef
)
end
- def time(arg = nil)
- set_or_return(
- :time,
- arg,
- :equal_to => Chef::Provider::Cron::SPECIAL_TIME_VALUES
- )
- end
-
- def mailto(arg = nil)
- set_or_return(
- :mailto,
- arg,
- :kind_of => String
- )
- end
-
- def path(arg = nil)
- set_or_return(
- :path,
- arg,
- :kind_of => String
- )
- end
-
- def home(arg = nil)
- set_or_return(
- :home,
- arg,
- :kind_of => String
- )
- end
-
- def shell(arg = nil)
- set_or_return(
- :shell,
- arg,
- :kind_of => String
- )
- end
-
- def command(arg = nil)
- set_or_return(
- :command,
- arg,
- :kind_of => String
- )
- end
-
- def user(arg = nil)
- set_or_return(
- :user,
- arg,
- :kind_of => String
- )
- end
-
- def environment(arg = nil)
- set_or_return(
- :environment,
- arg,
- :kind_of => Hash
- )
- end
+ property :time, Symbol, equal_to: Chef::Provider::Cron::SPECIAL_TIME_VALUES
+ property :mailto, String
+ property :path, String
+ property :home, String
+ property :shell, String
+ property :command, String, identity: true
+ property :user, String, default: "root"
+ property :environment, Hash, default: lazy { Hash.new }
private
- # On Ruby 1.8, Kernel#Integer will happily do this for you. On 1.9, no.
def integerize(integerish)
Integer(integerish)
rescue TypeError
diff --git a/lib/chef/resource/csh.rb b/lib/chef/resource/csh.rb
index 4e7c22b660..0938a19cb1 100644
--- a/lib/chef/resource/csh.rb
+++ b/lib/chef/resource/csh.rb
@@ -22,6 +22,12 @@ require "chef/provider/script"
class Chef
class Resource
class Csh < Chef::Resource::Script
+ description "Use the csh resource to execute scripts using the csh interpreter."\
+ " This resource may also use any of the actions and properties that are"\
+ " available to the execute resource. Commands that are executed with this"\
+ " resource are (by their nature) not idempotent, as they are typically"\
+ " unique to the environment in which they are run. Use not_if and only_if"\
+ " to guard this resource for idempotence."
def initialize(name, run_context = nil)
super
diff --git a/lib/chef/resource/deploy.rb b/lib/chef/resource/deploy.rb
deleted file mode 100644
index b8e6a26f97..0000000000
--- a/lib/chef/resource/deploy.rb
+++ /dev/null
@@ -1,443 +0,0 @@
-#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# EX:
-# deploy "/my/deploy/dir" do
-# repo "git@github.com/whoami/project"
-# revision "abc123" # or "HEAD" or "TAG_for_1.0" or (subversion) "1234"
-# user "deploy_ninja"
-# enable_submodules true
-# migrate true
-# migration_command "rake db:migrate"
-# environment "RAILS_ENV" => "production", "OTHER_ENV" => "foo"
-# shallow_clone true
-# depth 1
-# action :deploy # or :rollback
-# restart_command "touch tmp/restart.txt"
-# git_ssh_wrapper "wrap-ssh4git.sh"
-# scm_provider Chef::Provider::Git # is the default, for svn: Chef::Provider::Subversion
-# svn_username "whoami"
-# svn_password "supersecret"
-# end
-
-require "chef/resource/scm"
-
-class Chef
- class Resource
-
- # Deploy: Deploy apps from a source control repository.
- #
- # Callbacks:
- # Callbacks can be a block or a string. If given a block, the code
- # is evaluated as an embedded recipe, and run at the specified
- # point in the deploy process. If given a string, the string is taken as
- # a path to a callback file/recipe. Paths are evaluated relative to the
- # release directory. Callback files can contain chef code (resources, etc.)
- #
- class Deploy < Chef::Resource
-
- identity_attr :repository
-
- state_attrs :deploy_to, :revision
-
- default_action :deploy
- allowed_actions :force_deploy, :deploy, :rollback
-
- def initialize(name, run_context = nil)
- super
- @deploy_to = name
- @environment = nil
- @repository_cache = "cached-copy"
- @copy_exclude = []
- @purge_before_symlink = %w{log tmp/pids public/system}
- @create_dirs_before_symlink = %w{tmp public config}
- @symlink_before_migrate = { "config/database.yml" => "config/database.yml" }
- @symlinks = { "system" => "public/system", "pids" => "tmp/pids", "log" => "log" }
- @revision = "HEAD"
- @migrate = false
- @rollback_on_error = false
- @remote = "origin"
- @enable_submodules = false
- @shallow_clone = false
- @depth = nil
- @scm_provider = Chef::Provider::Git
- @svn_force_export = false
- @additional_remotes = Hash[]
- @keep_releases = 5
- @enable_checkout = true
- @checkout_branch = "deploy"
- end
-
- # where the checked out/cloned code goes
- def destination
- @destination ||= shared_path + "/#{@repository_cache}"
- end
-
- # where shared stuff goes, i.e., logs, tmp, etc. goes here
- def shared_path
- @shared_path ||= @deploy_to + "/shared"
- end
-
- # where the deployed version of your code goes
- def current_path
- @current_path ||= @deploy_to + "/current"
- end
-
- def depth(arg = @shallow_clone ? 5 : nil)
- set_or_return(
- :depth,
- arg,
- :kind_of => [ Integer ]
- )
- end
-
- # note: deploy_to is your application "meta-root."
- def deploy_to(arg = nil)
- set_or_return(
- :deploy_to,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def repo(arg = nil)
- set_or_return(
- :repo,
- arg,
- :kind_of => [ String ]
- )
- end
- alias :repository :repo
-
- def remote(arg = nil)
- set_or_return(
- :remote,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def role(arg = nil)
- set_or_return(
- :role,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def restart_command(arg = nil, &block)
- arg ||= block
- set_or_return(
- :restart_command,
- arg,
- :kind_of => [ String, Proc ]
- )
- end
- alias :restart :restart_command
-
- def migrate(arg = nil)
- set_or_return(
- :migrate,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def migration_command(arg = nil)
- set_or_return(
- :migration_command,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def rollback_on_error(arg = nil)
- set_or_return(
- :rollback_on_error,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def user(arg = nil)
- set_or_return(
- :user,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def group(arg = nil)
- set_or_return(
- :group,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def enable_submodules(arg = nil)
- set_or_return(
- :enable_submodules,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def shallow_clone(arg = nil)
- set_or_return(
- :shallow_clone,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def repository_cache(arg = nil)
- set_or_return(
- :repository_cache,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def copy_exclude(arg = nil)
- set_or_return(
- :copy_exclude,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def revision(arg = nil)
- set_or_return(
- :revision,
- arg,
- :kind_of => [ String ]
- )
- end
- alias :branch :revision
-
- def git_ssh_wrapper(arg = nil)
- set_or_return(
- :git_ssh_wrapper,
- arg,
- :kind_of => [ String ]
- )
- end
- alias :ssh_wrapper :git_ssh_wrapper
-
- def svn_username(arg = nil)
- set_or_return(
- :svn_username,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def svn_password(arg = nil)
- set_or_return(
- :svn_password,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def svn_arguments(arg = nil)
- set_or_return(
- :svn_arguments,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def svn_info_args(arg = nil)
- set_or_return(
- :svn_arguments,
- arg,
- :kind_of => [ String ])
- end
-
- def scm_provider(arg = nil)
- klass = if arg.kind_of?(String) || arg.kind_of?(Symbol)
- lookup_provider_constant(arg)
- else
- arg
- end
- set_or_return(
- :scm_provider,
- klass,
- :kind_of => [ Class ]
- )
- end
-
- # This is to support "provider :revision" without deprecation warnings.
- # Do NOT copy this.
- def self.provider_base
- Chef::Provider::Deploy
- end
-
- def svn_force_export(arg = nil)
- set_or_return(
- :svn_force_export,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def environment(arg = nil)
- if arg.is_a?(String)
- Chef::Log.debug "Setting RAILS_ENV, RACK_ENV, and MERB_ENV to `#{arg}'"
- Chef::Log.warn "[DEPRECATED] please modify your deploy recipe or attributes to set the environment using a hash"
- arg = { "RAILS_ENV" => arg, "MERB_ENV" => arg, "RACK_ENV" => arg }
- end
- set_or_return(
- :environment,
- arg,
- :kind_of => [ Hash ]
- )
- end
-
- # The number of old release directories to keep around after cleanup
- def keep_releases(arg = nil)
- [set_or_return(
- :keep_releases,
- arg,
- :kind_of => [ Integer ]), 1].max
- end
-
- # An array of paths, relative to your app's root, to be purged from a
- # SCM clone/checkout before symlinking. Use this to get rid of files and
- # directories you want to be shared between releases.
- # Default: ["log", "tmp/pids", "public/system"]
- def purge_before_symlink(arg = nil)
- set_or_return(
- :purge_before_symlink,
- arg,
- :kind_of => Array
- )
- end
-
- # An array of paths, relative to your app's root, where you expect dirs to
- # exist before symlinking. This runs after #purge_before_symlink, so you
- # can use this to recreate dirs that you had previously purged.
- # For example, if you plan to use a shared directory for pids, and you
- # want it to be located in $APP_ROOT/tmp/pids, you could purge tmp,
- # then specify tmp here so that the tmp directory will exist when you
- # symlink the pids directory in to the current release.
- # Default: ["tmp", "public", "config"]
- def create_dirs_before_symlink(arg = nil)
- set_or_return(
- :create_dirs_before_symlink,
- arg,
- :kind_of => Array
- )
- end
-
- # A Hash of shared/dir/path => release/dir/path. This attribute determines
- # which files and dirs in the shared directory get symlinked to the current
- # release directory, and where they go. If you have a directory
- # $shared/pids that you would like to symlink as $current_release/tmp/pids
- # you specify it as "pids" => "tmp/pids"
- # Default {"system" => "public/system", "pids" => "tmp/pids", "log" => "log"}
- def symlinks(arg = nil)
- set_or_return(
- :symlinks,
- arg,
- :kind_of => Hash
- )
- end
-
- # A Hash of shared/dir/path => release/dir/path. This attribute determines
- # which files in the shared directory get symlinked to the current release
- # directory and where they go. Unlike map_shared_files, these are symlinked
- # *before* any migration is run.
- # For a rails/merb app, this is used to link in a known good database.yml
- # (with the production db password) before running migrate.
- # Default {"config/database.yml" => "config/database.yml"}
- def symlink_before_migrate(arg = nil)
- set_or_return(
- :symlink_before_migrate,
- arg,
- :kind_of => Hash
- )
- end
-
- # Callback fires before migration is run.
- def before_migrate(arg = nil, &block)
- arg ||= block
- set_or_return(:before_migrate, arg, :kind_of => [Proc, String])
- end
-
- # Callback fires before symlinking
- def before_symlink(arg = nil, &block)
- arg ||= block
- set_or_return(:before_symlink, arg, :kind_of => [Proc, String])
- end
-
- # Callback fires before restart
- def before_restart(arg = nil, &block)
- arg ||= block
- set_or_return(:before_restart, arg, :kind_of => [Proc, String])
- end
-
- # Callback fires after restart
- def after_restart(arg = nil, &block)
- arg ||= block
- set_or_return(:after_restart, arg, :kind_of => [Proc, String])
- end
-
- def additional_remotes(arg = nil)
- set_or_return(
- :additional_remotes,
- arg,
- :kind_of => Hash
- )
- end
-
- def enable_checkout(arg = nil)
- set_or_return(
- :enable_checkout,
- arg,
- :kind_of => [TrueClass, FalseClass]
- )
- end
-
- def checkout_branch(arg = nil)
- set_or_return(
- :checkout_branch,
- arg,
- :kind_of => String
- )
- end
-
- # FIXME The Deploy resource may be passed to an SCM provider as its
- # resource. The SCM provider knows that SCM resources can specify a
- # timeout for SCM operations. The deploy resource must therefore support
- # a timeout method, but the timeout it describes is for SCM operations,
- # not the overall deployment. This is potentially confusing.
- def timeout(arg = nil)
- set_or_return(
- :timeout,
- arg,
- :kind_of => Integer
- )
- end
-
- end
- end
-end
diff --git a/lib/chef/resource/directory.rb b/lib/chef/resource/directory.rb
index faad659668..1f18a4b951 100644
--- a/lib/chef/resource/directory.rb
+++ b/lib/chef/resource/directory.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,14 +19,19 @@
#
require "chef/resource"
-require "chef/provider/directory"
require "chef/mixin/securable"
class Chef
class Resource
class Directory < Chef::Resource
+ resource_name :directory
- identity_attr :path
+ description "Use the directory resource to manage a directory, which is a hierarchy"\
+ " of folders that comprises all of the information stored on a computer."\
+ " The root directory is the top-level, under which the rest of the directory"\
+ " is organized. The directory resource uses the name property to specify the"\
+ " path to a location in a directory. Typically, permission to access that"\
+ " location in the directory is required."
state_attrs :group, :mode, :owner
@@ -35,28 +40,8 @@ class Chef
default_action :create
allowed_actions :create, :delete
- def initialize(name, run_context = nil)
- super
- @path = name
- @recursive = false
- end
-
- def recursive(arg = nil)
- set_or_return(
- :recursive,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def path(arg = nil)
- set_or_return(
- :path,
- arg,
- :kind_of => String
- )
- end
-
+ property :path, String, name_property: true, identity: true
+ property :recursive, [ TrueClass, FalseClass ], default: false
end
end
end
diff --git a/lib/chef/resource/dmg_package.rb b/lib/chef/resource/dmg_package.rb
new file mode 100644
index 0000000000..af0bc341b4
--- /dev/null
+++ b/lib/chef/resource/dmg_package.rb
@@ -0,0 +1,162 @@
+#
+# Author:: Joshua Timberman (<jtimberman@chef.io>)
+# Copyright:: 2011-2018, Chef Software, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class DmgPackage < Chef::Resource
+ resource_name :dmg_package
+ provides(:dmg_package) { true }
+
+ description "Use the dmg_package resource to install a dmg 'package'. The resource will retrieve the"\
+ " dmg file from a remote URL, mount it using OS X's hdidutil, copy the application (.app directory)"\
+ " to the specified destination (/Applications), and detach the image using hdiutil. The dmg file"\
+ " will be stored in the Chef::Config[:file_cache_path]."
+ introduced "14.0"
+
+ property :app, String,
+ description: "The name of the application used by default for the /Volumes directory and the .app directory copied to /Applications.",
+ name_property: true
+
+ property :source, String,
+ description: "The remote URL for the dmg to download if specified."
+
+ property :file, String,
+ description: "The local dmg full file path."
+
+ property :owner, String,
+ description: "The owner that should own the package installation."
+
+ property :destination, String,
+ description: "The directory to copy the .app into.",
+ default: "/Applications"
+
+ property :checksum, String,
+ description: "The sha256 checksum of the dmg to download"
+
+ property :volumes_dir, String,
+ description: "The Directory under /Volumes where the dmg is mounted as not all dmgs are mounted into a /Volumes location matching the name of the dmg."
+
+ property :dmg_name, String,
+ description: "The name of the dmg if it is not the same as app, or if the name has spaces."
+
+ property :type, String,
+ description: "The type of package.",
+ equal_to: %w{app pkg mpkg},
+ default: "app"
+
+ property :installed, [TrueClass, FalseClass],
+ default: false, desired_state: false
+
+ property :package_id, String,
+ description: "The package id registered with pkgutil when a pkg or mpkg is installed"
+
+ property :dmg_passphrase, String,
+ description: "Specify a passphrase to use to unencrypt the dmg while mounting."
+
+ property :accept_eula, [TrueClass, FalseClass],
+ description: "Specify whether to accept the EULA. Certain dmgs require acceptance of EULA before mounting.",
+ default: false
+
+ property :headers, [Hash, nil],
+ description: "Allows custom HTTP headers (like cookies) to be set on the remote_file resource.",
+ default: nil
+
+ property :allow_untrusted, [TrueClass, FalseClass],
+ description: "Allows packages with untrusted certs to be installed.",
+ default: false
+
+ load_current_value do |new_resource|
+ if ::File.directory?("#{new_resource.destination}/#{new_resource.app}.app")
+ Chef::Log.info "Already installed; to upgrade, remove \"#{new_resource.destination}/#{new_resource.app}.app\""
+ installed true
+ elsif shell_out("pkgutil --pkgs='#{new_resource.package_id}'").exitstatus == 0
+ Chef::Log.info "Already installed; to upgrade, try \"sudo pkgutil --forget '#{new_resource.package_id}'\""
+ installed true
+ else
+ installed false
+ end
+ end
+
+ action :install do
+ description "Installs the application."
+
+ unless current_resource.installed
+
+ volumes_dir = new_resource.volumes_dir ? new_resource.volumes_dir : new_resource.app
+ dmg_name = new_resource.dmg_name ? new_resource.dmg_name : new_resource.app
+
+ if new_resource.source
+ declare_resource(:remote_file, "#{dmg_file} - #{new_resource.name}") do
+ path dmg_file
+ source new_resource.source
+ headers new_resource.headers if new_resource.headers
+ checksum new_resource.checksum if new_resource.checksum
+ end
+ end
+
+ passphrase_cmd = new_resource.dmg_passphrase ? "-passphrase #{new_resource.dmg_passphrase}" : ""
+ ruby_block "attach #{dmg_file}" do
+ block do
+ cmd = shell_out("hdiutil imageinfo #{passphrase_cmd} '#{dmg_file}' | grep -q 'Software License Agreement: true'")
+ software_license_agreement = cmd.exitstatus == 0
+ raise "Requires EULA Acceptance; add 'accept_eula true' to package resource" if software_license_agreement && !new_resource.accept_eula
+ accept_eula_cmd = new_resource.accept_eula ? "echo Y | PAGER=true" : ""
+ shell_out!("#{accept_eula_cmd} hdiutil attach #{passphrase_cmd} '#{dmg_file}' -mountpoint '/Volumes/#{volumes_dir}' -quiet")
+ end
+ not_if "hdiutil info #{passphrase_cmd} | grep -q 'image-path.*#{dmg_file}'"
+ end
+
+ case new_resource.type
+ when "app"
+ declare_resource(:execute, "rsync --force --recursive --links --perms --executability --owner --group --times '/Volumes/#{volumes_dir}/#{new_resource.app}.app' '#{new_resource.destination}'") do
+ user new_resource.owner if new_resource.owner
+ end
+
+ declare_resource(:file, "#{new_resource.destination}/#{new_resource.app}.app/Contents/MacOS/#{new_resource.app}") do
+ mode "755"
+ ignore_failure true
+ end
+ when "mpkg", "pkg"
+ install_cmd = "installation_file=$(ls '/Volumes/#{volumes_dir}' | grep '.#{new_resource.type}$') && sudo installer -pkg \"/Volumes/#{volumes_dir}/$installation_file\" -target /"
+ install_cmd += " -allowUntrusted" if new_resource.allow_untrusted
+
+ declare_resource(:execute, install_cmd) do
+ # Prevent cfprefsd from holding up hdiutil detach for certain disk images
+ environment("__CFPREFERENCES_AVOID_DAEMON" => "1")
+ end
+ end
+
+ declare_resource(:execute, "hdiutil detach '/Volumes/#{volumes_dir}' || hdiutil detach '/Volumes/#{volumes_dir}' -force")
+ end
+ end
+
+ action_class do
+ def dmg_file
+ @dmg_file ||= begin
+ if new_resource.file.nil?
+ "#{Chef::Config[:file_cache_path]}/#{new_resource.dmg_name}.dmg"
+ else
+ new_resource.file
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/dnf_package.rb b/lib/chef/resource/dnf_package.rb
new file mode 100644
index 0000000000..13bdc5065e
--- /dev/null
+++ b/lib/chef/resource/dnf_package.rb
@@ -0,0 +1,82 @@
+#
+# Copyright:: Copyright 2016-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource/package"
+require "chef/mixin/which"
+require "chef/mixin/shell_out"
+
+class Chef
+ class Resource
+ class DnfPackage < Chef::Resource::Package
+ extend Chef::Mixin::Which
+ extend Chef::Mixin::ShellOut
+
+ resource_name :dnf_package
+
+ # all rhel variants >= 8 will use DNF
+ provides :package, platform_family: "rhel", platform_version: ">= 8"
+
+ # fedora >= 22 uses DNF
+ provides :package, platform: "fedora", platform_version: ">= 22"
+
+ # amazon will eventually use DNF
+ provides :package, platform: "amazon" do
+ which("dnf")
+ end
+
+ provides :dnf_package
+
+ description "Use the dnf_package resource to install, upgrade, and remove packages"\
+ " with DNF for Fedora platforms. The dnf_package resource is able to"\
+ " resolve provides data for packages much like DNF can do when it is"\
+ " run from the command line. This allows a variety of options for"\
+ " installing packages, like minimum versions, virtual provides,"\
+ " and library names."
+ introduced "12.18"
+
+ allowed_actions :install, :upgrade, :remove, :purge, :reconfig, :lock, :unlock, :flush_cache
+
+ # Install a specific arch
+ property :arch, [String, Array], coerce: proc { |x| [x].flatten }
+
+ # Flush the in-memory available/installed cache, this does not flush the dnf caches on disk
+ property :flush_cache,
+ Hash,
+ default: { before: false, after: false },
+ coerce: proc { |v|
+ if v.is_a?(Hash)
+ v
+ elsif v.is_a?(Array)
+ v.each_with_object({}) { |arg, obj| obj[arg] = true }
+ elsif v.is_a?(TrueClass) || v.is_a?(FalseClass)
+ { before: v, after: v }
+ elsif v == :before
+ { before: true, after: false }
+ elsif v == :after
+ { after: true, before: false }
+ end
+ }
+
+ def allow_downgrade(arg = nil)
+ if !arg.nil?
+ Chef.deprecated(:dnf_package_allow_downgrade, "the allow_downgrade property on the dnf_package provider is not used, DNF supports downgrades by default.")
+ end
+ false
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/dpkg_package.rb b/lib/chef/resource/dpkg_package.rb
index 9ff3239884..bdf761888e 100644
--- a/lib/chef/resource/dpkg_package.rb
+++ b/lib/chef/resource/dpkg_package.rb
@@ -22,7 +22,11 @@ class Chef
class Resource
class DpkgPackage < Chef::Resource::Package
resource_name :dpkg_package
- provides :dpkg_package, os: "linux"
+ provides :dpkg_package
+
+ description "Use the dpkg_package resource to manage packages for the dpkg platform."\
+ " When a package is installed from a local file, it must be added to the"\
+ " node using the remote_file or cookbook_file resources."
property :source, [ String, Array, nil ]
end
diff --git a/lib/chef/resource/dsc_resource.rb b/lib/chef/resource/dsc_resource.rb
index 58594cce7b..9c703b81cc 100644
--- a/lib/chef/resource/dsc_resource.rb
+++ b/lib/chef/resource/dsc_resource.rb
@@ -20,7 +20,14 @@ require "chef/dsl/powershell"
class Chef
class Resource
class DscResource < Chef::Resource
- provides :dsc_resource, os: "windows"
+ resource_name :dsc_resource
+ provides :dsc_resource
+
+ description "The dsc_resource resource allows any DSC resource to be used in a"\
+ " Chef recipe, as well as any custom resources that have been added"\
+ " to your Windows PowerShell environment. Microsoft frequently adds"\
+ " new resources to the DSC resource collection."
+ introduced "12.2"
# This class will check if the object responds to
# to_text. If it does, it will call that as opposed
@@ -29,7 +36,7 @@ class Chef
# to dump the actual ivars
class ToTextHash < Hash
def to_text
- descriptions = self.map do |(property, obj)|
+ descriptions = map do |(property, obj)|
obj_text = if obj.respond_to?(:to_text)
obj.to_text
else
@@ -49,7 +56,6 @@ class Chef
super
@properties = ToTextHash.new
@resource = nil
- @reboot_action = :nothing
end
def resource(value = nil)
@@ -68,6 +74,8 @@ class Chef
end
end
+ property :module_version, String
+
def property(property_name, value = nil)
if not property_name.is_a?(Symbol)
raise TypeError, "A property name of type Symbol must be specified, '#{property_name}' of type #{property_name.class} was given"
@@ -91,21 +99,9 @@ class Chef
# If the set method of the DSC resource indicate that a reboot
# is necessary, reboot_action provides the mechanism for a reboot to
# be requested.
- def reboot_action(value = nil)
- if value
- @reboot_action = value
- else
- @reboot_action
- end
- end
+ property :reboot_action, Symbol, default: :nothing, equal_to: [:nothing, :reboot_now, :request_reboot]
- def timeout(arg = nil)
- set_or_return(
- :timeout,
- arg,
- :kind_of => [ Integer ]
- )
- end
+ property :timeout, Integer
private
diff --git a/lib/chef/resource/dsc_script.rb b/lib/chef/resource/dsc_script.rb
index 7da29a651a..d10113be56 100644
--- a/lib/chef/resource/dsc_script.rb
+++ b/lib/chef/resource/dsc_script.rb
@@ -24,7 +24,17 @@ class Chef
class DscScript < Chef::Resource
include Chef::DSL::Powershell
- provides :dsc_script, os: "windows"
+ resource_name :dsc_script
+ provides :dsc_script
+
+ description "Many DSC resources are comparable to built-in Chef resources. For"\
+ " example, both DSC and Chef have file, package, and service resources."\
+ " The dsc_script resource is most useful for those DSC resources that"\
+ " do not have a direct comparison to a resource in Chef, such as the"\
+ " Archive resource, a custom DSC resource, an existing DSC script"\
+ " that performs an important task, and so on. Use the dsc_script resource"\
+ " to embed the code that defines a DSC configuration directly within a"\
+ " Chef recipe."
default_action :run
@@ -104,37 +114,10 @@ class Chef
end
end
- def flags(arg = nil)
- set_or_return(
- :flags,
- arg,
- :kind_of => [ Hash ]
- )
- end
-
- def cwd(arg = nil)
- set_or_return(
- :cwd,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def environment(arg = nil)
- set_or_return(
- :environment,
- arg,
- :kind_of => [ Hash ]
- )
- end
-
- def timeout(arg = nil)
- set_or_return(
- :timeout,
- arg,
- :kind_of => [ Integer ]
- )
- end
+ property :flags, Hash
+ property :cwd, String
+ property :environment, Hash
+ property :timeout, Integer
end
end
end
diff --git a/lib/chef/resource/erl_call.rb b/lib/chef/resource/erl_call.rb
deleted file mode 100644
index 3e317676a5..0000000000
--- a/lib/chef/resource/erl_call.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-#
-# Author:: Joe Williams (<joe@joetify.com>)
-# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2009-2016, Joe Williams
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "chef/resource"
-require "chef/provider/erl_call"
-
-class Chef
- class Resource
- class ErlCall < Chef::Resource
-
- # erl_call : http://erlang.org/doc/man/erl_call.html
-
- identity_attr :code
-
- default_action :run
-
- def initialize(name, run_context = nil)
- super
-
- @code = "q()." # your erlang code goes here
- @cookie = nil # cookie of the erlang node
- @distributed = false # if you want to have a distributed erlang node
- @name_type = "sname" # type of erlang hostname name or sname
- @node_name = "chef@localhost" # the erlang node hostname
- end
-
- def code(arg = nil)
- set_or_return(
- :code,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def cookie(arg = nil)
- set_or_return(
- :cookie,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def distributed(arg = nil)
- set_or_return(
- :distributed,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def name_type(arg = nil)
- set_or_return(
- :name_type,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def node_name(arg = nil)
- set_or_return(
- :node_name,
- arg,
- :kind_of => [ String ]
- )
- end
-
- end
- end
-end
diff --git a/lib/chef/resource/execute.rb b/lib/chef/resource/execute.rb
index 1a56607267..f7313cae40 100644
--- a/lib/chef/resource/execute.rb
+++ b/lib/chef/resource/execute.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,14 +18,20 @@
#
require "chef/resource"
-require "chef/provider/execute"
class Chef
class Resource
class Execute < Chef::Resource
+ resource_name :execute
+ provides :execute
identity_attr :command
+ description "Use the execute resource to execute a single command. Commands that"\
+ " are executed with this resource are (by their nature) not idempotent,"\
+ " as they are typically unique to the environment in which they are run."\
+ " Use not_if and only_if to guard this resource for idempotence."
+
# The ResourceGuardInterpreter wraps a resource's guards in another resource. That inner resource
# needs to behave differently during (for example) why_run mode, so we flag it here. For why_run mode
# we still want to execute the guard resource even if we are not executing the wrapping resource.
@@ -38,26 +44,8 @@ class Chef
super
@command = name
@backup = 5
- @creates = nil
- @cwd = nil
- @environment = nil
- @group = nil
- @path = nil
- @returns = 0
- @timeout = nil
- @user = nil
- @umask = nil
@default_guard_interpreter = :execute
@is_guard_interpreter = false
- @live_stream = false
- end
-
- def umask(arg = nil)
- set_or_return(
- :umask,
- arg,
- :kind_of => [ String, Integer ]
- )
end
def command(arg = nil)
@@ -67,81 +55,25 @@ class Chef
:kind_of => [ String, Array ]
)
end
-
- def creates(arg = nil)
- set_or_return(
- :creates,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def cwd(arg = nil)
- set_or_return(
- :cwd,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def environment(arg = nil)
- set_or_return(
- :environment,
- arg,
- :kind_of => [ Hash ]
- )
- end
+ property :umask, [ String, Integer ]
+ property :creates, String
+ property :cwd, String
+ property :environment, Hash
alias :env :environment
- def group(arg = nil)
- set_or_return(
- :group,
- arg,
- :kind_of => [ String, Integer ]
- )
- end
-
- def live_stream(arg = nil)
- set_or_return(
- :live_stream,
- arg,
- :kind_of => [ TrueClass, FalseClass ])
- end
-
- def path(arg = nil)
- Chef::Log.warn "The 'path' attribute of 'execute' is not used by any provider in Chef 11 or Chef 12. Use 'environment' attribute to configure 'PATH'. This attribute will be removed in Chef 13."
-
- set_or_return(
- :path,
- arg,
- :kind_of => [ Array ]
- )
- end
-
- def returns(arg = nil)
- set_or_return(
- :returns,
- arg,
- :kind_of => [ Integer, Array ]
- )
- end
+ property :group, [ String, Integer ]
+ property :live_stream, [ TrueClass, FalseClass ], default: false
+ property :returns, [ Integer, Array ], default: 0
+ property :timeout, [ Integer, Float ]
+ property :user, [ String, Integer ]
+ property :domain, String
+ property :password, String, sensitive: true
- def timeout(arg = nil)
- set_or_return(
- :timeout,
- arg,
- :kind_of => [ Integer, Float ]
- )
- end
+ # lazy used to set default value of sensitive to true if password is set
+ property :sensitive, [ TrueClass, FalseClass ], default: lazy { |r| r.password ? true : false }
- def user(arg = nil)
- set_or_return(
- :user,
- arg,
- :kind_of => [ String, Integer ]
- )
- end
+ property :elevated, [ TrueClass, FalseClass ], default: false
def self.set_guard_inherited_attributes(*inherited_attributes)
@class_inherited_attributes = inherited_attributes
@@ -159,6 +91,72 @@ class Chef
ancestor_attributes.concat(@class_inherited_attributes ? @class_inherited_attributes : []).uniq
end
+ def after_created
+ validate_identity_platform(user, password, domain, elevated)
+ identity = qualify_user(user, password, domain)
+ domain(identity[:domain])
+ user(identity[:user])
+ end
+
+ def validate_identity_platform(specified_user, password = nil, specified_domain = nil, elevated = false)
+ if node[:platform_family] == "windows"
+ if specified_user && password.nil?
+ raise ArgumentError, "A value for `password` must be specified when a value for `user` is specified on the Windows platform"
+ end
+
+ if elevated && !specified_user && !password
+ raise ArgumentError, "`elevated` option should be passed only with `username` and `password`."
+ end
+ else
+ if password || specified_domain
+ raise Exceptions::UnsupportedPlatform, "Values for `domain` and `password` are only supported on the Windows platform"
+ end
+
+ if elevated
+ raise Exceptions::UnsupportedPlatform, "Value for `elevated` is only supported on the Windows platform"
+ end
+ end
+ end
+
+ def qualify_user(specified_user, password = nil, specified_domain = nil)
+ domain = specified_domain
+ user = specified_user
+
+ if specified_user.nil? && ! specified_domain.nil?
+ raise ArgumentError, "The domain `#{specified_domain}` was specified, but no user name was given"
+ end
+
+ # if domain is provided in both username and domain
+ if specified_user && ((specified_user.include? '\\') || (specified_user.include? "@")) && specified_domain
+ raise ArgumentError, "The domain is provided twice. Username: `#{specified_user}`, Domain: `#{specified_domain}`. Please specify domain only once."
+ end
+
+ if ! specified_user.nil? && specified_domain.nil?
+ # Splitting username of format: Domain\Username
+ domain_and_user = user.split('\\')
+
+ if domain_and_user.length == 2
+ domain = domain_and_user[0]
+ user = domain_and_user[1]
+ elsif domain_and_user.length == 1
+ # Splitting username of format: Username@Domain
+ domain_and_user = user.split("@")
+ if domain_and_user.length == 2
+ domain = domain_and_user[1]
+ user = domain_and_user[0]
+ elsif domain_and_user.length != 1
+ raise ArgumentError, "The specified user name `#{user}` is not a syntactically valid user name"
+ end
+ end
+ end
+
+ if ( password || domain ) && user.nil?
+ raise ArgumentError, "A value for `password` or `domain` was specified without specification of a value for `user`"
+ end
+
+ { domain: domain, user: user }
+ end
+
set_guard_inherited_attributes(
:cwd,
:environment,
diff --git a/lib/chef/resource/file.rb b/lib/chef/resource/file.rb
index ac6dc5fbdb..341b106720 100644
--- a/lib/chef/resource/file.rb
+++ b/lib/chef/resource/file.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2008-2016, 2011-2015 Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,12 +21,15 @@ require "chef/resource"
require "chef/platform/query_helpers"
require "chef/mixin/securable"
require "chef/resource/file/verification"
+require "pathname"
class Chef
class Resource
class File < Chef::Resource
include Chef::Mixin::Securable
+ description "Use the file resource to manage files directly on a node."
+
if Platform.windows?
# Use Windows rights instead of standard *nix permissions
state_attrs :checksum, :rights, :deny_rights
@@ -42,20 +45,20 @@ class Chef
# mutate the new_resource.checksum which would change the
# user intent in the new_resource if the resource is reused.
#
- # @returns [String] Checksum of the file we actually rendered
+ # @return [String] Checksum of the file we actually rendered
attr_accessor :final_checksum
default_action :create
allowed_actions :create, :delete, :touch, :create_if_missing
property :path, String, name_property: true, identity: true
- property :atomic_update, [ true, false ], desired_state: false, default: lazy { Chef::Config[:file_atomic_update] }
+ property :atomic_update, [ TrueClass, FalseClass ], desired_state: false, default: lazy { |r| r.docker? && r.special_docker_files?(r.path) ? false : Chef::Config[:file_atomic_update] }
property :backup, [ Integer, false ], desired_state: false, default: 5
property :checksum, [ /^[a-zA-Z0-9]{64}$/, nil ]
property :content, [ String, nil ], desired_state: false
property :diff, [ String, nil ], desired_state: false
- property :force_unlink, [ true, false ], desired_state: false, default: false
- property :manage_symlink_source, [ true, false ], desired_state: false
+ property :force_unlink, [ TrueClass, FalseClass ], desired_state: false, default: false
+ property :manage_symlink_source, [ TrueClass, FalseClass ], desired_state: false
property :verifications, Array, default: lazy { [] }
def verify(command = nil, opts = {}, &block)
@@ -78,6 +81,10 @@ class Chef
end
state_attrs
end
+
+ def special_docker_files?(file)
+ %w{/etc/hosts /etc/hostname /etc/resolv.conf}.include?(Pathname(file.scrub).cleanpath.to_path)
+ end
end
end
end
diff --git a/lib/chef/resource/file/verification.rb b/lib/chef/resource/file/verification.rb
index e11035d33f..578e6066a2 100644
--- a/lib/chef/resource/file/verification.rb
+++ b/lib/chef/resource/file/verification.rb
@@ -80,6 +80,10 @@ class Chef
c
end
+ def logger
+ @parent_resource.logger
+ end
+
def initialize(parent_resource, command, opts, &block)
@command, @command_opts = command, opts
@block = block
@@ -87,7 +91,7 @@ class Chef
end
def verify(path, opts = {})
- Chef::Log.debug("Running verification[#{self}] on #{path}")
+ logger.trace("Running verification[#{self}] on #{path}")
if @block
verify_block(path, opts)
elsif @command.is_a?(Symbol)
@@ -106,13 +110,10 @@ class Chef
# We reuse Chef::GuardInterpreter in order to support
# the same set of options that the not_if/only_if blocks do
def verify_command(path, opts)
- # First implementation interpolated `file`; docs & RFC claim `path`
- # is interpolated. Until `file` can be deprecated, interpolate both.
- Chef.log_deprecation(
- "%{file} is deprecated in verify command and will not be "\
- "supported in Chef 13. Please use %{path} instead."
- ) if @command.include?("%{file}")
- command = @command % { :file => path, :path => path }
+ if @command.include?("%{file}")
+ raise ArgumentError, "The %{file} expansion for verify commands has been removed. Please use %{path} instead."
+ end
+ command = @command % { :path => path }
interpreter = Chef::GuardInterpreter.for_resource(@parent_resource, command, @command_opts)
interpreter.evaluate
end
@@ -122,6 +123,16 @@ class Chef
v = verification_class.new(@parent_resource, @command, @command_opts, &@block)
v.verify(path, opts)
end
+
+ def to_s
+ if @block
+ "<Proc>"
+ elsif @command.is_a?(Symbol)
+ "#{@command.inspect} (#{Chef::Resource::File::Verification.lookup(@command).name})"
+ elsif @command.is_a?(String)
+ @command
+ end
+ end
end
end
end
diff --git a/lib/chef/resource/file/verification/systemd_unit.rb b/lib/chef/resource/file/verification/systemd_unit.rb
new file mode 100644
index 0000000000..b492b32d39
--- /dev/null
+++ b/lib/chef/resource/file/verification/systemd_unit.rb
@@ -0,0 +1,67 @@
+#
+# Author:: Mal Graty (<mal.graty@googlemail.com>)
+# Copyright:: Copyright 2013-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/mixin/which"
+
+class Chef
+ class Resource
+ class File
+ class Verification
+
+ #
+ # Systemd provides a binary for verifying the correctness of
+ # unit files. Unfortunately some units have constraints on the
+ # filename meaning that normal verification against temp files
+ # won't work.
+ #
+ # Working around that requires placing a copy of the temp file
+ # in a temp directory, under its real name and running the
+ # verification tool against that file.
+ #
+
+ class SystemdUnit < Chef::Resource::File::Verification
+ include Chef::Mixin::Which
+
+ provides :systemd_unit
+
+ def initialize(parent_resource, command, opts, &block)
+ super
+ @command = systemd_analyze_cmd
+ end
+
+ def verify(path, opts = {})
+ return true unless systemd_analyze_path
+ Dir.mktmpdir("chef-systemd-unit") do |dir|
+ temp = "#{dir}/#{::File.basename(@parent_resource.path)}"
+ ::FileUtils.cp(path, temp)
+ verify_command(temp, opts)
+ end
+ end
+
+ def systemd_analyze_cmd
+ @systemd_analyze_cmd ||= "#{systemd_analyze_path} verify %{path}"
+ end
+
+ def systemd_analyze_path
+ @systemd_analyze_path ||= which("systemd-analyze")
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/freebsd_package.rb b/lib/chef/resource/freebsd_package.rb
index 540774e864..d16355d027 100644
--- a/lib/chef/resource/freebsd_package.rb
+++ b/lib/chef/resource/freebsd_package.rb
@@ -32,20 +32,29 @@ class Chef
resource_name :freebsd_package
provides :package, platform: "freebsd"
+ description "Use the freebsd_package resource to manage packages for the FreeBSD platform."
+
+ # make sure we assign the appropriate underlying providers based on what
+ # package managers exist on this FreeBSD system or the source of the package
+ #
+ # @return [void]
def after_created
assign_provider
end
+ # Is the system at least version 1000017 or is the make variable WITH_PKGNG set
+ #
+ # @return [Boolean] do we support pkgng
def supports_pkgng?
- ships_with_pkgng? || !!shell_out!("make -V WITH_PKGNG", :env => nil).stdout.match(/yes/i)
+ ships_with_pkgng? || !!shell_out_compact!("make", "-V", "WITH_PKGNG", :env => nil).stdout.match(/yes/i)
end
private
+ # It was not until __FreeBSD_version 1000017 that pkgng became
+ # the default binary package manager. See '/usr/ports/Mk/bsd.port.mk'.
def ships_with_pkgng?
- # It was not until __FreeBSD_version 1000017 that pkgng became
- # the default binary package manager. See '/usr/ports/Mk/bsd.port.mk'.
- node.automatic[:os_version].to_i >= 1000017
+ node[:os_version].to_i >= 1000017
end
def assign_provider
diff --git a/lib/chef/resource/gem_package.rb b/lib/chef/resource/gem_package.rb
index e095115356..ee1262cb47 100644
--- a/lib/chef/resource/gem_package.rb
+++ b/lib/chef/resource/gem_package.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,18 +23,37 @@ class Chef
class GemPackage < Chef::Resource::Package
resource_name :gem_package
+ description "Use the gem_package resource to manage gem packages that are only"\
+ " included in recipes. When a package is installed from a local file,"\
+ " it must be added to the node using the remote_file or cookbook_file"\
+ " resources."
+
+ # the source can either be a path to a package source like:
+ # source /var/tmp/mygem-1.2.3.4.gem
+ # or it can be a url rubygems source like:
+ # https://www.rubygems.org
+ # the default has to be nil in order for the magical wiring up of the name property to
+ # the source pathname to work correctly.
+ #
+ # we don't do coercions here because its all a bit too complicated
+ #
+ # FIXME? the array form of installing paths most likely does not work?
+ #
property :source, [ String, Array ]
- property :clear_sources, [ true, false ], default: false, desired_state: false
+ property :clear_sources, [ TrueClass, FalseClass ], default: false, desired_state: false
# Sets a custom gem_binary to run for gem commands.
property :gem_binary, String, desired_state: false
+ # set to false to avoid including Chef::Config[:rubygems_url] in the sources
+ property :include_default_source, [ TrueClass, FalseClass ], default: true
+
##
# Options for the gem install, either a Hash or a String. When a hash is
# given, the options are passed to Gem::DependencyInstaller.new, and the
# gem will be installed via the gems API. When a String is given, the gem
# will be installed by shelling out to the gem command. Using a Hash of
# options with an explicit gem_binary will result in undefined behavior.
- property :options, [ String, Hash, nil ], desired_state: false
+ property :options, [ String, Hash, Array, nil ], desired_state: false
end
end
diff --git a/lib/chef/resource/git.rb b/lib/chef/resource/git.rb
index 4799b54d3d..7e756db78f 100644
--- a/lib/chef/resource/git.rb
+++ b/lib/chef/resource/git.rb
@@ -21,23 +21,14 @@ require "chef/resource/scm"
class Chef
class Resource
class Git < Chef::Resource::Scm
+ description "Use the git resource to manage source control resources that exist"\
+ " in a git repository. git version 1.6.5 (or higher) is required to"\
+ " use all of the functionality in the git resource."
- def initialize(name, run_context = nil)
- super
- @additional_remotes = Hash[]
- end
-
- def additional_remotes(arg = nil)
- set_or_return(
- :additional_remotes,
- arg,
- :kind_of => Hash
- )
- end
+ property :additional_remotes, Hash, default: lazy { Hash.new }
alias :branch :revision
alias :reference :revision
-
alias :repo :repository
end
end
diff --git a/lib/chef/resource/group.rb b/lib/chef/resource/group.rb
index d3a4a1ce89..9298fbc150 100644
--- a/lib/chef/resource/group.rb
+++ b/lib/chef/resource/group.rb
@@ -20,39 +20,21 @@
class Chef
class Resource
class Group < Chef::Resource
-
- identity_attr :group_name
-
state_attrs :members
+ description "Use the group resource to manage a local group."
+
allowed_actions :create, :remove, :modify, :manage
default_action :create
def initialize(name, run_context = nil)
super
- @group_name = name
- @gid = nil
@members = []
@excluded_members = []
- @append = false
- @non_unique = false
end
- def group_name(arg = nil)
- set_or_return(
- :group_name,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def gid(arg = nil)
- set_or_return(
- :gid,
- arg,
- :kind_of => [ String, Integer ]
- )
- end
+ property :group_name, String, name_property: true, identity: true
+ property :gid, [ String, Integer ]
def members(arg = nil)
converted_members = arg.is_a?(String) ? arg.split(",") : arg
@@ -74,29 +56,9 @@ class Chef
)
end
- def append(arg = nil)
- set_or_return(
- :append,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def system(arg = nil)
- set_or_return(
- :system,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def non_unique(arg = nil)
- set_or_return(
- :non_unique,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :append, [ TrueClass, FalseClass ], default: false
+ property :system, [ TrueClass, FalseClass ], default: false
+ property :non_unique, [ TrueClass, FalseClass ], default: false
end
end
end
diff --git a/lib/chef/resource/homebrew_cask.rb b/lib/chef/resource/homebrew_cask.rb
new file mode 100644
index 0000000000..e45a5c4052
--- /dev/null
+++ b/lib/chef/resource/homebrew_cask.rb
@@ -0,0 +1,99 @@
+#
+# Author:: Joshua Timberman (<jtimberman@chef.io>)
+# Author:: Graeme Mathieson (<mathie@woss.name>)
+#
+# Copyright:: 2011-2018, Chef Software, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+require "chef/mixin/homebrew_user"
+
+class Chef
+ class Resource
+ class HomebrewCask < Chef::Resource
+ resource_name :homebrew_cask
+ provides(:homebrew_cask) { true }
+
+ description "Use the homebrew_cask resource to install binaries distributed via the Homebrew package manager."
+ introduced "14.0"
+
+ include Chef::Mixin::HomebrewUser
+
+ property :cask_name, String,
+ description: "Cask name to override the resource name.",
+ regex: %r{^[\w/-]+$},
+ name_property: true
+
+ property :options, String,
+ description: "Options to pass to the brew CLI during installation."
+
+ property :install_cask, [TrueClass, FalseClass],
+ description: "Auto install cask tap if necessary.",
+ default: true
+
+ property :homebrew_path, String,
+ description: "The path to the homebrew binary.",
+ default: "/usr/local/bin/brew"
+
+ property :owner, String,
+ description: "The owner of the homebrew installation.",
+ default: lazy { find_homebrew_username }
+
+ action :install do
+ description "Install an application packaged as a Homebrew cask."
+
+ homebrew_tap "caskroom/cask" if new_resource.install_cask
+
+ unless casked?
+ converge_by("install cask #{new_resource.name} #{new_resource.options}") do
+ shell_out!("#{new_resource.homebrew_path} cask install #{new_resource.name} #{new_resource.options}",
+ user: new_resource.owner,
+ env: { "HOME" => ::Dir.home(new_resource.owner), "USER" => new_resource.owner },
+ cwd: ::Dir.home(new_resource.owner))
+ end
+ end
+ end
+
+ action :remove do
+ description "Remove an application packaged as a Homebrew cask."
+
+ homebrew_tap "caskroom/cask" if new_resource.install_cask
+
+ if casked?
+ converge_by("uninstall cask #{new_resource.name}") do
+ shell_out!("#{new_resource.homebrew_path} cask uninstall #{new_resource.name}",
+ user: new_resource.owner,
+ env: { "HOME" => ::Dir.home(new_resource.owner), "USER" => new_resource.owner },
+ cwd: ::Dir.home(new_resource.owner))
+ end
+ end
+ end
+
+ action_class do
+ alias_method :action_cask, :action_install
+ alias_method :action_uncask, :action_remove
+ alias_method :action_uninstall, :action_remove
+
+ def casked?
+ unscoped_name = new_resource.name.split("/").last
+ shell_out!('#{new_resource.homebrew_path} cask list 2>/dev/null',
+ user: new_resource.owner,
+ env: { "HOME" => ::Dir.home(new_resource.owner), "USER" => new_resource.owner },
+ cwd: ::Dir.home(new_resource.owner)).stdout.split.include?(unscoped_name)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/homebrew_package.rb b/lib/chef/resource/homebrew_package.rb
index c2d0a65c5b..dee132a1b4 100644
--- a/lib/chef/resource/homebrew_package.rb
+++ b/lib/chef/resource/homebrew_package.rb
@@ -27,6 +27,9 @@ class Chef
resource_name :homebrew_package
provides :package, os: "darwin"
+ description "Use the homebrew_package resource to manage packages for the macOS platform."
+ introduced "12.0"
+
property :homebrew_user, [ String, Integer ]
end
diff --git a/lib/chef/resource/homebrew_tap.rb b/lib/chef/resource/homebrew_tap.rb
new file mode 100644
index 0000000000..2028ce80c8
--- /dev/null
+++ b/lib/chef/resource/homebrew_tap.rb
@@ -0,0 +1,87 @@
+#
+# Author:: Joshua Timberman (<jtimberman@chef.io>)
+# Author:: Graeme Mathieson (<mathie@woss.name>)
+#
+# Copyright:: 2011-2018, Chef Software, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+require "chef/mixin/homebrew_user"
+
+class Chef
+ class Resource
+ class HomebrewTap < Chef::Resource
+ resource_name :homebrew_tap
+ provides(:homebrew_tap) { true }
+
+ description "Use the homebrew_tap resource to add additional formula repositories to the Homebrew package manager."
+ introduced "14.0"
+
+ include Chef::Mixin::HomebrewUser
+
+ property :tap_name, String,
+ description: "Optional tap name to override the resource name",
+ validation_message: "Homebrew tap names must be in the form REPO/TAP",
+ regex: %r{^[\w-]+(?:\/[\w-]+)+$},
+ name_property: true
+
+ property :url, String,
+ description: "URL to the tap."
+
+ property :full, [TrueClass, FalseClass],
+ description: "Perform a full clone rather than a shallow clone on the tap.",
+ default: false
+
+ property :homebrew_path, String,
+ description: "The path to the homebrew binary.",
+ default: "/usr/local/bin/brew"
+
+ property :owner, String,
+ description: "The owner of the homebrew installation",
+ default: lazy { find_homebrew_username }
+
+ action :tap do
+ description "Add a Homebrew tap."
+
+ unless tapped?(new_resource.name)
+ converge_by("tap #{new_resource.name}") do
+ shell_out!("#{new_resource.homebrew_path} tap #{new_resource.full ? '--full' : ''} #{new_resource.name} #{new_resource.url || ''}",
+ user: new_resource.owner,
+ env: { "HOME" => ::Dir.home(new_resource.owner), "USER" => new_resource.owner },
+ cwd: ::Dir.home(new_resource.owner))
+ end
+ end
+ end
+
+ action :untap do
+ description "Remove a Homebrew tap."
+
+ if tapped?(new_resource.name)
+ converge_by("untap #{new_resource.name}") do
+ shell_out!("#{new_resource.homebrew_path} untap #{new_resource.name}",
+ user: new_resource.owner,
+ env: { "HOME" => ::Dir.home(new_resource.owner), "USER" => new_resource.owner },
+ cwd: ::Dir.home(new_resource.owner))
+ end
+ end
+ end
+
+ def tapped?(name)
+ tap_dir = name.gsub("/", "/homebrew-")
+ ::File.directory?("/usr/local/Homebrew/Library/Taps/#{tap_dir}")
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/hostname.rb b/lib/chef/resource/hostname.rb
new file mode 100644
index 0000000000..243c86287b
--- /dev/null
+++ b/lib/chef/resource/hostname.rb
@@ -0,0 +1,249 @@
+class Chef
+ class Resource
+ # Sets the hostname and updates /etc/hosts on *nix systems
+ # @since 14.0.0
+ class Hostname < Chef::Resource
+ resource_name :hostname
+ provides :hostname
+
+ description "Use the hostname resource to set the system's hostname, configure hostname and hosts config"\
+ " file, and re-run the Ohai hostname plugin so the hostname will be available in subsequent cookbooks."
+ introduced "14.0"
+
+ property :hostname, String,
+ description: "The hostname if different than the resource's name",
+ name_property: true
+
+ property :compile_time, [ TrueClass, FalseClass ],
+ description: "Should the resource run at compile time or not.",
+ default: true
+
+ property :ipaddress, String,
+ description: "The ip address to use when configuring the hosts file",
+ default: lazy { node["ipaddress"] }
+
+ property :aliases, [ Array, nil ],
+ description: "An array of hostname aliases to use when configuring the hosts file",
+ default: nil
+
+ property :windows_reboot, [ TrueClass, FalseClass ],
+ description: "Should Windows nodes be rebooted upon changing the name so it can take effect",
+ default: true
+
+ action_class do
+ def append_replacing_matching_lines(path, regex, string)
+ text = IO.read(path).split("\n")
+ text.reject! { |s| s =~ regex }
+ text += [ string ]
+ file path do
+ content text.join("\n") + "\n"
+ owner "root"
+ group node["root_group"]
+ mode "0644"
+ not_if { IO.read(path).split("\n").include?(string) }
+ end
+ end
+
+ # read in the xml file used by Ec2ConfigService and update the Ec2SetComputerName
+ # setting to disable updating the computer name so we don't revert our change on reboot
+ # @return [String]
+ def updated_ec2_config_xml
+ begin
+ require "rexml/document"
+ config_file = 'C:\Program Files\Amazon\Ec2ConfigService\Settings\config.xml'
+ config = REXML::Document.new(::File.read(config_file))
+ # find an element named State with a sibling element whose value is Ec2SetComputerName
+ REXML::XPath.each(config, "//Plugin/State[../Name/text() = 'Ec2SetComputerName']") do |element|
+ element.text = "Disabled"
+ end
+ rescue
+ return ""
+ end
+ config.to_s
+ end
+ end
+
+ action :set do
+ description "Sets the node's hostname"
+
+ if node["platform_family"] != "windows"
+ ohai "reload hostname" do
+ plugin "hostname"
+ action :nothing
+ end
+
+ # set the hostname via /bin/hostname
+ declare_resource(:execute, "set hostname to #{new_resource.hostname}") do
+ command "/bin/hostname #{new_resource.hostname}"
+ not_if { shell_out!("hostname").stdout.chomp == new_resource.hostname }
+ notifies :reload, "ohai[reload hostname]"
+ end
+
+ # make sure node['fqdn'] resolves via /etc/hosts
+ unless new_resource.ipaddress.nil?
+ newline = "#{new_resource.ipaddress} #{new_resource.hostname}"
+ newline << " #{new_resource.aliases.join(" ")}" if new_resource.aliases && !new_resource.aliases.empty?
+ newline << " #{new_resource.hostname[/[^\.]*/]}"
+ r = append_replacing_matching_lines("/etc/hosts", /^#{new_resource.ipaddress}\s+|\s+#{new_resource.hostname}\s+/, newline)
+ r.atomic_update false if docker?
+ r.notifies :reload, "ohai[reload hostname]"
+ end
+
+ # setup the hostname to perist on a reboot
+ case
+ when ::File.exist?("/usr/sbin/scutil")
+ # darwin
+ declare_resource(:execute, "set HostName via scutil") do
+ command "/usr/sbin/scutil --set HostName #{new_resource.hostname}"
+ not_if { shell_out!("/usr/sbin/scutil --get HostName").stdout.chomp == new_resource.hostname }
+ notifies :reload, "ohai[reload hostname]"
+ end
+ declare_resource(:execute, "set ComputerName via scutil") do
+ command "/usr/sbin/scutil --set ComputerName #{new_resource.hostname}"
+ not_if { shell_out!("/usr/sbin/scutil --get ComputerName").stdout.chomp == new_resource.hostname }
+ notifies :reload, "ohai[reload hostname]"
+ end
+ shortname = new_resource.hostname[/[^\.]*/]
+ declare_resource(:execute, "set LocalHostName via scutil") do
+ command "/usr/sbin/scutil --set LocalHostName #{shortname}"
+ not_if { shell_out!("/usr/sbin/scutil --get LocalHostName").stdout.chomp == shortname }
+ notifies :reload, "ohai[reload hostname]"
+ end
+ when node["os"] == "linux"
+ case
+ when ::File.exist?("/usr/bin/hostnamectl") && !docker?
+ # use hostnamectl whenever we find it on linux (as systemd takes over the world)
+ # this must come before other methods like /etc/hostname and /etc/sysconfig/network
+ declare_resource(:execute, "hostnamectl set-hostname #{new_resource.hostname}") do
+ notifies :reload, "ohai[reload hostname]"
+ not_if { shell_out!("hostnamectl status", { :returns => [0, 1] }).stdout =~ /Static hostname:\s+#{new_resource.hostname}/ }
+ end
+ when ::File.exist?("/etc/hostname")
+ # debian family uses /etc/hostname
+ # arch also uses /etc/hostname
+ # the "platform: iox_xr, platform_family: wrlinux, os: linux" platform also hits this
+ # the "platform: nexus, platform_family: wrlinux, os: linux" platform also hits this
+ # this is also fallback for any linux systemd host in a docker container (where /usr/bin/hostnamectl will fail)
+ declare_resource(:file, "/etc/hostname") do
+ atomic_update false if docker?
+ content "#{new_resource.hostname}\n"
+ owner "root"
+ group node["root_group"]
+ mode "0644"
+ end
+ when ::File.file?("/etc/sysconfig/network")
+ # older non-systemd RHEL/Fedora derived
+ append_replacing_matching_lines("/etc/sysconfig/network", /^HOSTNAME\s*=/, "HOSTNAME=#{new_resource.hostname}")
+ when ::File.exist?("/etc/HOSTNAME")
+ # SuSE/OpenSUSE uses /etc/HOSTNAME
+ declare_resource(:file, "/etc/HOSTNAME") do
+ content "#{new_resource.hostname}\n"
+ owner "root"
+ group node["root_group"]
+ mode "0644"
+ end
+ when ::File.exist?("/etc/conf.d/hostname")
+ # Gentoo
+ declare_resource(:file, "/etc/conf.d/hostname") do
+ content "hostname=\"#{new_resource.hostname}\"\n"
+ owner "root"
+ group node["root_group"]
+ mode "0644"
+ end
+ else
+ # This is a failsafe for all other linux distributions where we set the hostname
+ # via /etc/sysctl.conf on reboot. This may get into a fight with other cookbooks
+ # that manage sysctls on linux.
+ append_replacing_matching_lines("/etc/sysctl.conf", /^\s+kernel\.hostname\s+=/, "kernel.hostname=#{new_resource.hostname}")
+ end
+ when ::File.exist?("/etc/rc.conf")
+ # *BSD systems with /etc/rc.conf + /etc/myname
+ append_replacing_matching_lines("/etc/rc.conf", /^\s+hostname\s+=/, "hostname=#{new_resource.hostname}")
+
+ declare_resource(:file, "/etc/myname") do
+ content "#{new_resource.hostname}\n"
+ owner "root"
+ group node["root_group"]
+ mode "0644"
+ end
+ when ::File.exist?("/etc/nodename")
+ # Solaris <= 5.10 systems prior to svccfg taking over this functionality (must come before svccfg handling)
+ declare_resource(:file, "/etc/nodename") do
+ content "#{new_resource.hostname}\n"
+ owner "root"
+ group node["root_group"]
+ mode "0644"
+ end
+ # Solaris also has /etc/inet/hosts (copypasta alert)
+ unless new_resource.ipaddress.nil?
+ newline = "#{new_resource.ipaddress} #{new_resource.hostname}"
+ newline << " #{new_resource.aliases.join(" ")}" if new_resource.aliases && !new_resource.aliases.empty?
+ newline << " #{new_resource.hostname[/[^\.]*/]}"
+ r = append_replacing_matching_lines("/etc/inet/hosts", /^#{new_resource.ipaddress}\s+|\s+#{new_resource.hostname}\s+/, newline)
+ r.notifies :reload, "ohai[reload hostname]"
+ end
+ when ::File.exist?("/usr/sbin/svccfg")
+ # Solaris >= 5.11 systems using svccfg (must come after /etc/nodename handling)
+ declare_resource(:execute, "svccfg -s system/identity:node setprop config/nodename=\'#{new_resource.hostname}\'") do
+ notifies :run, "execute[svcadm refresh]", :immediately
+ notifies :run, "execute[svcadm restart]", :immediately
+ not_if { shell_out!("svccfg -s system/identity:node listprop config/nodename").stdout.chomp =~ /config\/nodename\s+astring\s+#{new_resource.hostname}/ }
+ end
+ declare_resource(:execute, "svcadm refresh") do
+ command "svcadm refresh system/identity:node"
+ action :nothing
+ end
+ declare_resource(:execute, "svcadm restart") do
+ command "svcadm restart system/identity:node"
+ action :nothing
+ end
+ else
+ raise "Do not know how to set hostname on os #{node["os"]}, platform #{node["platform"]},"\
+ "platform_version #{node["platform_version"]}, platform_family #{node["platform_family"]}"
+ end
+
+ else # windows
+ raise "Windows hostnames cannot contain a period." if new_resource.hostname.match?(/\./)
+
+ # suppress EC2 config service from setting our hostname
+ if ::File.exist?('C:\Program Files\Amazon\Ec2ConfigService\Settings\config.xml')
+ xml_contents = updated_ec2_config_xml
+ if xml_contents.empty?
+ Chef::Log.warn('Unable to properly parse and update C:\Program Files\Amazon\Ec2ConfigService\Settings\config.xml contents. Skipping file update.')
+ else
+ declare_resource(:file, 'C:\Program Files\Amazon\Ec2ConfigService\Settings\config.xml') do
+ content xml_contents
+ end
+ end
+ end
+
+ # update via netdom
+ declare_resource(:powershell_script, "set hostname") do
+ code <<-EOH
+ $sysInfo = Get-WmiObject -Class Win32_ComputerSystem
+ $sysInfo.Rename("#{new_resource.hostname}")
+ EOH
+ notifies :request_reboot, "reboot[setting hostname]"
+ not_if { Socket.gethostbyname(Socket.gethostname).first == new_resource.hostname }
+ end
+
+ # reboot because $windows
+ declare_resource(:reboot, "setting hostname") do
+ reason "chef setting hostname"
+ action :nothing
+ only_if { new_resource.windows_reboot }
+ end
+ end
+ end
+
+ # this resource forces itself to run at compile_time
+ def after_created
+ if compile_time
+ Array(action).each do |action|
+ run_action(action)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/http_request.rb b/lib/chef/resource/http_request.rb
index fcc48470bc..f209e419dd 100644
--- a/lib/chef/resource/http_request.rb
+++ b/lib/chef/resource/http_request.rb
@@ -18,30 +18,26 @@
#
require "chef/resource"
-require "chef/provider/http_request"
class Chef
class Resource
class HttpRequest < Chef::Resource
+ resource_name :http_request
+ provides :http_request
- identity_attr :url
+ description "Use the http_request resource to send an HTTP request (GET, PUT,"\
+ " POST, DELETE, HEAD, or OPTIONS) with an arbitrary message. This"\
+ " resource is often useful when custom callbacks are necessary."
default_action :get
- allowed_actions :get, :put, :post, :delete, :head, :options
+ allowed_actions :get, :patch, :put, :post, :delete, :head, :options
+
+ property :url, String, identity: true
+ property :headers, Hash, default: lazy { Hash.new }
def initialize(name, run_context = nil)
super
@message = name
- @url = nil
- @headers = {}
- end
-
- def url(args = nil)
- set_or_return(
- :url,
- args,
- :kind_of => String
- )
end
def message(args = nil, &block)
@@ -53,14 +49,6 @@ class Chef
)
end
- def headers(args = nil)
- set_or_return(
- :headers,
- args,
- :kind_of => Hash
- )
- end
-
end
end
end
diff --git a/lib/chef/resource/ifconfig.rb b/lib/chef/resource/ifconfig.rb
index fd523d9580..579a4eeedb 100644
--- a/lib/chef/resource/ifconfig.rb
+++ b/lib/chef/resource/ifconfig.rb
@@ -21,127 +21,37 @@ require "chef/resource"
class Chef
class Resource
+ # @example set a static ip on eth1
+ # ifconfig '33.33.33.80' do
+ # device 'eth1'
+ # end
class Ifconfig < Chef::Resource
+ resource_name :ifconfig
- identity_attr :device
+ description "Use the ifconfig resource to manage interfaces on *nix systems."
state_attrs :inet_addr, :mask
default_action :add
allowed_actions :add, :delete, :enable, :disable
- def initialize(name, run_context = nil)
- super
- @target = name
- @hwaddr = nil
- @mask = nil
- @inet_addr = nil
- @bcast = nil
- @mtu = nil
- @metric = nil
- @device = nil
- @onboot = nil
- @network = nil
- @bootproto = nil
- @onparent = nil
- end
-
- def target(arg = nil)
- set_or_return(
- :target,
- arg,
- :kind_of => String
- )
- end
-
- def device(arg = nil)
- set_or_return(
- :device,
- arg,
- :kind_of => String
- )
- end
-
- def hwaddr(arg = nil)
- set_or_return(
- :hwaddr,
- arg,
- :kind_of => String
- )
- end
-
- def inet_addr(arg = nil)
- set_or_return(
- :inet_addr,
- arg,
- :kind_of => String
- )
- end
-
- def bcast(arg = nil)
- set_or_return(
- :bcast,
- arg,
- :kind_of => String
- )
- end
-
- def mask(arg = nil)
- set_or_return(
- :mask,
- arg,
- :kind_of => String
- )
- end
-
- def mtu(arg = nil)
- set_or_return(
- :mtu,
- arg,
- :kind_of => String
- )
- end
-
- def metric(arg = nil)
- set_or_return(
- :metric,
- arg,
- :kind_of => String
- )
- end
-
- def onboot(arg = nil)
- set_or_return(
- :onboot,
- arg,
- :kind_of => String
- )
- end
-
- def network(arg = nil)
- set_or_return(
- :network,
- arg,
- :kind_of => String
- )
- end
-
- def bootproto(arg = nil)
- set_or_return(
- :bootproto,
- arg,
- :kind_of => String
- )
- end
-
- def onparent(arg = nil)
- set_or_return(
- :onparent,
- arg,
- :kind_of => String
- )
- end
+ property :target, String, name_property: true
+ property :hwaddr, String
+ property :mask, String
+ property :family, String, default: "inet"
+ property :inet_addr, String
+ property :bcast, String
+ property :mtu, String
+ property :metric, String
+ property :device, String, identity: true
+ property :onboot, String
+ property :network, String
+ property :bootproto, String
+ property :onparent, String
+ property :ethtool_opts, String
+ property :bonding_opts, String
+ property :master, String
+ property :slave, String
end
-
end
end
diff --git a/lib/chef/resource/ips_package.rb b/lib/chef/resource/ips_package.rb
index 4d2c957e17..70e3bfee81 100644
--- a/lib/chef/resource/ips_package.rb
+++ b/lib/chef/resource/ips_package.rb
@@ -24,11 +24,13 @@ class Chef
class IpsPackage < ::Chef::Resource::Package
resource_name :ips_package
provides :package, os: "solaris2"
- provides :ips_package, os: "solaris2"
+ provides :ips_package
+
+ description "Use the ips_package resource to manage packages (using Image Packaging System (IPS)) on the Solaris 11 platform."
allowed_actions :install, :remove, :upgrade
- property :accept_license, [ true, false ], default: false, desired_state: false
+ property :accept_license, [TrueClass, FalseClass], default: false, desired_state: false
end
end
end
diff --git a/lib/chef/resource/ksh.rb b/lib/chef/resource/ksh.rb
index 3097156329..f0bc277d17 100644
--- a/lib/chef/resource/ksh.rb
+++ b/lib/chef/resource/ksh.rb
@@ -21,6 +21,13 @@ require "chef/resource/script"
class Chef
class Resource
class Ksh < Chef::Resource::Script
+ description "Use the ksh resource to execute scripts using the Korn shell (ksh)"\
+ " interpreter. This resource may also use any of the actions and properties"\
+ " that are available to the execute resource. Commands that are executed"\
+ " with this resource are (by their nature) not idempotent, as they are"\
+ " typically unique to the environment in which they are run. Use not_if"\
+ " and only_if to guard this resource for idempotence."
+ introduced "12.6"
def initialize(name, run_context = nil)
super
diff --git a/lib/chef/resource/launchd.rb b/lib/chef/resource/launchd.rb
index f3c378a6a8..9161efa6ea 100644
--- a/lib/chef/resource/launchd.rb
+++ b/lib/chef/resource/launchd.rb
@@ -17,35 +17,77 @@
#
require "chef/resource"
-require "chef/provider/launchd"
class Chef
class Resource
class Launchd < Chef::Resource
- provides :launchd, os: "darwin"
+ resource_name :launchd
+ provides :launchd
- identity_attr :label
+ description "Use the launchd resource to manage system-wide services (daemons) and per-user services (agents) on the macOS platform."
+ introduced "12.8"
default_action :create
- allowed_actions :create, :create_if_missing, :delete, :enable, :disable
+ allowed_actions :create, :create_if_missing, :delete, :enable, :disable, :restart
- def initialize(name, run_context = nil)
- super
- provider = Chef::Provider::Launchd
- resource_name = :launchd
- end
-
- property :label, String, default: lazy { name }, identity: true
- property :backup, [Integer, FalseClass]
- property :cookbook, String
+ property :label, String, identity: true, name_property: true
+ property :backup, [Integer, FalseClass], desired_state: false
+ property :cookbook, String, desired_state: false
property :group, [String, Integer]
- property :hash, Hash
+ property :plist_hash, Hash
property :mode, [String, Integer]
property :owner, [String, Integer]
property :path, String
property :source, String
property :session_type, String
+ # StartCalendarInterval has some gotchas so we coerce it to help sanity
+ # check. According to `man 5 launchd.plist`:
+ # StartCalendarInterval <dictionary of integers or array of dictionaries of integers>
+ # ... Missing arguments are considered to be wildcard.
+ # What the man page doesn't state, but what was observed (OSX 10.11.5, launchctrl v3.4.0)
+ # Is that keys that are specified, but invalid, will also be treated as a wildcard
+ # this means that an entry like:
+ # { "Hour"=>0, "Weekday"=>"6-7"}
+ # will not just run on midnight of Sat and Sun, rather it will run _every_ midnight.
+ property :start_calendar_interval, [Hash, Array], coerce: proc { |type|
+ # Coerce into an array of hashes to make validation easier
+ array = if type.is_a?(Array)
+ type
+ else
+ [type]
+ end
+
+ # Check to make sure that our array only has hashes
+ unless array.all? { |obj| obj.is_a?(Hash) }
+ error_msg = "start_calendar_interval must be a single hash or an array of hashes!"
+ raise Chef::Exceptions::ValidationFailed, error_msg
+ end
+
+ # Make sure the hashes don't have any incorrect keys/values
+ array.each do |entry|
+ allowed_keys = %w{Minute Hour Day Weekday Month}
+ unless entry.keys.all? { |key| allowed_keys.include?(key) }
+ failed_keys = entry.keys.reject { |k| allowed_keys.include?(k) }.join(", ")
+ error_msg = "The following key(s): #{failed_keys} are invalid for start_calendar_interval, must be one of: #{allowed_keys.join(", ")}"
+ raise Chef::Exceptions::ValidationFailed, error_msg
+ end
+
+ unless entry.values.all? { |val| val.is_a?(Integer) }
+ failed_values = entry.values.reject { |val| val.is_a?(Integer) }.join(", ")
+ error_msg = "Invalid value(s) (#{failed_values}) for start_calendar_interval item. Values must be integers!"
+ raise Chef::Exceptions::ValidationFailed, error_msg
+ end
+ end
+
+ # Don't return array if we only have one entry
+ if array.size == 1
+ array.first
+ else
+ array
+ end
+ }
+
property :type, String, default: "daemon", coerce: proc { |type|
type = type ? type.downcase : "daemon"
types = %w{daemon agent}
@@ -68,12 +110,12 @@ class Chef
property :hard_resource_limits, Hash
property :inetd_compatibility, Hash
property :init_groups, [ TrueClass, FalseClass ]
- property :keep_alive, [ TrueClass, FalseClass ]
+ property :keep_alive, [ TrueClass, FalseClass, Hash ]
property :launch_only_once, [ TrueClass, FalseClass ]
property :ld_group, String
property :limit_load_from_hosts, Array
property :limit_load_to_hosts, Array
- property :limit_load_to_session_type, String
+ property :limit_load_to_session_type, [ Array, String ]
property :low_priority_io, [ TrueClass, FalseClass ]
property :mach_services, Hash
property :nice, Integer
@@ -89,7 +131,6 @@ class Chef
property :standard_error_path, String
property :standard_in_path, String
property :standard_out_path, String
- property :start_calendar_interval, Hash
property :start_interval, Integer
property :start_on_mount, [ TrueClass, FalseClass ]
property :throttle_interval, Integer
diff --git a/lib/chef/resource/link.rb b/lib/chef/resource/link.rb
index 5717ec7bad..487befde2e 100644
--- a/lib/chef/resource/link.rb
+++ b/lib/chef/resource/link.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,10 +24,20 @@ class Chef
class Resource
class Link < Chef::Resource
include Chef::Mixin::Securable
+ resource_name :link
+ provides :link
- identity_attr :target_file
+ description "Use the link resource to create symbolic or hard links.\n\n"\
+ "A symbolic link—sometimes referred to as a soft link—is a directory entry"\
+ " that associates a file name with a string that contains an absolute or"\
+ " relative path to a file on any file system. In other words, “a file that"\
+ " contains a path that points to another file.” A symbolic link creates a new"\
+ " file with a new inode that points to the inode location of the original file.\n\n"\
+ "A hard link is a directory entry that associates a file with another file in the"\
+ " same file system. In other words, “multiple directory entries to the same file.”"\
+ " A hard link creates a new file that points to the same inode as the original file."
- state_attrs :to, :owner, :group
+ state_attrs :owner # required since it's not a property below
default_action :create
allowed_actions :create, :delete
@@ -35,51 +45,27 @@ class Chef
def initialize(name, run_context = nil)
verify_links_supported!
super
- @to = nil
- @link_type = :symbolic
- @target_file = name
end
- def to(arg = nil)
- set_or_return(
- :to,
- arg,
- :kind_of => String
- )
- end
+ property :target_file, String,
+ description: "The name of the link. Default value: the name of the resource block See “Syntax” section above for more information.",
+ name_property: true, identity: true
- def target_file(arg = nil)
- set_or_return(
- :target_file,
- arg,
- :kind_of => String
- )
- end
+ property :to, String,
+ description: "The actual file to which the link is to be created."
- def link_type(arg = nil)
- real_arg = arg.kind_of?(String) ? arg.to_sym : arg
- set_or_return(
- :link_type,
- real_arg,
- :equal_to => [ :symbolic, :hard ]
- )
- end
+ property :link_type, [String, Symbol],
+ description: "The type of link: :symbolic or :hard.",
+ coerce: proc { |arg| arg.kind_of?(String) ? arg.to_sym : arg },
+ equal_to: [ :symbolic, :hard ], default: :symbolic
- def group(arg = nil)
- set_or_return(
- :group,
- arg,
- :regex => Chef::Config[:group_valid_regex]
- )
- end
+ property :group, [String, Integer],
+ description: "A string or ID that identifies the group associated with a symbolic link.",
+ regex: [Chef::Config[:group_valid_regex]]
- def owner(arg = nil)
- set_or_return(
- :owner,
- arg,
- :regex => Chef::Config[:user_valid_regex]
- )
- end
+ property :owner, [String, Integer],
+ description: "The owner associated with a symbolic link.",
+ regex: [Chef::Config[:user_valid_regex]]
# make link quack like a file (XXX: not for public consumption)
def path
diff --git a/lib/chef/resource/log.rb b/lib/chef/resource/log.rb
index 8f7879872f..a9dea6b104 100644
--- a/lib/chef/resource/log.rb
+++ b/lib/chef/resource/log.rb
@@ -18,59 +18,30 @@
#
require "chef/resource"
-require "chef/provider/log"
class Chef
class Resource
+ # @example logging at default info level
+ # log "your string to log"
+ #
+ # @example logging at specified debug level
+ # log "a debug string" do
+ # level :debug
+ # end
class Log < Chef::Resource
+ resource_name :log
- identity_attr :message
+ description "Use the log resource to create log entries. The log resource behaves"\
+ " like any other resource: built into the resource collection during the"\
+ " compile phase, and then run during the execution phase. (To create a log"\
+ " entry that is not built into the resource collection, use Chef::Log instead"\
+ " of the log resource.)"
- default_action :write
-
- # Sends a string from a recipe to a log provider
- #
- # log "some string to log" do
- # level :info # (default) also supports :warn, :debug, and :error
- # end
- #
- # === Example
- # log "your string to log"
- #
- # or
- #
- # log "a debug string" { level :debug }
- #
-
- # Initialize log resource with a name as the string to log
- #
- # === Parameters
- # name<String>:: Message to log
- # collection<Array>:: Collection of included recipes
- # node<Chef::Node>:: Node where resource will be used
- def initialize(name, run_context = nil)
- super
- @level = :info
- @message = name
- end
-
- def message(arg = nil)
- set_or_return(
- :message,
- arg,
- :kind_of => String
- )
- end
-
- # <Symbol> Log level, one of :debug, :info, :warn, :error or :fatal
- def level(arg = nil)
- set_or_return(
- :level,
- arg,
- :equal_to => [ :debug, :info, :warn, :error, :fatal ]
- )
- end
+ property :message, String, name_property: true, identity: true
+ property :level, Symbol, equal_to: [ :debug, :info, :warn, :error, :fatal ], default: :info
+ allowed_actions :write
+ default_action :write
end
end
end
diff --git a/lib/chef/resource/lwrp_base.rb b/lib/chef/resource/lwrp_base.rb
index 7dfe147341..c0f6f835ad 100644
--- a/lib/chef/resource/lwrp_base.rb
+++ b/lib/chef/resource/lwrp_base.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -41,11 +41,9 @@ class Chef
include Chef::Mixin::ConvertToClassName
include Chef::Mixin::FromFile
- attr_accessor :loaded_lwrps
-
def build_from_file(cookbook_name, filename, run_context)
if LWRPBase.loaded_lwrps[filename]
- Chef::Log.debug("Custom resource #{filename} from cookbook #{cookbook_name} has already been loaded! Skipping the reload.")
+ Chef::Log.trace("Custom resource #{filename} from cookbook #{cookbook_name} has already been loaded! Skipping the reload.")
return loaded_lwrps[filename]
end
@@ -65,12 +63,10 @@ class Chef
define_singleton_method(:inspect) { to_s }
end
- Chef::Log.debug("Loaded contents of #{filename} into resource #{resource_name} (#{resource_class})")
+ Chef::Log.trace("Loaded contents of #{filename} into resource #{resource_name} (#{resource_class})")
LWRPBase.loaded_lwrps[filename] = true
- # Create the deprecated Chef::Resource::LwrpFoo class
- Chef::Resource.register_deprecated_lwrp_class(resource_class, convert_to_class_name(resource_name))
resource_class
end
@@ -104,6 +100,7 @@ class Chef
protected
+ attr_writer :loaded_lwrps
def loaded_lwrps
@loaded_lwrps ||= {}
end
diff --git a/lib/chef/resource/macos_userdefaults.rb b/lib/chef/resource/macos_userdefaults.rb
new file mode 100644
index 0000000000..3cf44eb161
--- /dev/null
+++ b/lib/chef/resource/macos_userdefaults.rb
@@ -0,0 +1,133 @@
+#
+# Copyright:: 2011-2018, Joshua Timberman
+# Copyright:: 2018, Chef Software, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class MacosUserDefaults < Chef::Resource
+ # align with apple's marketing department
+ resource_name :macos_userdefaults
+ provides(:mac_os_x_userdefaults) { true }
+ provides(:macos_userdefaults) { true }
+
+ description "Use the macos_userdefaults resource to manage the macOS user defaults system. The properties"\
+ " of this resource are passed to the defaults command, and the parameters follow the convention"\
+ " of that command. See the defaults(1) man page for details on how the tool works."
+ introduced "14.0"
+
+ property :domain, String,
+ description: "The domain the defaults belong to.",
+ required: true
+
+ property :global, [TrueClass, FalseClass],
+ description: "Whether the domain is global.",
+ default: false
+
+ property :key, String,
+ description: "The preference key."
+
+ property :value, [Integer, Float, String, TrueClass, FalseClass, Hash, Array],
+ description: "The value of the key.",
+ required: true
+
+ property :type, String,
+ description: "Value type of the preference key.",
+ default: ""
+
+ property :user, String,
+ description: "User for which to set the default."
+
+ property :sudo, [TrueClass, FalseClass],
+ description: "Set to true if the setting requires privileged access to modify.",
+ default: false,
+ desired_state: false
+
+ property :is_set, [TrueClass, FalseClass],
+ description: "",
+ default: false,
+ desired_state: false
+
+ # coerce various ways of representing a boolean into either 0 (false) or 1 (true)
+ # which is what the defaults CLI expects. Why? Well defaults itself accepts a few
+ # different formats, but when you do a read command it all comes back as 1 or 0.
+ def coerce_booleans(val)
+ return 1 if [true, "TRUE", "1", "true", "YES", "yes"].include?(val)
+ return 0 if [false, "FALSE", "0", "false", "NO", "no"].include?(val)
+ val
+ end
+
+ load_current_value do |desired|
+ value = coerce_booleans(desired.value)
+ drcmd = "defaults read '#{desired.domain}' "
+ drcmd << "'#{desired.key}' " if desired.key
+ shell_out_opts = {}
+ shell_out_opts[:user] = desired.user unless desired.user.nil?
+ vc = shell_out("#{drcmd} | grep -qx '#{value}'", shell_out_opts)
+ is_set vc.exitstatus == 0 ? true : false
+ end
+
+ action :write do
+ description "Write the setting to the specified domain"
+
+ unless current_resource.is_set
+ cmd = ["defaults write"]
+ cmd.unshift("sudo") if new_resource.sudo
+
+ cmd << if new_resource.global
+ "NSGlobalDomain"
+ else
+ "'#{new_resource.domain}'"
+ end
+
+ cmd << "'#{new_resource.key}'" if new_resource.key
+ value = new_resource.value
+ type = new_resource.type.empty? ? value_type(value) : new_resource.type
+ # creates a string of Key1 Value1 Key2 Value2...
+ value = value.map { |k, v| "\"#{k}\" \"#{v}\"" }.join(" ") if type == "dict"
+ if type == "array"
+ value = value.join("' '")
+ value = "'#{value}'"
+ end
+ cmd << "-#{type}" if type
+ cmd << value
+
+ declare_resource(:execute, cmd.join(" ")) do
+ user new_resource.user unless new_resource.user.nil?
+ end
+ end
+ end
+
+ action_class do
+ def value_type(value)
+ case value
+ when true, false
+ "bool"
+ when Integer
+ "int"
+ when Float
+ "float"
+ when Hash
+ "dict"
+ when Array
+ "array"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/macosx_service.rb b/lib/chef/resource/macosx_service.rb
index 08c748bead..8fa8fd7a36 100644
--- a/lib/chef/resource/macosx_service.rb
+++ b/lib/chef/resource/macosx_service.rb
@@ -21,37 +21,21 @@ require "chef/resource/service"
class Chef
class Resource
class MacosxService < Chef::Resource::Service
-
- provides :macosx_service, os: "darwin"
+ resource_name :macosx_service
+ provides :macosx_service
provides :service, os: "darwin"
+ description "Use the macosx_service resource to manage services on the macOS platform."
+
identity_attr :service_name
state_attrs :enabled, :running
- def initialize(name, run_context = nil)
- super
- @plist = nil
- @session_type = nil
- end
-
# This will enable user to pass a plist in the case
# that the filename and label for the service dont match
- def plist(arg = nil)
- set_or_return(
- :plist,
- arg,
- :kind_of => String
- )
- end
-
- def session_type(arg = nil)
- set_or_return(
- :session_type,
- arg,
- :kind_of => String
- )
- end
+ property :plist, String
+
+ property :session_type, String
end
end
diff --git a/lib/chef/resource/macports_package.rb b/lib/chef/resource/macports_package.rb
index 3685334c17..254d7e7c5f 100644
--- a/lib/chef/resource/macports_package.rb
+++ b/lib/chef/resource/macports_package.rb
@@ -22,6 +22,8 @@ class Chef
class Resource
class MacportsPackage < Chef::Resource::Package
resource_name :macports_package
+
+ description "Use the macports_package resource to manage packages for the macOS platform."
end
end
end
diff --git a/lib/chef/resource/mdadm.rb b/lib/chef/resource/mdadm.rb
index df6e705f15..3b54a53e98 100644
--- a/lib/chef/resource/mdadm.rb
+++ b/lib/chef/resource/mdadm.rb
@@ -22,91 +22,24 @@ require "chef/resource"
class Chef
class Resource
class Mdadm < Chef::Resource
+ resource_name :mdadm
- identity_attr :raid_device
-
- state_attrs :devices, :level, :chunk
+ description "Use the mdadm resource to manage RAID devices in a Linux environment using the mdadm utility. The mdadm resource"\
+ " will create and assemble an array, but it will not create the config file that is used to persist the array upon"\
+ " reboot. If the config file is required, it must be done by specifying a template with the correct array layout,"\
+ " and then by using the mount provider to create a file systems table (fstab) entry."
default_action :create
allowed_actions :create, :assemble, :stop
- def initialize(name, run_context = nil)
- super
-
- @chunk = 16
- @devices = []
- @exists = false
- @level = 1
- @metadata = "0.90"
- @bitmap = nil
- @raid_device = name
- @layout = nil
- end
-
- def chunk(arg = nil)
- set_or_return(
- :chunk,
- arg,
- :kind_of => [ Integer ]
- )
- end
-
- def devices(arg = nil)
- set_or_return(
- :devices,
- arg,
- :kind_of => [ Array ]
- )
- end
-
- def exists(arg = nil)
- set_or_return(
- :exists,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def level(arg = nil)
- set_or_return(
- :level,
- arg,
- :kind_of => [ Integer ]
- )
- end
-
- def metadata(arg = nil)
- set_or_return(
- :metadata,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def bitmap(arg = nil)
- set_or_return(
- :bitmap,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def raid_device(arg = nil)
- set_or_return(
- :raid_device,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def layout(arg = nil)
- set_or_return(
- :layout,
- arg,
- :kind_of => [ String ]
- )
- end
-
+ property :chunk, Integer, default: 16
+ property :devices, Array, default: lazy { [] }
+ property :exists, [ TrueClass, FalseClass ], default: false
+ property :level, Integer, default: 1
+ property :metadata, String, default: "0.90"
+ property :bitmap, String
+ property :raid_device, String, identity: true, name_property: true
+ property :layout, String
end
end
end
diff --git a/lib/chef/resource/mount.rb b/lib/chef/resource/mount.rb
index 2d85b3897c..dffb96cb53 100644
--- a/lib/chef/resource/mount.rb
+++ b/lib/chef/resource/mount.rb
@@ -1,7 +1,7 @@
#
# Author:: Joshua Timberman (<joshua@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,157 +22,39 @@ require "chef/resource"
class Chef
class Resource
class Mount < Chef::Resource
-
- identity_attr :device
-
- state_attrs :mount_point, :device_type, :fstype, :username, :password, :domain
+ description "Use the mount resource to manage a mounted file system."
default_action :mount
- allowed_actions :mount, :umount, :remount, :enable, :disable
-
- def initialize(name, run_context = nil)
- super
- @mount_point = name
- @device = nil
- @device_type = :device
- @fsck_device = "-"
- @fstype = "auto"
- @options = ["defaults"]
- @dump = 0
- @pass = 2
- @mounted = false
- @enabled = false
- @supports = { :remount => false }
- @username = nil
- @password = nil
- @domain = nil
- end
-
- def mount_point(arg = nil)
- set_or_return(
- :mount_point,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def device(arg = nil)
- set_or_return(
- :device,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def device_type(arg = nil)
- real_arg = arg.kind_of?(String) ? arg.to_sym : arg
- valid_devices = if RUBY_PLATFORM =~ /solaris/i
- [ :device ]
- else
- [ :device, :label, :uuid ]
- end
- set_or_return(
- :device_type,
- real_arg,
- :equal_to => valid_devices
- )
- end
-
- def fsck_device(arg = nil)
- set_or_return(
- :fsck_device,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def fstype(arg = nil)
- set_or_return(
- :fstype,
- arg,
- :kind_of => [ String ]
- )
- end
+ allowed_actions :mount, :umount, :unmount, :remount, :enable, :disable
- def options(arg = nil)
- ret = set_or_return(
- :options,
- arg,
- :kind_of => [ Array, String ]
- )
+ # this is a poor API please do not re-use this pattern
+ property :supports, Hash,
+ default: lazy { { remount: false } },
+ coerce: proc { |x| x.is_a?(Array) ? x.each_with_object({}) { |i, m| m[i] = true } : x }
- if ret.is_a? String
- ret.tr(",", " ").split(/ /)
- else
- ret
- end
- end
+ property :password, String, sensitive: true
- def dump(arg = nil)
- set_or_return(
- :dump,
- arg,
- :kind_of => [ Integer, FalseClass ]
- )
- end
+ property :mount_point, String, name_property: true
+ property :device, String, identity: true
- def pass(arg = nil)
- set_or_return(
- :pass,
- arg,
- :kind_of => [ Integer, FalseClass ]
- )
- end
+ property :device_type, [String, Symbol],
+ coerce: proc { |arg| arg.kind_of?(String) ? arg.to_sym : arg },
+ default: :device,
+ equal_to: RUBY_PLATFORM =~ /solaris/i ? %i{ device } : %i{ device label uuid }
- def mounted(arg = nil)
- set_or_return(
- :mounted,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :fsck_device, String, default: "-"
+ property :fstype, [String, nil], default: "auto"
- def enabled(arg = nil)
- set_or_return(
- :enabled,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :options, [Array, String, nil],
+ coerce: proc { |arg| arg.kind_of?(String) ? arg.split(",") : arg },
+ default: %w{defaults}
- def supports(args = {})
- if args.is_a? Array
- args.each { |arg| @supports[arg] = true }
- elsif args.any?
- @supports = args
- else
- @supports
- end
- end
-
- def username(arg = nil)
- set_or_return(
- :username,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def password(arg = nil)
- set_or_return(
- :password,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def domain(arg = nil)
- set_or_return(
- :domain,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :dump, [Integer, FalseClass], default: 0
+ property :pass, [Integer, FalseClass], default: 2
+ property :mounted, [TrueClass, FalseClass], default: false
+ property :enabled, [TrueClass, FalseClass], default: false
+ property :username, String
+ property :domain, String
private
diff --git a/lib/chef/resource/msu_package.rb b/lib/chef/resource/msu_package.rb
new file mode 100644
index 0000000000..85ab170ee0
--- /dev/null
+++ b/lib/chef/resource/msu_package.rb
@@ -0,0 +1,46 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource/package"
+require "chef/mixin/uris"
+
+class Chef
+ class Resource
+ class MsuPackage < Chef::Resource::Package
+ include Chef::Mixin::Uris
+
+ resource_name :msu_package
+ provides :msu_package
+
+ description "Use the msu_package resource to install Microsoft Update(MSU) packages on Microsoft Windows machines."
+ introduced "12.17"
+
+ allowed_actions :install, :remove
+ default_action :install
+
+ property :source, String,
+ coerce: (proc do |s|
+ unless s.nil?
+ uri_scheme?(s) ? s : Chef::Util::PathHelper.canonical_path(s, false)
+ end
+ end),
+ default: lazy { |r| r.package_name }
+ property :checksum, String, desired_state: false
+ end
+ end
+end
diff --git a/lib/chef/resource/ohai.rb b/lib/chef/resource/ohai.rb
index 09cd22efc5..a84d521333 100644
--- a/lib/chef/resource/ohai.rb
+++ b/lib/chef/resource/ohai.rb
@@ -20,34 +20,18 @@
class Chef
class Resource
class Ohai < Chef::Resource
+ resource_name :ohai
+ provides :ohai
- identity_attr :name
+ description "Use the ohai resource to reload the Ohai configuration on a node."\
+ " This allows recipes that change system attributes (like a recipe"\
+ " that adds a user) to refer to those attributes later on during the chef-client run."
- state_attrs :plugin
+ property :ohai_name, name_property: true
+ property :plugin, String
default_action :reload
-
- def initialize(name, run_context = nil)
- super
- @name = name
- @plugin = nil
- end
-
- def plugin(arg = nil)
- set_or_return(
- :plugin,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def name(arg = nil)
- set_or_return(
- :name,
- arg,
- :kind_of => [ String ]
- )
- end
+ allowed_actions :reload
end
end
end
diff --git a/lib/chef/resource/ohai_hint.rb b/lib/chef/resource/ohai_hint.rb
new file mode 100644
index 0000000000..6eb36aea13
--- /dev/null
+++ b/lib/chef/resource/ohai_hint.rb
@@ -0,0 +1,93 @@
+#
+# Copyright:: Copyright 2011-2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ class Resource
+ class OhaiHint < Chef::Resource
+ resource_name :ohai_hint
+ provides(:ohai_hint) { true }
+
+ description "Use the ohai_hint resource to pass hint data to Ohai to aid in configuration detection."
+ introduced "14.0"
+
+ property :hint_name, String,
+ description: "The name of hints file if different from the resource name.",
+ name_property: true
+
+ property :content, Hash,
+ description: "Values to include in the hint file."
+
+ property :compile_time, [TrueClass, FalseClass],
+ description: "Should the resource execute during the compile time phase",
+ default: true, desired_state: false
+
+ action :create do
+ description "Create an Ohai hint file"
+
+ declare_resource(:directory, ::Ohai::Config.ohai.hints_path.first) do
+ action :create
+ recursive true
+ end
+
+ declare_resource(:file, ohai_hint_file_path(new_resource.hint_name)) do
+ action :create
+ content format_content(new_resource.content)
+ end
+ end
+
+ action :delete do
+ description "Delete an Ohai hint file"
+
+ declare_resource(:file, ohai_hint_file_path(new_resource.hint_name)) do
+ action :delete
+ notifies :reload, ohai[reload ohai post hint removal]
+ end
+
+ declare_resource(:ohai, "reload ohai post hint removal") do
+ action :nothing
+ end
+ end
+
+ action_class do
+ # given a hint filename return the platform specific hint file path
+ # @param filename [String] the name of the hint file
+ # @return [String] absolute path to the file
+ def ohai_hint_file_path(filename)
+ path = ::File.join(::Ohai::Config.ohai.hints_path.first, filename)
+ path << ".json" unless path.end_with?(".json")
+ path
+ end
+
+ # format content hash as JSON
+ # @param content [Hash] the content of the hint file
+ # @return [JSON] json representation of the content of an empty string if content was nil
+ def format_content(content)
+ return "" if content.nil? || content.empty?
+ JSON.pretty_generate(content)
+ end
+ end
+
+ # this resource forces itself to run at compile_time
+ def after_created
+ return unless compile_time
+ Array(action).each do |action|
+ run_action(action)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/openbsd_package.rb b/lib/chef/resource/openbsd_package.rb
index d0f9fe877f..85109de9e8 100644
--- a/lib/chef/resource/openbsd_package.rb
+++ b/lib/chef/resource/openbsd_package.rb
@@ -30,6 +30,9 @@ class Chef
resource_name :openbsd_package
provides :package, os: "openbsd"
+
+ description "Use the openbsd_package resource to manage packages for the OpenBSD platform."
+ introduced "12.1"
end
end
end
diff --git a/lib/chef/resource/openssl_dhparam.rb b/lib/chef/resource/openssl_dhparam.rb
new file mode 100644
index 0000000000..ec98237a1b
--- /dev/null
+++ b/lib/chef/resource/openssl_dhparam.rb
@@ -0,0 +1,81 @@
+#
+# Copyright:: Copyright 2009-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class OpensslDhparam < Chef::Resource
+ require "chef/mixin/openssl_helper"
+ include Chef::Mixin::OpenSSLHelper
+
+ resource_name :openssl_dhparam
+ provides(:openssl_dhparam) { true }
+
+ description "Use the openssl_dhparam resource to generate dhparam.pem files. If a"\
+ " valid dhparam.pem file is found at the specified location, no new file"\
+ " will be created. If a file is found at the specified location but it is"\
+ " not a valid dhparam file, it will be overwritten."
+ introduced "14.0"
+
+ property :path, String,
+ description: "The path to write the file to if it's different than the resource name.",
+ name_property: true
+
+ property :key_length, Integer,
+ equal_to: [1024, 2048, 4096, 8192],
+ validation_message: "key_length must be 1024, 2048, 4096, or 8192.",
+ description: "The desired bit length of the generated key.",
+ default: 2048
+
+ property :generator, Integer,
+ equal_to: [2, 5],
+ validation_message: "generator must be either 2 or 5.",
+ description: "The desired Diffie-Hellmann generator.",
+ default: 2
+
+ property :owner, [String, nil],
+ description: "The owner of all files created by the resource."
+
+ property :group, [String, nil],
+ description: "The group of all files created by the resource."
+
+ property :mode, [Integer, String],
+ description: "The permission mode of all files created by the resource.",
+ default: "0640"
+
+ action :create do
+ description "Create the dhparam file"
+
+ unless dhparam_pem_valid?(new_resource.path)
+ converge_by("Create a dhparam file #{new_resource.path}") do
+ dhparam_content = gen_dhparam(new_resource.key_length, new_resource.generator).to_pem
+
+ declare_resource(:file, new_resource.path) do
+ action :create
+ owner new_resource.owner unless new_resource.owner.nil?
+ group new_resource.group unless new_resource.group.nil?
+ mode new_resource.mode
+ sensitive true
+ content dhparam_content
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/openssl_rsa_private_key.rb b/lib/chef/resource/openssl_rsa_private_key.rb
new file mode 100644
index 0000000000..be4c85bcbb
--- /dev/null
+++ b/lib/chef/resource/openssl_rsa_private_key.rb
@@ -0,0 +1,93 @@
+#
+# Copyright:: Copyright 2009-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class OpensslRsaPrivateKey < Chef::Resource
+ require "chef/mixin/openssl_helper"
+ include Chef::Mixin::OpenSSLHelper
+
+ resource_name :openssl_rsa_private_key
+ provides(:openssl_rsa_private_key) { true }
+ provides(:openssl_rsa_key) { true } # legacy cookbook resource name
+
+ description "Use the openssl_rsa_private_key resource to generate RSA private key files."\
+ " If a valid RSA key file can be opened at the specified location, no new file"\
+ " will be created. If the RSA key file cannot be opened, either because it does"\
+ " not exist or because the password to the RSA key file does not match the"\
+ " password in the recipe, it will be overwritten."
+ introduced "14.0"
+
+ property :path, String,
+ description: "The path to write the file to it's different than the resource name.",
+ name_property: true
+
+ property :key_length, Integer,
+ equal_to: [1024, 2048, 4096, 8192],
+ validation_message: "key_length (bits) must be 1024, 2048, 4096, or 8192.",
+ description: "The desired bit length of the generated key.",
+ default: 2048
+
+ property :key_pass, String,
+ description: "The desired passphrase for the key."
+
+ property :key_cipher, String,
+ equal_to: OpenSSL::Cipher.ciphers,
+ validation_message: "key_cipher must be a cipher known to openssl. Run `openssl list-cipher-algorithms` to see available options.",
+ description: "The designed cipher to use when generating your key. Run `openssl list-cipher-algorithms` to see available options.",
+ default: "des3"
+
+ property :owner, [String, nil],
+ description: "The owner of all files created by the resource."
+
+ property :group, [String, nil],
+ description: "The group of all files created by the resource."
+
+ property :mode, [Integer, String],
+ description: "The permission mode of all files created by the resource.",
+ default: "0600"
+
+ property :force, [TrueClass, FalseClass],
+ description: "Force creating the key even if the existing key exists.",
+ default: false, desired_state: false
+
+ action :create do
+ return if new_resource.force || priv_key_file_valid?(new_resource.path, new_resource.key_pass)
+
+ converge_by("create #{new_resource.key_length} bit RSA key #{new_resource.path}") do
+ if new_resource.key_pass
+ unencrypted_rsa_key = gen_rsa_priv_key(new_resource.key_length)
+ rsa_key_content = encrypt_rsa_key(unencrypted_rsa_key, new_resource.key_pass, new_resource.key_cipher)
+ else
+ rsa_key_content = gen_rsa_priv_key(new_resource.key_length).to_pem
+ end
+
+ declare_resource(:file, new_resource.path) do
+ action :create
+ owner new_resource.owner unless new_resource.owner.nil?
+ group new_resource.group unless new_resource.group.nil?
+ mode new_resource.mode
+ sensitive true
+ content rsa_key_content
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/openssl_rsa_public_key.rb b/lib/chef/resource/openssl_rsa_public_key.rb
new file mode 100644
index 0000000000..fa00404999
--- /dev/null
+++ b/lib/chef/resource/openssl_rsa_public_key.rb
@@ -0,0 +1,74 @@
+#
+# Copyright:: Copyright 2009-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class OpensslRsaPublicKey < Chef::Resource
+ require "chef/mixin/openssl_helper"
+ include Chef::Mixin::OpenSSLHelper
+
+ resource_name :openssl_rsa_public_key
+ provides(:openssl_rsa_public_key) { true }
+
+ description "Use the openssl_rsa_public_key resource to generate RSA public key files given a RSA private key"
+ introduced "14.0"
+
+ property :path, String,
+ description: "The path to write the file to if different than the resource's name.",
+ name_property: true
+
+ property :private_key_path, String,
+ description: "The path to the private key."
+
+ property :private_key_content, String,
+ description: "The content of the private key including new lines. Used instead of private_key_path to avoid having to first write a key to disk."
+
+ property :private_key_pass, String,
+ description: "The passphrase of the provided private key."
+
+ property :owner, [String, nil],
+ description: "The owner of all files created by the resource."
+
+ property :group, [String, nil],
+ description: "The group of all files created by the resource."
+
+ property :mode, [Integer, String],
+ description: "The permission mode of all files created by the resource.",
+ default: "0640"
+
+ action :create do
+ description "Create the RSA public key."
+
+ raise ArgumentError, "You cannot specify both 'private_key_path' and 'private_key_content' properties at the same time." if new_resource.private_key_path && new_resource.private_key_content
+ raise ArgumentError, "You must specify the private key with either 'private_key_path' or 'private_key_content' properties." unless new_resource.private_key_path || new_resource.private_key_content
+ raise "#{new_resource.private_key_path} not a valid private RSA key or password is invalid" unless priv_key_file_valid?((new_resource.private_key_path || new_resource.private_key_content), new_resource.private_key_pass)
+
+ rsa_key_content = gen_rsa_pub_key((new_resource.private_key_path || new_resource.private_key_content), new_resource.private_key_pass)
+
+ declare_resource(:file, new_resource.path) do
+ action :create
+ owner new_resource.owner unless new_resource.owner.nil?
+ group new_resource.group unless new_resource.group.nil?
+ mode new_resource.mode
+ content rsa_key_content
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/osx_profile.rb b/lib/chef/resource/osx_profile.rb
index 8142e1fd96..612ff637e4 100644
--- a/lib/chef/resource/osx_profile.rb
+++ b/lib/chef/resource/osx_profile.rb
@@ -21,54 +21,23 @@ require "chef/resource"
class Chef
class Resource
class OsxProfile < Chef::Resource
- provides :osx_profile, os: "darwin"
- provides :osx_config_profile, os: "darwin"
+ resource_name :osx_profile
+ provides :osx_profile
+ provides :osx_config_profile
- identity_attr :profile_name
+ description "Use the osx_profile resource to manage configuration profiles (.mobileconfig files)"\
+ " on the macOS platform. The osx_profile resource installs profiles by using"\
+ " the uuidgen library to generate a unique ProfileUUID, and then using the"\
+ " profiles command to install the profile on the system."
+ introduced "12.7"
default_action :install
allowed_actions :install, :remove
- def initialize(name, run_context = nil)
- super
- @profile_name = name
- @profile = nil
- @identifier = nil
- @path = nil
- end
-
- def profile_name(arg = nil)
- set_or_return(
- :profile_name,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def profile(arg = nil)
- set_or_return(
- :profile,
- arg,
- :kind_of => [ String, Hash ]
- )
- end
-
- def identifier(arg = nil)
- set_or_return(
- :identifier,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def path(arg = nil)
- set_or_return(
- :path,
- arg,
- :kind_of => [ String ]
- )
- end
-
+ property :profile_name, String, name_property: true, identity: true
+ property :profile, [ String, Hash ]
+ property :identifier, String
+ property :path, String
end
end
end
diff --git a/lib/chef/resource/package.rb b/lib/chef/resource/package.rb
index 32339e1a24..8362dde47e 100644
--- a/lib/chef/resource/package.rb
+++ b/lib/chef/resource/package.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,8 +24,16 @@ class Chef
class Package < Chef::Resource
resource_name :package
+ description "Use the package resource to manage packages. When the package is"\
+ " installed from a local file (such as with RubyGems, dpkg, or RPM"\
+ " Package Manager), the file must be added to the node using the remote_file"\
+ " or cookbook_file resources.\n\nThis resource is the base resource for"\
+ " several other resources used for package management on specific platforms."\
+ " While it is possible to use each of these specific resources, it is"\
+ " recommended to use the package resource as often as possible."
+
default_action :install
- allowed_actions :install, :upgrade, :remove, :purge, :reconfig
+ allowed_actions :install, :upgrade, :remove, :purge, :reconfig, :lock, :unlock
def initialize(name, *args)
# We capture name here, before it gets coerced to name
@@ -36,7 +44,7 @@ class Chef
property :package_name, [ String, Array ], identity: true
property :version, [ String, Array ]
- property :options, String
+ property :options, [ String, Array ], coerce: proc { |x| x.is_a?(String) ? x.shellsplit : x }
property :response_file, String, desired_state: false
property :response_file_variables, Hash, default: lazy { {} }, desired_state: false
property :source, String, desired_state: false
diff --git a/lib/chef/resource/pacman_package.rb b/lib/chef/resource/pacman_package.rb
index 66b39d164d..4661b6f07d 100644
--- a/lib/chef/resource/pacman_package.rb
+++ b/lib/chef/resource/pacman_package.rb
@@ -22,7 +22,9 @@ class Chef
class Resource
class PacmanPackage < Chef::Resource::Package
resource_name :pacman_package
- provides :pacman_package, os: "linux"
+ provides :pacman_package
+
+ description "Use the pacman_package resource to manage packages (using pacman) on the Arch Linux platform."
end
end
end
diff --git a/lib/chef/resource/paludis_package.rb b/lib/chef/resource/paludis_package.rb
index 31c0f31b8c..15378cd2e5 100644
--- a/lib/chef/resource/paludis_package.rb
+++ b/lib/chef/resource/paludis_package.rb
@@ -23,7 +23,10 @@ class Chef
class Resource
class PaludisPackage < Chef::Resource::Package
resource_name :paludis_package
- provides :paludis_package, os: "linux"
+ provides :paludis_package
+
+ description "Use the paludis_package resource to manage packages for the Paludis platform."
+ introduced "12.1"
allowed_actions :install, :remove, :upgrade
diff --git a/lib/chef/resource/perl.rb b/lib/chef/resource/perl.rb
index 60af0e92da..f3079eb40e 100644
--- a/lib/chef/resource/perl.rb
+++ b/lib/chef/resource/perl.rb
@@ -27,6 +27,12 @@ class Chef
@interpreter = "perl"
end
+ description "Use the perl resource to execute scripts using the Perl interpreter."\
+ " This resource may also use any of the actions and properties that are"\
+ " available to the execute resource. Commands that are executed with this"\
+ " resource are (by their nature) not idempotent, as they are typically"\
+ " unique to the environment in which they are run. Use not_if and only_if"\
+ " to guard this resource for idempotence."
end
end
end
diff --git a/lib/chef/resource/portage_package.rb b/lib/chef/resource/portage_package.rb
index ad66c7b42b..6936f5129f 100644
--- a/lib/chef/resource/portage_package.rb
+++ b/lib/chef/resource/portage_package.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,11 +22,9 @@ class Chef
class Resource
class PortagePackage < Chef::Resource::Package
resource_name :portage_package
- def initialize(name, run_context = nil)
- super
- @provider = Chef::Provider::Package::Portage
- end
+ provides :portage_package
+ description "Use the portage_package resource to manage packages for the Gentoo platform."
end
end
end
diff --git a/lib/chef/resource/powershell_package.rb b/lib/chef/resource/powershell_package.rb
new file mode 100644
index 0000000000..184b63f950
--- /dev/null
+++ b/lib/chef/resource/powershell_package.rb
@@ -0,0 +1,42 @@
+# Author:: Dheeraj Dubey(dheeraj.dubey@msystechnologies.com)
+# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource/package"
+require "chef/mixin/uris"
+
+class Chef
+ class Resource
+ class PowershellPackage < Chef::Resource::Package
+ include Chef::Mixin::Uris
+
+ resource_name :powershell_package
+ provides :powershell_package
+
+ description "Use the powershell_package resource to install and manage packages via"\
+ " the Powershell Package Manager for the Microsoft Windows platform. The"\
+ " powershell_package resource requires administrative access, and a source"\
+ " must be configured in the Powershell Package Manager via the Register-PackageSource command"
+ introduced "12.16"
+
+ allowed_actions :install, :remove
+
+ property :package_name, [String, Array], coerce: proc { |x| [x].flatten }
+ property :version, [String, Array], coerce: proc { |x| [x].flatten }
+ property :source, [String]
+ end
+ end
+end
diff --git a/lib/chef/resource/powershell_script.rb b/lib/chef/resource/powershell_script.rb
index a530a9116c..99821bcefd 100644
--- a/lib/chef/resource/powershell_script.rb
+++ b/lib/chef/resource/powershell_script.rb
@@ -22,8 +22,17 @@ class Chef
class PowershellScript < Chef::Resource::WindowsScript
provides :powershell_script, os: "windows"
+ description "Use the powershell_script resource to execute a script using the Windows PowerShell"\
+ " interpreter, much like how the script and script-based resources—bash, csh, perl, python,"\
+ " and ruby—are used. The powershell_script is specific to the Microsoft Windows platform"\
+ " and the Windows PowerShell interpreter.\n\n The powershell_script resource creates and"\
+ " executes a temporary file (similar to how the script resource behaves), rather than running"\
+ " the command inline. Commands that are executed with this resource are (by their nature) not"\
+ " idempotent, as they are typically unique to the environment in which they are run. Use not_if"\
+ " and only_if to guard this resource for idempotence."
+
def initialize(name, run_context = nil)
- super(name, run_context, nil, "powershell.exe")
+ super(name, run_context, :powershell_script, "powershell.exe")
@convert_boolean_return = false
end
diff --git a/lib/chef/resource/python.rb b/lib/chef/resource/python.rb
index bcad3d090b..f6c259b858 100644
--- a/lib/chef/resource/python.rb
+++ b/lib/chef/resource/python.rb
@@ -26,6 +26,11 @@ class Chef
@interpreter = "python"
end
+ description "Use the python resource to execute scripts using the Python interpreter."\
+ " This resource may also use any of the actions and properties that are available"\
+ " to the execute resource. Commands that are executed with this resource are (by"\
+ " their nature) not idempotent, as they are typically unique to the environment in"\
+ " which they are run. Use not_if and only_if to guard this resource for idempotence."
end
end
end
diff --git a/lib/chef/resource/reboot.rb b/lib/chef/resource/reboot.rb
index 24d6e74157..bc649bee09 100644
--- a/lib/chef/resource/reboot.rb
+++ b/lib/chef/resource/reboot.rb
@@ -18,31 +18,24 @@
require "chef/resource"
-# In using this resource via notifications, it's important to *only* use
-# immediate notifications. Delayed notifications produce unintuitive and
-# probably undesired results.
class Chef
class Resource
class Reboot < Chef::Resource
- allowed_actions :request_reboot, :reboot_now, :cancel
-
- def initialize(name, run_context = nil)
- super
- @provider = Chef::Provider::Reboot
+ resource_name :reboot
- @reason = "Reboot by Chef"
- @delay_mins = 0
+ description "Use the reboot resource to reboot a node, a necessary step with some"\
+ " installations on certain platforms. This resource is supported for use on"\
+ " the Microsoft Windows, macOS, and Linux platforms.\n"\
+ "In using this resource via notifications, it's important to *only* use"\
+ " immediate notifications. Delayed notifications produce unintuitive and"\
+ " probably undesired results."
+ introduced "12.0"
- # no default action.
- end
-
- def reason(arg = nil)
- set_or_return(:reason, arg, :kind_of => String)
- end
+ allowed_actions :request_reboot, :reboot_now, :cancel
+ default_action :nothing # make sure people are quite clear what they want
- def delay_mins(arg = nil)
- set_or_return(:delay_mins, arg, :kind_of => Fixnum)
- end
+ property :reason, String, default: "Reboot by Chef"
+ property :delay_mins, Integer, default: 0
end
end
end
diff --git a/lib/chef/resource/registry_key.rb b/lib/chef/resource/registry_key.rb
index d11f826c38..84abb98699 100644
--- a/lib/chef/resource/registry_key.rb
+++ b/lib/chef/resource/registry_key.rb
@@ -15,14 +15,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require "chef/provider/registry_key"
+
require "chef/resource"
require "chef/digester"
class Chef
class Resource
class RegistryKey < Chef::Resource
- identity_attr :key
+ resource_name :registry_key
+ provides(:registry_key) { true }
+
+ description "Use the registry_key resource to create and delete registry keys in Microsoft Windows."
+ introduced "11.0"
+
state_attrs :values
default_action :create
@@ -61,19 +66,10 @@ class Chef
def initialize(name, run_context = nil)
super
- @architecture = :machine
- @recursive = false
- @key = name
@values, @unscrubbed_values = [], []
end
- def key(arg = nil)
- set_or_return(
- :key,
- arg,
- :kind_of => String
- )
- end
+ property :key, String, name_property: true, identity: true
def values(arg = nil)
if not arg.nil?
@@ -87,35 +83,22 @@ class Chef
@values.each do |v|
raise ArgumentError, "Missing name key in RegistryKey values hash" unless v.has_key?(:name)
- raise ArgumentError, "Missing type key in RegistryKey values hash" unless v.has_key?(:type)
- raise ArgumentError, "Missing data key in RegistryKey values hash" unless v.has_key?(:data)
v.each_key do |key|
raise ArgumentError, "Bad key #{key} in RegistryKey values hash" unless [:name, :type, :data].include?(key)
end
raise ArgumentError, "Type of name => #{v[:name]} should be string" unless v[:name].is_a?(String)
- raise ArgumentError, "Type of type => #{v[:type]} should be symbol" unless v[:type].is_a?(Symbol)
+ if v[:type]
+ raise ArgumentError, "Type of type => #{v[:type]} should be symbol" unless v[:type].is_a?(Symbol)
+ end
end
@unscrubbed_values = @values
- elsif self.instance_variable_defined?(:@values)
+ elsif instance_variable_defined?(:@values)
scrub_values(@values)
end
end
- def recursive(arg = nil)
- set_or_return(
- :recursive,
- arg,
- :kind_of => [TrueClass, FalseClass]
- )
- end
-
- def architecture(arg = nil)
- set_or_return(
- :architecture,
- arg,
- :kind_of => Symbol
- )
- end
+ property :recursive, [TrueClass, FalseClass], default: false
+ property :architecture, Symbol, default: :machine, equal_to: [:machine, :x86_64, :i386]
private
diff --git a/lib/chef/resource/remote_directory.rb b/lib/chef/resource/remote_directory.rb
index 6e2928f3eb..daeb7b35bb 100644
--- a/lib/chef/resource/remote_directory.rb
+++ b/lib/chef/resource/remote_directory.rb
@@ -26,6 +26,11 @@ class Chef
class RemoteDirectory < Chef::Resource::Directory
include Chef::Mixin::Securable
+ description "Use the remote_directory resource to incrementally transfer a directory"\
+ " from a cookbook to a node. The director that is copied from the cookbook"\
+ " should be located under COOKBOOK_NAME/files/default/REMOTE_DIRECTORY. The"\
+ " remote_directory resource will obey file specificity."
+
identity_attr :path
state_attrs :files_owner, :files_group, :files_mode
@@ -36,16 +41,11 @@ class Chef
def initialize(name, run_context = nil)
super
@path = name
- @source = ::File.basename(name)
@delete = false
@recursive = true
- @purge = false
- @files_backup = 5
@files_owner = nil
@files_group = nil
@files_mode = 0644 unless Chef::Platform.windows?
- @overwrite = true
- @cookbook = nil
end
if Chef::Platform.windows?
@@ -53,29 +53,11 @@ class Chef
rights_attribute(:files_rights)
end
- def source(args = nil)
- set_or_return(
- :source,
- args,
- :kind_of => String
- )
- end
-
- def files_backup(arg = nil)
- set_or_return(
- :files_backup,
- arg,
- :kind_of => [ Integer, FalseClass ]
- )
- end
-
- def purge(arg = nil)
- set_or_return(
- :purge,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :source, String, default: lazy { ::File.basename(path) }
+ property :files_backup, [ Integer, FalseClass ], default: 5, desired_state: false
+ property :purge, [ TrueClass, FalseClass ], default: false, desired_state: false
+ property :overwrite, [ TrueClass, FalseClass ], default: true
+ property :cookbook, String
def files_group(arg = nil)
set_or_return(
@@ -100,23 +82,6 @@ class Chef
:regex => Chef::Config[:user_valid_regex]
)
end
-
- def overwrite(arg = nil)
- set_or_return(
- :overwrite,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def cookbook(args = nil)
- set_or_return(
- :cookbook,
- args,
- :kind_of => String
- )
- end
-
end
end
end
diff --git a/lib/chef/resource/remote_file.rb b/lib/chef/resource/remote_file.rb
index 4a1d1c6cff..832fd3568e 100644
--- a/lib/chef/resource/remote_file.rb
+++ b/lib/chef/resource/remote_file.rb
@@ -28,14 +28,12 @@ class Chef
class RemoteFile < Chef::Resource::File
include Chef::Mixin::Securable
+ description "Use the remote_file resource to transfer a file from a remote location"\
+ " using file specificity. This resource is similar to the file resource."
+
def initialize(name, run_context = nil)
super
@source = []
- @use_etag = true
- @use_last_modified = true
- @ftp_active_mode = false
- @headers = {}
- @provider = Chef::Provider::RemoteFile
end
# source can take any of the following as arguments
@@ -72,13 +70,7 @@ class Chef
end
end
- def checksum(args = nil)
- set_or_return(
- :checksum,
- args,
- :kind_of => String
- )
- end
+ property :checksum, String
# Disable or enable ETag and Last Modified conditional GET. Equivalent to
# use_etag(true_or_false)
@@ -88,47 +80,78 @@ class Chef
use_last_modified(true_or_false)
end
- def use_etag(args = nil)
- set_or_return(
- :use_etag,
- args,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :use_etag, [ TrueClass, FalseClass ], default: true
alias :use_etags :use_etag
- def use_last_modified(args = nil)
- set_or_return(
- :use_last_modified,
- args,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :use_last_modified, [ TrueClass, FalseClass ], default: true
+
+ property :ftp_active_mode, [ TrueClass, FalseClass ], default: false
+
+ property :headers, Hash, default: lazy { Hash.new }
- def ftp_active_mode(args = nil)
- set_or_return(
- :ftp_active_mode,
- args,
- :kind_of => [ TrueClass, FalseClass ]
- )
+ property :show_progress, [ TrueClass, FalseClass ], default: false
+
+ property :remote_user, String
+
+ property :remote_domain, String
+
+ property :remote_password, String, sensitive: true
+
+ property :authentication, equal_to: [:remote, :local], default: :remote
+
+ def after_created
+ validate_identity_platform(remote_user, remote_password, remote_domain)
+ identity = qualify_user(remote_user, remote_password, remote_domain)
+ remote_domain(identity[:domain])
+ remote_user(identity[:user])
end
- def headers(args = nil)
- set_or_return(
- :headers,
- args,
- :kind_of => Hash
- )
+ def validate_identity_platform(specified_user, password = nil, specified_domain = nil)
+ if node[:platform_family] == "windows"
+ if specified_user && password.nil?
+ raise ArgumentError, "A value for `remote_password` must be specified when a value for `user` is specified on the Windows platform"
+ end
+ end
end
- def show_progress(args = nil)
- set_or_return(
- :show_progress,
- args,
- :default => false,
- :kind_of => [ TrueClass, FalseClass ]
- )
+ def qualify_user(specified_user, password = nil, specified_domain = nil)
+ domain = specified_domain
+ user = specified_user
+
+ if specified_user.nil? && ! specified_domain.nil?
+ raise ArgumentError, "The domain `#{specified_domain}` was specified, but no user name was given"
+ end
+
+ # if domain is provided in both username and domain
+ if specified_user && ((specified_user.include? '\\') || (specified_user.include? "@")) && specified_domain
+ raise ArgumentError, "The domain is provided twice. Username: `#{specified_user}`, Domain: `#{specified_domain}`. Please specify domain only once."
+ end
+
+ if ! specified_user.nil? && specified_domain.nil?
+ # Splitting username of format: Domain\Username
+ domain_and_user = user.split('\\')
+
+ if domain_and_user.length == 2
+ domain = domain_and_user[0]
+ user = domain_and_user[1]
+ elsif domain_and_user.length == 1
+ # Splitting username of format: Username@Domain
+ domain_and_user = user.split("@")
+ if domain_and_user.length == 2
+ domain = domain_and_user[1]
+ user = domain_and_user[0]
+ elsif domain_and_user.length != 1
+ raise ArgumentError, "The specified user name `#{user}` is not a syntactically valid user name"
+ end
+ end
+ end
+
+ if ( password || domain ) && user.nil?
+ raise ArgumentError, "A value for `password` or `domain` was specified without specification of a value for `user`"
+ end
+
+ { domain: domain, user: user }
end
private
diff --git a/lib/chef/resource/resource_notification.rb b/lib/chef/resource/resource_notification.rb
index ee90064a17..24f8821b6b 100644
--- a/lib/chef/resource/resource_notification.rb
+++ b/lib/chef/resource/resource_notification.rb
@@ -20,16 +20,24 @@ require "chef/resource"
class Chef
class Resource
+ # @author Tyler Ball
+ # @attr [Resource] resource the Chef resource object to notify to
+ # @attr [Action] action the action to notify
+ # @attr [Resource] notifying_resource the Chef resource performing the notification
class Notification
attr_accessor :resource, :action, :notifying_resource
def initialize(resource, action, notifying_resource)
@resource = resource
- @action = action
+ @action = action&.to_sym
@notifying_resource = notifying_resource
end
+ # Is the current notification a duplicate of another notification
+ #
+ # @param [Notification] other_notification another notification object to compare to
+ # @return [Boolean] does the resource match
def duplicates?(other_notification)
unless other_notification.respond_to?(:resource) && other_notification.respond_to?(:action)
msg = "only duck-types of Chef::Resource::Notification can be checked for duplication "\
@@ -41,6 +49,9 @@ class Chef
# If resource and/or notifying_resource is not a resource object, this will look them up in the resource collection
# and fix the references from strings to actual Resource objects.
+ # @param [ResourceCollection] resource_collection
+ #
+ # @return [void]
def resolve_resource_reference(resource_collection)
return resource if resource.kind_of?(Chef::Resource) && notifying_resource.kind_of?(Chef::Resource)
@@ -55,6 +66,9 @@ class Chef
# This will look up the resource if it is not a Resource Object. It will complain if it finds multiple
# resources, can't find a resource, or gets invalid syntax.
+ # @param [ResourceCollection] resource_collection
+ #
+ # @return [void]
def fix_resource_reference(resource_collection)
matching_resource = resource_collection.find(resource)
if Array(matching_resource).size > 1
@@ -84,6 +98,9 @@ is defined near #{notifying_resource.source_line}
# This will look up the notifying_resource if it is not a Resource Object. It will complain if it finds multiple
# resources, can't find a resource, or gets invalid syntax.
+ # @param [ResourceCollection] resource_collection
+ #
+ # @return [void]
def fix_notifier_reference(resource_collection)
matching_notifier = resource_collection.find(notifying_resource)
if Array(matching_notifier).size > 1
diff --git a/lib/chef/resource/rhsm_errata.rb b/lib/chef/resource/rhsm_errata.rb
new file mode 100644
index 0000000000..96de1bafe7
--- /dev/null
+++ b/lib/chef/resource/rhsm_errata.rb
@@ -0,0 +1,45 @@
+#
+# Copyright:: 2015-2018 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class RhsmErrata < Chef::Resource
+ resource_name :rhsm_errata
+ provides(:rhsm_errata) { true }
+
+ description "Use the rhsm_errata resource to install packages associated with a given Red"\
+ " Hat Subscription Manager Errata ID. This is helpful if packages"\
+ " to mitigate a single vulnerability must be installed on your hosts."
+ introduced "14.0"
+
+ property :errata_id, String,
+ description: "An optional property for specifying the errata ID if not using the resource's name.",
+ name_property: true
+
+ action :install do
+ description "Installs a package for a specific errata ID"
+
+ execute "Install errata packages for #{new_resource.errata_id}" do
+ command "yum update --advisory #{new_resource.errata_id} -y"
+ action :run
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/rhsm_errata_level.rb b/lib/chef/resource/rhsm_errata_level.rb
new file mode 100644
index 0000000000..73c9dc0423
--- /dev/null
+++ b/lib/chef/resource/rhsm_errata_level.rb
@@ -0,0 +1,52 @@
+#
+# Copyright:: 2015-2018 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class RhsmErrataLevel < Chef::Resource
+ resource_name :rhsm_errata_level
+ provides(:rhsm_errata_level) { true }
+
+ description " Use the rhsm_errata_level resource to install all packages of a specified errata level"\
+ " from the Red Hat Subscript Manager. For example, you can ensure that all packages associated"\
+ " with errata marked at a 'Critical' security level are installed."
+ introduced "14.0"
+
+ property :errata_level, String,
+ coerce: proc { |x| x.downcase },
+ equal_to: %w{critical moderate important low},
+ description: "The errata level of packages to install.",
+ name_property: true
+
+ action :install do
+ descripton "Install all packages of the specified errata level"
+
+ yum_package "yum-plugin-security" do
+ action :install
+ only_if { node["platform_version"].to_i == 6 }
+ end
+
+ execute "Install any #{new_resource.errata_level} errata" do
+ command "yum update --sec-severity=#{new_resource.errata_level.capitalize} -y"
+ action :run
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/rhsm_register.rb b/lib/chef/resource/rhsm_register.rb
new file mode 100644
index 0000000000..0e478eff38
--- /dev/null
+++ b/lib/chef/resource/rhsm_register.rb
@@ -0,0 +1,165 @@
+#
+# Copyright:: 2015-2018 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+require "shellwords"
+
+class Chef
+ class Resource
+ class RhsmRegister < Chef::Resource
+ resource_name :rhsm_register
+ provides(:rhsm_register) { true }
+
+ description "Use the rhsm_register resource to register a node with the Red Hat Subscription Manager"\
+ " or a local Red Hat Satellite server."
+ introduced "14.0"
+
+ property :activation_key, [String, Array],
+ coerce: proc { |x| Array(x) },
+ description: "A String or array of the activation keys to use when registering. You must also specify the organization property if using activation_key."
+
+ property :satellite_host, String,
+ description: "The FQDN of the Satellite host to register with. If not specified, the host will be registered with Red Hat's public RHSM service."
+
+ property :organization, String,
+ description: "The organization to use when registering, required when using an activation key"
+
+ property :environment, String,
+ description: "The environment to use when registering, required when using username and password"
+
+ property :username, String,
+ description: "The username to use when registering. Not applicable if using an activation key. If specified, password and environment are also required."
+
+ property :password, String,
+ description: "The password to use when registering. Not applicable if using an activation key. If specified, username and environment are also required."
+
+ property :auto_attach,
+ [TrueClass, FalseClass],
+ description: "If true, RHSM will attempt to automatically attach the host to applicable subscriptions. It is generally better to use an activation key with the subscriptions pre-defined.",
+ default: false
+
+ property :install_katello_agent, [TrueClass, FalseClass],
+ description: "If true, the 'katello-agent' RPM will be installed.",
+ default: true
+
+ property :force, [TrueClass, FalseClass],
+ description: "If true, the system will be registered even if it is already registered. Normally, any register operations will fail if the machine is has already registered.",
+ default: false, desired_state: false
+
+ action :register do
+ description "Register the node with RHSM"
+
+ package "subscription-manager"
+
+ unless new_resource.satellite_host.nil? || registered_with_rhsm?
+ remote_file "#{Chef::Config[:file_cache_path]}/katello-package.rpm" do
+ source "http://#{new_resource.satellite_host}/pub/katello-ca-consumer-latest.noarch.rpm"
+ action :create
+ notifies :install, "yum_package[katello-ca-consumer-latest]", :immediately
+ not_if { katello_cert_rpm_installed? }
+ end
+
+ yum_package "katello-ca-consumer-latest" do
+ options "--nogpgcheck"
+ source "#{Chef::Config[:file_cache_path]}/katello-package.rpm"
+ action :nothing
+ end
+
+ file "#{Chef::Config[:file_cache_path]}/katello-package.rpm" do
+ action :delete
+ end
+ end
+
+ execute "Register to RHSM" do
+ sensitive new_resource.sensitive
+ command register_command
+ action :run
+ not_if { registered_with_rhsm? } unless new_resource.force
+ end
+
+ yum_package "katello-agent" do
+ action :install
+ only_if { new_resource.install_katello_agent && !new_resource.satellite_host.nil? }
+ end
+ end
+
+ action :unregister do
+ description "Unregister the node from RHSM"
+
+ execute "Unregister from RHSM" do
+ command "subscription-manager unregister"
+ action :run
+ only_if { registered_with_rhsm? }
+ notifies :run, "execute[Clean RHSM Config]", :immediately
+ end
+
+ execute "Clean RHSM Config" do
+ command "subscription-manager clean"
+ action :nothing
+ end
+ end
+
+ action_class do
+ def registered_with_rhsm?
+ cmd = Mixlib::ShellOut.new("subscription-manager status", env: { LANG: "en_US" })
+ cmd.run_command
+ !cmd.stdout.match(/Overall Status: Unknown/)
+ end
+
+ def katello_cert_rpm_installed?
+ cmd = Mixlib::ShellOut.new("rpm -qa | grep katello-ca-consumer")
+ cmd.run_command
+ !cmd.stdout.match(/katello-ca-consumer/).nil?
+ end
+
+ def register_command
+ command = %w{subscription-manager register}
+
+ if new_resource.activation_key
+ unless new_resource.activation_key.empty?
+ raise "Unable to register - you must specify organization when using activation keys" if new_resource.organization.nil?
+
+ command << new_resource.activation_key.map { |key| "--activationkey=#{Shellwords.shellescape(key)}" }
+ command << "--org=#{Shellwords.shellescape(new_resource.organization)}"
+ command << "--force" if new_resource.force
+
+ return command.join(" ")
+ end
+ end
+
+ if new_resource.username && new_resource.password
+ raise "Unable to register - you must specify environment when using username/password" if new_resource.environment.nil? && using_satellite_host?
+
+ command << "--username=#{Shellwords.shellescape(new_resource.username)}"
+ command << "--password=#{Shellwords.shellescape(new_resource.password)}"
+ command << "--environment=#{Shellwords.shellescape(new_resource.environment)}" if using_satellite_host?
+ command << "--auto-attach" if new_resource.auto_attach
+ command << "--force" if new_resource.force
+
+ return command.join(" ")
+ end
+
+ raise "Unable to create register command - you must specify activation_key or username/password"
+ end
+
+ def using_satellite_host?
+ !new_resource.satellite_host.nil?
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/rhsm_repo.rb b/lib/chef/resource/rhsm_repo.rb
new file mode 100644
index 0000000000..9eba096f51
--- /dev/null
+++ b/lib/chef/resource/rhsm_repo.rb
@@ -0,0 +1,63 @@
+#
+# Copyright:: 2015-2018 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class RhsmRepo < Chef::Resource
+ resource_name :rhsm_repo
+ provides(:rhsm_repo) { true }
+
+ description "Use the rhsm_repo resource to enable or disable Red Hat Subscription Manager"\
+ " repositories that are made available via attached subscriptions."
+ introduced "14.0"
+
+ property :repo_name, String,
+ description: "An optional property for specifying the repository name if not using the resource's name.",
+ name_property: true
+
+ action :enable do
+ description "Enable a RHSM repository"
+
+ execute "Enable repository #{new_resource.repo_name}" do
+ command "subscription-manager repos --enable=#{new_resource.repo_name}"
+ action :run
+ not_if { repo_enabled?(new_resource.repo_name) }
+ end
+ end
+
+ action :disable do
+ description "Disable a RHSM repository"
+
+ execute "Enable repository #{new_resource.repo_name}" do
+ command "subscription-manager repos --disable=#{new_resource.repo_name}"
+ action :run
+ only_if { repo_enabled?(new_resource.repo_name) }
+ end
+ end
+
+ action_class do
+ def repo_enabled?(repo)
+ cmd = Mixlib::ShellOut.new("subscription-manager repos --list-enabled", env: { LANG: "en_US" })
+ cmd.run_command
+ !cmd.stdout.match(/Repo ID:\s+#{repo}$/).nil?
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/rhsm_subscription.rb b/lib/chef/resource/rhsm_subscription.rb
new file mode 100644
index 0000000000..9f198dc917
--- /dev/null
+++ b/lib/chef/resource/rhsm_subscription.rb
@@ -0,0 +1,96 @@
+#
+# Copyright:: 2015-2018 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class RhsmSubscription < Chef::Resource
+ resource_name :rhsm_subscription
+ provides(:rhsm_subscription) { true }
+
+ description "Use the rhsm_subscription resource to add or remove Red Hat Subscription Manager"\
+ " subscriptions from your host. This can be used when a host's activation_key"\
+ " does not attach all necessary subscriptions to your host."
+ introduced "14.0"
+
+ property :pool_id, String,
+ description: "An optional property for specifying the Pool ID if not using the resource's name.",
+ name_property: true
+
+ action :attach do
+ description "Attach the node to a subscription pool"
+
+ execute "Attach subscription pool #{new_resource.pool_id}" do
+ command "subscription-manager attach --pool=#{new_resource.pool_id}"
+ action :run
+ not_if { subscription_attached?(new_resource.pool_id) }
+ end
+ end
+
+ action :remove do
+ description "Remove the node from a subscription pool"
+
+ execute "Remove subscription pool #{new_resource.pool_id}" do
+ command "subscription-manager remove --serial=#{pool_serial(new_resource.pool_id)}"
+ action :run
+ only_if { subscription_attached?(new_resource.pool_id) }
+ end
+ end
+
+ action_class do
+ def subscription_attached?(subscription)
+ cmd = Mixlib::ShellOut.new("subscription-manager list --consumed | grep #{subscription}", env: { LANG: "en_US" })
+ cmd.run_command
+ !cmd.stdout.match(/Pool ID:\s+#{subscription}$/).nil?
+ end
+
+ def serials_by_pool
+ serials = {}
+ pool = nil
+ serial = nil
+
+ cmd = Mixlib::ShellOut.new("subscription-manager list --consumed", env: { LANG: "en_US" })
+ cmd.run_command
+ cmd.stdout.lines.each do |line|
+ line.strip!
+ key, value = line.split(/:\s+/, 2)
+ next unless ["Pool ID", "Serial"].include?(key)
+
+ if key == "Pool ID"
+ pool = value
+ elsif key == "Serial"
+ serial = value
+ end
+
+ next unless pool && serial
+
+ serials[pool] = serial
+ pool = nil
+ serial = nil
+ end
+
+ serials
+ end
+
+ def pool_serial(pool_id)
+ serials_by_pool[pool_id]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/route.rb b/lib/chef/resource/route.rb
index 0117a8bfc0..b64dcf26c6 100644
--- a/lib/chef/resource/route.rb
+++ b/lib/chef/resource/route.rb
@@ -22,116 +22,25 @@ require "chef/resource"
class Chef
class Resource
class Route < Chef::Resource
- identity_attr :target
-
- state_attrs :netmask, :gateway
-
default_action :add
allowed_actions :add, :delete
- def initialize(name, run_context = nil)
- super
- @target = name
- @netmask = nil
- @gateway = nil
- @metric = nil
- @device = nil
- @route_type = :host
- @networking = nil
- @networking_ipv6 = nil
- @hostname = nil
- @domainname = nil
- @domain = nil
- end
-
- def networking(arg = nil)
- set_or_return(
- :networking,
- arg,
- :kind_of => String
- )
- end
-
- def networking_ipv6(arg = nil)
- set_or_return(
- :networking_ipv6,
- arg,
- :kind_of => String
- )
- end
-
- def hostname(arg = nil)
- set_or_return(
- :hostname,
- arg,
- :kind_of => String
- )
- end
-
- def domainname(arg = nil)
- set_or_return(
- :domainname,
- arg,
- :kind_of => String
- )
- end
-
- def domain(arg = nil)
- set_or_return(
- :domain,
- arg,
- :kind_of => String
- )
- end
-
- def target(arg = nil)
- set_or_return(
- :target,
- arg,
- :kind_of => String
- )
- end
-
- def netmask(arg = nil)
- set_or_return(
- :netmask,
- arg,
- :kind_of => String
- )
- end
-
- def gateway(arg = nil)
- set_or_return(
- :gateway,
- arg,
- :kind_of => String
- )
- end
-
- def metric(arg = nil)
- set_or_return(
- :metric,
- arg,
- :kind_of => Integer
- )
- end
-
- def device(arg = nil)
- set_or_return(
- :device,
- arg,
- :kind_of => String
- )
- end
-
- def route_type(arg = nil)
- real_arg = arg.kind_of?(String) ? arg.to_sym : arg
- set_or_return(
- :route_type,
- real_arg,
- :equal_to => [ :host, :net ]
- )
- end
+ description "Use the route resource to manage the system routing table in a Linux environment."
+
+ property :target, String, identity: true, name_property: true
+ property :comment, [String, nil]
+ property :metric, [Integer, nil]
+ property :netmask, [String, nil]
+ property :gateway, [String, nil]
+ property :device, [String, nil], desired_state: false # Has a partial default in the provider of eth0.
+ property :route_type, [:host, :net], default: :host, coerce: proc { |x| x.to_sym }, desired_state: false
+
+ # I can find no evidence of these properties actually being used by Chef. NK 2017-04-11
+ property :networking, [String, nil], desired_state: false
+ property :networking_ipv6, [String, nil], desired_state: false
+ property :hostname, [String, nil], desired_state: false
+ property :domainname, [String, nil], desired_state: false
+ property :domain, [String, nil], desired_state: false
end
end
end
diff --git a/lib/chef/resource/rpm_package.rb b/lib/chef/resource/rpm_package.rb
index c93dfecaf5..4d79ba98d1 100644
--- a/lib/chef/resource/rpm_package.rb
+++ b/lib/chef/resource/rpm_package.rb
@@ -17,13 +17,14 @@
#
require "chef/resource/package"
-require "chef/provider/package/rpm"
class Chef
class Resource
class RpmPackage < Chef::Resource::Package
resource_name :rpm_package
- provides :rpm_package, os: %w{linux aix}
+ provides :rpm_package
+
+ description "Use the rpm_package resource to manage packages for the RPM Package Manager platform."
property :allow_downgrade, [ true, false ], default: false, desired_state: false
diff --git a/lib/chef/resource/ruby.rb b/lib/chef/resource/ruby.rb
index 91805a1db6..b5f36bb0e9 100644
--- a/lib/chef/resource/ruby.rb
+++ b/lib/chef/resource/ruby.rb
@@ -22,6 +22,12 @@ require "chef/provider/script"
class Chef
class Resource
class Ruby < Chef::Resource::Script
+ description "Use the ruby resource to execute scripts using the Ruby interpreter. This"\
+ " resource may also use any of the actions and properties that are available"\
+ " to the execute resource. Commands that are executed with this resource are (by"\
+ " their nature) not idempotent, as they are typically unique to the environment"\
+ " in which they are run. Use not_if and only_if to guard this resource for idempotence."
+
def initialize(name, run_context = nil)
super
@interpreter = "ruby"
diff --git a/lib/chef/resource/ruby_block.rb b/lib/chef/resource/ruby_block.rb
index 87a4cfb7c5..911e6203d8 100644
--- a/lib/chef/resource/ruby_block.rb
+++ b/lib/chef/resource/ruby_block.rb
@@ -23,16 +23,14 @@ require "chef/provider/ruby_block"
class Chef
class Resource
class RubyBlock < Chef::Resource
+ description "Use the ruby_block resource to execute Ruby code during a chef-client run."\
+ " Ruby code in the ruby_block resource is evaluated with other resources during"\
+ " convergence, whereas Ruby code outside of a ruby_block resource is evaluated"\
+ " before other resources, as the recipe is compiled."
+
default_action :run
allowed_actions :create, :run
- identity_attr :block_name
-
- def initialize(name, run_context = nil)
- super
- @block_name = name
- end
-
def block(&block)
if block_given? && block
@block = block
@@ -41,13 +39,7 @@ class Chef
end
end
- def block_name(arg = nil)
- set_or_return(
- :block_name,
- arg,
- :kind_of => String
- )
- end
+ property :block_name, String, name_property: true, identity: true
end
end
end
diff --git a/lib/chef/resource/scm.rb b/lib/chef/resource/scm.rb
index 1e8c71e59d..d8b4dd304a 100644
--- a/lib/chef/resource/scm.rb
+++ b/lib/chef/resource/scm.rb
@@ -21,163 +21,25 @@ require "chef/resource"
class Chef
class Resource
class Scm < Chef::Resource
- identity_attr :destination
-
- state_attrs :revision
-
default_action :sync
allowed_actions :checkout, :export, :sync, :diff, :log
- def initialize(name, run_context = nil)
- super
- @destination = name
- @enable_submodules = false
- @enable_checkout = true
- @revision = "HEAD"
- @remote = "origin"
- @ssh_wrapper = nil
- @depth = nil
- @checkout_branch = "deploy"
- @environment = nil
- end
-
- def destination(arg = nil)
- set_or_return(
- :destination,
- arg,
- :kind_of => String
- )
- end
-
- def repository(arg = nil)
- set_or_return(
- :repository,
- arg,
- :kind_of => String
- )
- end
-
- def revision(arg = nil)
- set_or_return(
- :revision,
- arg,
- :kind_of => String
- )
- end
-
- def user(arg = nil)
- set_or_return(
- :user,
- arg,
- :kind_of => [String, Integer]
- )
- end
-
- def group(arg = nil)
- set_or_return(
- :group,
- arg,
- :kind_of => [String, Integer]
- )
- end
-
- def svn_username(arg = nil)
- set_or_return(
- :svn_username,
- arg,
- :kind_of => String
- )
- end
-
- def svn_password(arg = nil)
- set_or_return(
- :svn_password,
- arg,
- :kind_of => String
- )
- end
-
- def svn_arguments(arg = nil)
- @svn_arguments, arg = nil, nil if arg == false
- set_or_return(
- :svn_arguments,
- arg,
- :kind_of => String
- )
- end
-
- def svn_info_args(arg = nil)
- @svn_info_args, arg = nil, nil if arg == false
- set_or_return(
- :svn_info_args,
- arg,
- :kind_of => String)
- end
-
+ property :destination, String, name_property: true, identity: true
+ property :repository, String
+ property :revision, String, default: "HEAD"
+ property :user, [String, Integer]
+ property :group, [String, Integer]
+ property :svn_username, String
+ property :svn_password, String, sensitive: true, desired_state: false
# Capistrano and git-deploy use ``shallow clone''
- def depth(arg = nil)
- set_or_return(
- :depth,
- arg,
- :kind_of => Integer
- )
- end
-
- def enable_submodules(arg = nil)
- set_or_return(
- :enable_submodules,
- arg,
- :kind_of => [TrueClass, FalseClass]
- )
- end
-
- def enable_checkout(arg = nil)
- set_or_return(
- :enable_checkout,
- arg,
- :kind_of => [TrueClass, FalseClass]
- )
- end
-
- def remote(arg = nil)
- set_or_return(
- :remote,
- arg,
- :kind_of => String
- )
- end
-
- def ssh_wrapper(arg = nil)
- set_or_return(
- :ssh_wrapper,
- arg,
- :kind_of => String
- )
- end
-
- def timeout(arg = nil)
- set_or_return(
- :timeout,
- arg,
- :kind_of => Integer
- )
- end
-
- def checkout_branch(arg = nil)
- set_or_return(
- :checkout_branch,
- arg,
- :kind_of => String
- )
- end
-
- def environment(arg = nil)
- set_or_return(
- :environment,
- arg,
- :kind_of => [ Hash ]
- )
- end
+ property :depth, Integer
+ property :enable_submodules, [TrueClass, FalseClass], default: false
+ property :enable_checkout, [TrueClass, FalseClass], default: true
+ property :remote, String, default: "origin"
+ property :ssh_wrapper, String, desired_state: false
+ property :timeout, Integer, desired_state: false
+ property :checkout_branch, String, default: "deploy"
+ property :environment, [Hash, nil], default: nil
alias :env :environment
end
diff --git a/lib/chef/resource/script.rb b/lib/chef/resource/script.rb
index 5173a76542..d3562ee6f5 100644
--- a/lib/chef/resource/script.rb
+++ b/lib/chef/resource/script.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,56 +18,36 @@
#
require "chef/resource/execute"
-require "chef/provider/script"
class Chef
class Resource
class Script < Chef::Resource::Execute
- # Chef-13: go back to using :name as the identity attr
- identity_attr :command
+ resource_name :script
+
+ identity_attr :name
+
+ description "Use the script resource to execute scripts using a specified interpreter, such as Bash, csh, Perl, Python, or Ruby."\
+ " This resource may also use any of the actions and properties that are available to the execute resource. Commands"\
+ " that are executed with this resource are (by their nature) not idempotent, as they are typically unique to the"\
+ " environment in which they are run. Use not_if and only_if to guard this resource for idempotence."
def initialize(name, run_context = nil)
super
- # Chef-13: the command variable should be initialized to nil
- @command = name
- @code = nil
- @interpreter = nil
- @flags = nil
+ @command = nil
@default_guard_interpreter = :default
end
+ # FIXME: remove this and use an execute sub-resource instead of inheriting from Execute
def command(arg = nil)
unless arg.nil?
- # Chef-13: change this to raise if the user is trying to set a value here
- Chef::Log.warn "Specifying command attribute on a script resource is a coding error, use the 'code' attribute, or the execute resource"
- Chef::Log.warn "This attribute is deprecated and must be fixed or this code will fail on Chef 13"
+ raise Chef::Exceptions::Script, "Do not use the command attribute on a #{resource_name} resource, use the 'code' attribute instead."
end
super
end
- def code(arg = nil)
- set_or_return(
- :code,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def interpreter(arg = nil)
- set_or_return(
- :interpreter,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def flags(arg = nil)
- set_or_return(
- :flags,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :code, String, required: true
+ property :interpreter, String
+ property :flags, String
end
end
diff --git a/lib/chef/resource/service.rb b/lib/chef/resource/service.rb
index 1ca4b84af0..07473c9709 100644
--- a/lib/chef/resource/service.rb
+++ b/lib/chef/resource/service.rb
@@ -1,7 +1,7 @@
#
# Author:: AJ Christensen (<aj@hjksolutions.com>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +18,7 @@
#
require "chef/resource"
+require "shellwords"
class Chef
class Resource
@@ -26,16 +27,23 @@ class Chef
state_attrs :enabled, :running, :masked
+ description "Use the service resource to manage a service."
+
default_action :nothing
allowed_actions :enable, :disable, :start, :stop, :restart, :reload,
:mask, :unmask
+ # this is a poor API please do not re-use this pattern
+ property :supports, Hash, default: { restart: nil, reload: nil, status: nil },
+ coerce: proc { |x| x.is_a?(Array) ? x.each_with_object({}) { |i, m| m[i] = true } : x }
+
def initialize(name, run_context = nil)
super
@service_name = name
@enabled = nil
@running = nil
@masked = nil
+ @options = nil
@parameters = nil
@pattern = service_name
@start_command = nil
@@ -48,7 +56,6 @@ class Chef
@timeout = nil
@run_levels = nil
@user = nil
- @supports = { :restart => nil, :reload => nil, :status => nil }
end
def service_name(arg = nil)
@@ -73,7 +80,7 @@ class Chef
set_or_return(
:start_command,
arg,
- :kind_of => [ String ]
+ :kind_of => [ String, NilClass, FalseClass ]
)
end
@@ -82,7 +89,7 @@ class Chef
set_or_return(
:stop_command,
arg,
- :kind_of => [ String ]
+ :kind_of => [ String, NilClass, FalseClass ]
)
end
@@ -91,7 +98,7 @@ class Chef
set_or_return(
:status_command,
arg,
- :kind_of => [ String ]
+ :kind_of => [ String, NilClass, FalseClass ]
)
end
@@ -100,7 +107,7 @@ class Chef
set_or_return(
:restart_command,
arg,
- :kind_of => [ String ]
+ :kind_of => [ String, NilClass, FalseClass ]
)
end
@@ -108,7 +115,7 @@ class Chef
set_or_return(
:reload_command,
arg,
- :kind_of => [ String ]
+ :kind_of => [ String, NilClass, FalseClass ]
)
end
@@ -152,6 +159,14 @@ class Chef
)
end
+ def options(arg = nil)
+ set_or_return(
+ :options,
+ arg.respond_to?(:split) ? arg.shellsplit : arg,
+ :kind_of => [ Array, String ]
+ )
+ end
+
# Priority arguments can have two forms:
#
# - a simple number, in which the default start runlevels get
@@ -201,17 +216,6 @@ class Chef
:kind_of => [ String ]
)
end
-
- def supports(args = {})
- if args.is_a? Array
- args.each { |arg| @supports[arg] = true }
- elsif args.any?
- @supports = args
- else
- @supports
- end
- end
-
end
end
end
diff --git a/lib/chef/resource/smartos_package.rb b/lib/chef/resource/smartos_package.rb
index 87173ccfa9..d9ef55c50f 100644
--- a/lib/chef/resource/smartos_package.rb
+++ b/lib/chef/resource/smartos_package.rb
@@ -17,13 +17,15 @@
#
require "chef/resource/package"
-require "chef/provider/package/smartos"
class Chef
class Resource
class SmartosPackage < Chef::Resource::Package
resource_name :smartos_package
- provides :package, os: "solaris2", platform_family: "smartos"
+ provides :smartos_package
+ provides :package, platform_family: "smartos"
+
+ description "Use the smartos_package resource to manage packages for the SmartOS platform."
end
end
end
diff --git a/lib/chef/resource/solaris_package.rb b/lib/chef/resource/solaris_package.rb
index d0f8c144af..435a6ecc44 100644
--- a/lib/chef/resource/solaris_package.rb
+++ b/lib/chef/resource/solaris_package.rb
@@ -18,14 +18,16 @@
#
require "chef/resource/package"
-require "chef/provider/package/solaris"
class Chef
class Resource
class SolarisPackage < Chef::Resource::Package
resource_name :solaris_package
+ provides :solaris_package
provides :package, os: "solaris2", platform_family: "nexentacore"
provides :package, os: "solaris2", platform_family: "solaris2", platform_version: "<= 5.10"
+
+ description "The solaris_package resource is used to manage packages for the Solaris platform."
end
end
end
diff --git a/lib/chef/resource/subversion.rb b/lib/chef/resource/subversion.rb
index 9966614eeb..d67fd22834 100644
--- a/lib/chef/resource/subversion.rb
+++ b/lib/chef/resource/subversion.rb
@@ -22,23 +22,27 @@ require "chef/resource/scm"
class Chef
class Resource
class Subversion < Chef::Resource::Scm
+ description "Use the subversion resource to manage source control resources that exist in a Subversion repository."
+
allowed_actions :force_export
- def initialize(name, run_context = nil)
- super
- @svn_arguments = "--no-auth-cache"
- @svn_info_args = "--no-auth-cache"
- @svn_binary = nil
- end
+ property :svn_arguments, [String, nil, FalseClass],
+ description: "The extra arguments that are passed to the Subversion command.",
+ coerce: proc { |v| v == false ? nil : v }, # coerce false to nil
+ default: "--no-auth-cache"
+
+ property :svn_info_args, [String, nil, FalseClass],
+ description: "Use when the svn info command is used by the chef-client and arguments need to be passed. The svn_arguments command does not work when the svn info command is used.",
+ coerce: proc { |v| v == false ? nil : v }, # coerce false to nil
+ default: "--no-auth-cache"
+
+ property :svn_binary, String,
+ description: "The location of the svn binary."
# Override exception to strip password if any, so it won't appear in logs and different Chef notifications
def custom_exception_message(e)
"#{self} (#{defined_at}) had an error: #{e.class.name}: #{svn_password ? e.message.gsub(svn_password, "[hidden_password]") : e.message}"
end
-
- def svn_binary(arg = nil)
- set_or_return(:svn_binary, arg, :kind_of => [String])
- end
end
end
end
diff --git a/lib/chef/resource/sudo.rb b/lib/chef/resource/sudo.rb
new file mode 100644
index 0000000000..af26bde595
--- /dev/null
+++ b/lib/chef/resource/sudo.rb
@@ -0,0 +1,230 @@
+#
+# Author:: Bryan W. Berry (<bryan.berry@gmail.com>)
+# Author:: Seth Vargo (<sethvargo@gmail.com>)
+#
+# Copyright:: 2011-2018, Bryan w. Berry
+# Copyright:: 2012-2018, Seth Vargo
+# Copyright:: 2015-2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class Sudo < Chef::Resource
+ resource_name :sudo
+ provides(:sudo) { true }
+
+ description "Use the sudo resource to add or remove individual sudo entries using sudoers.d files."\
+ " Sudo version 1.7.2 or newer is required to use the sudo resource, as it relies on the"\
+ " '#includedir' directive introduced in version 1.7.2. This resource does not enforce"\
+ " installation of the required sudo version. Supported releases of Ubuntu, SuSE, Debian,"\
+ " and RHEL (6+) all support this feature."
+ introduced "14.0"
+
+ # acording to the sudo man pages sudo will ignore files in an include dir that have a `.` or `~`
+ # We convert either to `__`
+ property :filename, String,
+ description: "The name of the sudoers.d file",
+ name_property: true,
+ coerce: proc { |x| x.gsub(/[\.~]/, "__") }
+
+ property :users, [String, Array],
+ description: "User(s) to provide sudo privileges to. This accepts either an array or a comma separated.",
+ default: lazy { [] },
+ coerce: proc { |x| x.is_a?(Array) ? x : x.split(/\s*,\s*/) }
+
+ property :groups, [String, Array],
+ description: "Group(s) to provide sudo privileges to. This accepts either an array or a comma separated list. Leading % on group names is optional.",
+ default: lazy { [] },
+ coerce: proc { |x| coerce_groups(x) }
+
+ property :commands, Array,
+ description: "An array of commands this sudoer can execute.",
+ default: ["ALL"]
+
+ property :host, String,
+ description: "The host to set in the sudo config.",
+ default: "ALL"
+
+ property :runas, String,
+ description: "User the command(s) can be run as",
+ default: "ALL"
+
+ property :nopasswd, [TrueClass, FalseClass],
+ description: "Allow running sudo without specifying a password sudo",
+ default: false
+
+ property :noexec, [TrueClass, FalseClass],
+ description: "Prevent commands from shelling out.",
+ default: false
+
+ property :template, String,
+ description: "The name of the erb template in your cookbook if you wish to supply your own template."
+
+ property :variables, [Hash, nil],
+ description: "The variables to pass to the custom template. Ignored if not using a custom template.",
+ default: nil
+
+ property :defaults, Array,
+ description: "An array of defaults for the user/group.",
+ default: lazy { [] }
+
+ property :command_aliases, Array,
+ description: "Command aliases that can be used as allowed commands later in the config",
+ default: lazy { [] }
+
+ property :setenv, [TrueClass, FalseClass],
+ description: "Whether to permit the preserving of environment with sudo -E.",
+ default: false
+
+ property :env_keep_add, Array,
+ description: "An array of strings to add to env_keep.",
+ default: lazy { [] }
+
+ property :env_keep_subtract, Array,
+ description: "An array of strings to remove from env_keep.",
+ default: lazy { [] }
+
+ property :visudo_path, String,
+ description: "Deprecated property. Do not use."
+
+ property :visudo_binary, String,
+ description: "The path to visudo for config verification.",
+ default: "/usr/sbin/visudo"
+
+ property :config_prefix, String,
+ description: "The directory containing the sudoers config file.",
+ default: lazy { platform_config_prefix }
+
+ # handle legacy cookbook property
+ def after_created
+ raise "The 'visudo_path' property from the sudo cookbook has been replaced with the 'visudo_binary' property. The path is now more intelligently determined and for most users specifying the path should no longer be necessary. If this resource still cannot determine the path to visudo then provide the full path to the binary with the 'visudo_binary' property." if visudo_path
+ end
+
+ # VERY old legacy properties
+ alias_method :user, :users
+ alias_method :group, :groups
+
+ # make sure each group starts with a %
+ def coerce_groups(x)
+ # split strings on the commas with optional spaces on either side
+ groups = x.is_a?(Array) ? x : x.split(/\s*,\s*/)
+
+ # make sure all the groups start with %
+ groups.map { |g| g[0] == "%" ? g : "%#{g}" }
+ end
+
+ # default config prefix paths based on platform
+ # @return [String]
+ def platform_config_prefix
+ case node["platform_family"]
+ when "smartos"
+ "/opt/local/etc"
+ when "mac_os_x"
+ "/private/etc"
+ when "freebsd"
+ "/usr/local/etc"
+ else
+ "/etc"
+ end
+ end
+
+ action :create do
+ description "Create a single sudoers config in the sudoers.d directory"
+
+ validate_properties
+
+ if docker? # don't even put this into resource collection unless we're in docker
+ declare_resource(:package, "sudo") do
+ action :nothing
+ not_if "which sudo"
+ end.run_action(:install)
+ end
+
+ target = "#{new_resource.config_prefix}/sudoers.d/"
+ declare_resource(:directory, target) unless ::File.exist?(target)
+
+ Chef::Log.warn("#{new_resource.filename} will be rendered, but will not take effect because the #{new_resource.config_prefix}/sudoers config lacks the includedir directive that loads configs from #{new_resource.config_prefix}/sudoers.d/!") if ::File.readlines("#{new_resource.config_prefix}/sudoers").grep(/includedir/).empty?
+
+ if new_resource.template
+ logger.trace("Template property provided, all other properties ignored.")
+
+ declare_resource(:template, "#{target}#{new_resource.filename}") do
+ source new_resource.template
+ mode "0440"
+ variables new_resource.variables
+ verify "#{new_resource.visudo_binary} -cf %{path}" if visudo_present?
+ action :create
+ end
+ else
+ declare_resource(:template, "#{target}#{new_resource.filename}") do
+ source ::File.expand_path("../support/sudoer.erb", __FILE__)
+ local true
+ mode "0440"
+ variables sudoer: (new_resource.groups + new_resource.users).join(","),
+ host: new_resource.host,
+ runas: new_resource.runas,
+ nopasswd: new_resource.nopasswd,
+ noexec: new_resource.noexec,
+ commands: new_resource.commands,
+ command_aliases: new_resource.command_aliases,
+ defaults: new_resource.defaults,
+ setenv: new_resource.setenv,
+ env_keep_add: new_resource.env_keep_add,
+ env_keep_subtract: new_resource.env_keep_subtract
+ verify "#{new_resource.visudo_binary} -cf %{path}" if visudo_present?
+ action :create
+ end
+ end
+ end
+
+ action :install do
+ Chef::Log.warn("The sudo :install action has been renamed :create. Please update your cookbook code for the new action")
+ action_create
+ end
+
+ action :remove do
+ Chef::Log.warn("The sudo :remove action has been renamed :delete. Please update your cookbook code for the new action")
+ action_delete
+ end
+
+ # Removes a user from the sudoers group
+ action :delete do
+ description "Remove a sudoers config from the sudoers.d directory"
+
+ file "#{new_resource.config_prefix}/sudoers.d/#{new_resource.filename}" do
+ action :delete
+ end
+ end
+
+ action_class do
+ # Ensure that the inputs are valid (we cannot just use the resource for this)
+ def validate_properties
+ # if group, user, env_keep_add, env_keep_subtract and template are nil, throw an exception
+ raise "You must specify users, groups, env_keep_add, env_keep_subtract, or template properties!" if new_resource.users.empty? && new_resource.groups.empty? && new_resource.template.nil? && new_resource.env_keep_add.empty? && new_resource.env_keep_subtract.empty?
+
+ # if specifying user or group and template at the same time fail
+ raise "You cannot specify users or groups properties and also specify a template. To use your own template pass in all template variables using the variables property." if (!new_resource.users.empty? || !new_resource.groups.empty?) && !new_resource.template.nil?
+ end
+
+ def visudo_present?
+ return true if ::File.exist?(new_resource.visudo_binary)
+ Chef::Log.warn("The visudo binary cannot be found at '#{new_resource.visudo_binary}'. Skipping sudoer file validation. If visudo is on this system you can specify the path using the 'visudo_binary' property.")
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/support/sudoer.erb b/lib/chef/resource/support/sudoer.erb
new file mode 100644
index 0000000000..d19540bd33
--- /dev/null
+++ b/lib/chef/resource/support/sudoer.erb
@@ -0,0 +1,18 @@
+# This file is managed by Chef.
+# Do NOT modify this file directly.
+
+<% @command_aliases.each do |a| -%>
+Cmnd_Alias <%= a[:name].upcase %> = <%= a[:command_list].join(', ') %>
+<% end -%>
+<% @env_keep_add.each do |env_keep| -%>
+Defaults env_keep += "<%= env_keep %>"
+<% end -%>
+<% @env_keep_subtract.each do |env_keep| -%>
+Defaults env_keep -= "<%= env_keep %>"
+<% end -%>
+<% @commands.each do |command| -%>
+<% unless @sudoer.empty? %><%= @sudoer %> <%= @host %>=(<%= @runas %>) <%= 'NOEXEC:' if @noexec %><%= 'NOPASSWD:' if @nopasswd.to_s == 'true' %><%= 'SETENV:' if @setenv.to_s == 'true' %><%= command %><% end -%>
+<% end -%>
+<% unless @defaults.empty? %>
+Defaults:<%= @sudoer %> <%= @defaults.join(',') %>
+<% end -%>
diff --git a/lib/chef/resource/swap_file.rb b/lib/chef/resource/swap_file.rb
new file mode 100644
index 0000000000..ea7440a890
--- /dev/null
+++ b/lib/chef/resource/swap_file.rb
@@ -0,0 +1,209 @@
+#
+# Copyright 2012-2018, Seth Vargo
+# Copyright 2017-2018, Chef Software, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class SwapFile < Chef::Resource
+ resource_name :swap_file
+ provides(:swap_file) { true }
+
+ description "Use the swap_file resource to create or delete swap files on Linux systems, and optionally to manage the swappiness configuration for a host."
+ introduced "14.0"
+
+ property :path, String,
+ description: "The path to put the swap file on the system.",
+ name_property: true
+
+ property :size, Integer,
+ description: "The size (in MBs) for the swap file."
+
+ property :persist, [TrueClass, FalseClass],
+ description: "Persist the swapon.",
+ default: false
+
+ property :timeout, Integer,
+ description: "Timeout for dd/fallocate.",
+ default: 600
+
+ property :swappiness, Integer,
+ description: "The swappiness value to set on the system."
+
+ action :create do
+ description "Create a swapfile."
+
+ if swap_enabled?
+ Chef::Log.debug("#{new_resource} already created - nothing to do")
+ else
+ begin
+ Chef::Log.info "starting first create: #{node['virtualization']['system']}"
+ do_create(swap_creation_command)
+ rescue Mixlib::ShellOut::ShellCommandFailed => e
+ Chef::Log.warn("#{new_resource} Rescuing failed swapfile creation for #{new_resource.path}")
+ Chef::Log.debug("#{new_resource} Exception when creating swapfile #{new_resource.path}: #{e}")
+ do_create(dd_command)
+ end
+ end
+ if new_resource.swappiness
+ declare_resource(:sysctl, "vm.swappiness") do
+ value new_resource.swappiness
+ end
+ end
+ end
+
+ action :remove do
+ description "Remove a swapfile and disable swap."
+
+ swapoff if swap_enabled?
+ remove_swapfile if ::File.exist?(new_resource.path)
+ end
+
+ action_class do
+ def do_create(command)
+ create_swapfile(command)
+ set_permissions
+ mkswap
+ swapon
+ persist if persist?
+ end
+
+ def create_swapfile(command)
+ converge_by "create empty swapfile at #{new_resource.path}" do # ~FC054
+ shell_out!(command, timeout: new_resource.timeout)
+ end
+ end
+
+ def set_permissions
+ permissions = "600"
+ converge_by "set permissions on #{new_resource.path} to #{permissions}" do
+ shell_out!("chmod #{permissions} #{new_resource.path}")
+ end
+ end
+
+ def mkswap
+ converge_by "make #{new_resource.path} swappable" do
+ shell_out!("mkswap -f #{new_resource.path}")
+ end
+ end
+
+ def swapon
+ converge_by "enable swap for #{new_resource.path}" do
+ shell_out!("swapon #{new_resource.path}")
+ end
+ end
+
+ def swapoff
+ converge_by "turn off swap for #{new_resource.path}" do
+ shell_out!("swapoff #{new_resource.path}")
+ end
+ end
+
+ def remove_swapfile
+ converge_by "remove swap file #{new_resource.path}" do
+ ::FileUtils.rm(new_resource.path)
+ end
+ end
+
+ def swap_enabled?
+ enabled_swapfiles = shell_out("swapon --summary").stdout
+ # Regex for our resource path and only our resource path
+ # It will terminate on whitespace after the path it match
+ # /testswapfile would match
+ # /testswapfiledir/someotherfile will not
+ swapfile_regex = Regexp.new("^#{new_resource.path}[\\s\\t\\n\\f]+")
+ !swapfile_regex.match(enabled_swapfiles).nil?
+ end
+
+ def swap_creation_command
+ command = if compatible_filesystem? && compatible_kernel && !docker?
+ fallocate_command
+ else
+ dd_command
+ end
+ Chef::Log.debug("#{new_resource} swap creation command is '#{command}'")
+ command
+ end
+
+ def fallback_swap_creation_command
+ command = dd_command
+ Chef::Log.debug("#{new_resource} fallback swap creation command is '#{command}'")
+ command
+ end
+
+ # The block size (1MB)
+ def block_size
+ 1_048_576
+ end
+
+ def fallocate_size
+ size = block_size * new_resource.size
+ Chef::Log.debug("#{new_resource} fallocate size is #{size}")
+ size
+ end
+
+ def fallocate_command
+ size = fallocate_size
+ command = "fallocate -l #{size} #{new_resource.path}"
+ Chef::Log.debug("#{new_resource} fallocate command is '#{command}'")
+ command
+ end
+
+ def dd_command
+ command = "dd if=/dev/zero of=#{new_resource.path} bs=#{block_size} count=#{new_resource.size}"
+ Chef::Log.debug("#{new_resource} dd command is '#{command}'")
+ command
+ end
+
+ def compatible_kernel
+ fallocate_location = shell_out("which fallocate").stdout
+ Chef::Log.debug("#{new_resource} fallocate location is '#{fallocate_location}'")
+ ::File.exist?(fallocate_location.chomp)
+ end
+
+ def compatible_filesystem?
+ compatible_filesystems = %w{xfs ext4}
+ parent_directory = ::File.dirname(new_resource.path)
+ # Get FS info, get second line as first is column headings
+ command = "df -PT #{parent_directory} | awk 'NR==2 {print $2}'"
+ result = shell_out(command).stdout
+ Chef::Log.debug("#{new_resource} filesystem listing is '#{result}'")
+ compatible_filesystems.any? { |fs| result.include? fs }
+ end
+
+ def persist?
+ !!new_resource.persist
+ end
+
+ def persist
+ fstab = "/etc/fstab"
+ contents = ::File.readlines(fstab)
+ addition = "#{new_resource.path} swap swap defaults 0 0"
+
+ if contents.any? { |line| line.strip == addition }
+ Chef::Log.debug("#{new_resource} already added to /etc/fstab - skipping")
+ else
+ Chef::Log.info("#{new_resource} adding entry to #{fstab} for #{new_resource.path}")
+
+ contents << "#{addition}\n"
+ ::File.open(fstab, "w") { |f| f.write(contents.join("")) }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/sysctl.rb b/lib/chef/resource/sysctl.rb
new file mode 100644
index 0000000000..aab1c055c1
--- /dev/null
+++ b/lib/chef/resource/sysctl.rb
@@ -0,0 +1,115 @@
+#
+# Copyright:: 2018, Webb Agile Solutions Ltd.
+# Copyright:: 2018, Chef Software Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class Sysctl < Chef::Resource
+ resource_name :sysctl
+ provides(:sysctl) { true }
+ provides(:sysctl_param) { true }
+
+ description "Use the sysctl resource to set kernel parameters using the sysctl"\
+ " command line tool and configuration files in the system's sysctl.d directory."\
+ "Configuration files managed by this resource are named 99-chef-KEYNAME.conf. If"\
+ " an existing value was already set for the value it will be backed up to the node"\
+ " and restored if the :remove action is used later."
+
+ introduced "14.0"
+
+ property :key, String,
+ description: "The kernel parameter key in dotted format.",
+ name_property: true
+
+ property :ignore_error, [TrueClass, FalseClass],
+ description: "Ignore any errors when setting the value on the command line.",
+ default: false, desired_state: false
+
+ property :value, [Array, String, Integer, Float],
+ description: "The value to set.",
+ coerce: proc { |v| coerce_value(v) },
+ required: true
+
+ property :conf_dir, String,
+ description: "The configuration directory to write the config to.",
+ default: "/etc/sysctl.d"
+
+ def after_created
+ raise "The systctl resource requires Linux as it needs sysctl and the systctl.d directory functionality." unless node["os"] == "linux"
+ raise "The systctl resource does not support SLES releases less than 12 as it requires a systctl.d directory" if platform_family?("suse") && node["platform_version"].to_i < 12
+ end
+
+ def coerce_value(v)
+ case v
+ when Array
+ v.join(" ")
+ else
+ v.to_s
+ end
+ end
+
+ # shellout to systctl to get the current value
+ # ignore missing keys by using '-e'
+ # convert tabs to spaces since systctl tab deliminates multivalue parameters
+ # strip the newline off the end of the output as well
+ load_current_value do
+ value shell_out!("sysctl -n -e #{key}").stdout.tr("\t", " ").strip
+ end
+
+ action :apply do
+ converge_if_changed do
+ # set it temporarily
+ set_sysctl_param(new_resource.key, new_resource.value)
+
+ directory new_resource.conf_dir
+
+ file "#{new_resource.conf_dir}/99-chef-#{new_resource.key}.conf" do
+ content "#{new_resource.key} = #{new_resource.value}"
+ end
+
+ execute "Load sysctl values" do
+ command "sysctl #{'-e ' if new_resource.ignore_error}-p"
+ action :run
+ end
+ end
+ end
+
+ action :remove do
+ # only converge the resource if the file actually exists to delete
+ if ::File.exist?("#{new_resource.conf_dir}/99-chef-#{new_resource.key}.conf")
+ converge_by "removing systctl config at #{new_resource.conf_dir}/99-chef-#{new_resource.key}.conf" do
+ file "#{new_resource.conf_dir}/99-chef-#{new_resource.key}.conf" do
+ action :delete
+ end
+
+ execute "Load sysctl values" do
+ command "sysctl -p"
+ action :run
+ end
+ end
+ end
+ end
+
+ action_class do
+ def set_sysctl_param(key, value)
+ shell_out!("sysctl #{'-e ' if new_resource.ignore_error}-w \"#{key}=#{value}\"")
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/systemd_unit.rb b/lib/chef/resource/systemd_unit.rb
index 525e9ab35e..02194927ba 100644
--- a/lib/chef/resource/systemd_unit.rb
+++ b/lib/chef/resource/systemd_unit.rb
@@ -1,6 +1,6 @@
#
# Author:: Nathan Williams (<nath.e.will@gmail.com>)
-# Copyright:: Copyright 2016, Nathan Williams
+# Copyright:: Copyright 2016-2018, Nathan Williams
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,23 +22,37 @@ require "iniparse"
class Chef
class Resource
class SystemdUnit < Chef::Resource
- resource_name :systemd_unit
+ resource_name(:systemd_unit) { true }
+
+ description "Use the systemd_unit resource to create, manage, and run systemd units."
+ introduced "12.11"
default_action :nothing
allowed_actions :create, :delete,
- :enable, :disable,
+ :preset, :revert,
+ :enable, :disable, :reenable,
:mask, :unmask,
:start, :stop,
- :restart, :reload
+ :restart, :reload,
+ :try_restart, :reload_or_restart,
+ :reload_or_try_restart
+ # Internal provider-managed properties
property :enabled, [TrueClass, FalseClass]
property :active, [TrueClass, FalseClass]
property :masked, [TrueClass, FalseClass]
property :static, [TrueClass, FalseClass]
+
+ # User-provided properties
property :user, String, desired_state: false
property :content, [String, Hash]
property :triggers_reload, [TrueClass, FalseClass],
default: true, desired_state: false
+ property :verify, [TrueClass, FalseClass],
+ default: true, desired_state: false
+ property :unit_name, String, desired_state: false,
+ identity: true,
+ name_property: true
def to_ini
case content
diff --git a/lib/chef/resource/template.rb b/lib/chef/resource/template.rb
index 896aa71340..5fc29ec1c6 100644
--- a/lib/chef/resource/template.rb
+++ b/lib/chef/resource/template.rb
@@ -19,12 +19,23 @@
#
require "chef/resource/file"
-require "chef/provider/template"
require "chef/mixin/securable"
class Chef
class Resource
+ # A cookbook template is an Embedded Ruby (ERB) template that is used to dynamically generate static text files.
+ # Templates may contain Ruby expressions and statements, and are a great way to manage configuration files. Use the
+ # template resource to add cookbook templates to recipes; place the corresponding Embedded Ruby (ERB) template file
+ # in a cookbook’s /templates directory.
+ #
+ # Use the template resource to manage the contents of a file using an Embedded Ruby (ERB) template by transferring
+ # files from a sub-directory of COOKBOOK_NAME/templates/ to a specified path located on a host that is running the
+ # chef-client. This resource includes actions and properties from the file resource. Template files managed by the
+ # template resource follow the same file specificity rules as the remote_file and file resources.
class Template < Chef::Resource::File
+ resource_name :template
+ provides :template
+
include Chef::Mixin::Securable
attr_reader :inline_helper_blocks
@@ -33,9 +44,6 @@ class Chef
def initialize(name, run_context = nil)
super
@source = "#{::File.basename(name)}.erb"
- @cookbook = nil
- @local = false
- @variables = Hash.new
@inline_helper_blocks = {}
@inline_helper_modules = []
@helper_modules = []
@@ -49,29 +57,9 @@ class Chef
)
end
- def variables(args = nil)
- set_or_return(
- :variables,
- args,
- :kind_of => [ Hash ]
- )
- end
-
- def cookbook(args = nil)
- set_or_return(
- :cookbook,
- args,
- :kind_of => [ String ]
- )
- end
-
- def local(args = nil)
- set_or_return(
- :local,
- args,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :variables, Hash, default: lazy { Hash.new }
+ property :cookbook, String
+ property :local, [ TrueClass, FalseClass ], default: false
# Declares a helper method to be defined in the template context when
# rendering.
diff --git a/lib/chef/resource/user.rb b/lib/chef/resource/user.rb
index 012fa278f1..39d8159d4f 100644
--- a/lib/chef/resource/user.rb
+++ b/lib/chef/resource/user.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,7 +20,9 @@ require "chef/resource"
class Chef
class Resource
+ # Use the user resource to add users, update existing users, remove users, and to lock/unlock user passwords.
class User < Chef::Resource
+ resource_name :user_resource_abstract_base_class # this prevents magickal class name DSL wiring
identity_attr :username
state_attrs :uid, :gid, :home
@@ -41,10 +43,6 @@ class Chef
@manage_home = false
@force = false
@non_unique = false
- @supports = {
- :manage_home => false,
- :non_unique => false,
- }
@iterations = 27855
@salt = nil
end
@@ -65,19 +63,21 @@ class Chef
)
end
- def uid(arg = nil)
+ def uid(arg = Chef::NOT_PASSED)
set_or_return(
:uid,
arg,
- :kind_of => [ String, Integer ]
+ :kind_of => [ String, Integer, NilClass ],
+ :coerce => proc { |x| x || nil }
)
end
- def gid(arg = nil)
+ def gid(arg = Chef::NOT_PASSED)
set_or_return(
:gid,
arg,
- :kind_of => [ String, Integer ]
+ :kind_of => [ String, Integer, NilClass ],
+ :coerce => proc { |x| x || nil }
)
end
@@ -154,7 +154,6 @@ class Chef
:kind_of => [ TrueClass, FalseClass ]
)
end
-
end
end
end
diff --git a/lib/chef/resource/timestamped_deploy.rb b/lib/chef/resource/user/aix_user.rb
index 1d6b07a719..d5f1829b63 100644
--- a/lib/chef/resource/timestamped_deploy.rb
+++ b/lib/chef/resource/user/aix_user.rb
@@ -1,6 +1,5 @@
#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2009-2016, Daniel DeLeo
+# Copyright:: Copyright 2016-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +15,17 @@
# limitations under the License.
#
+require "chef/resource/user"
+
class Chef
class Resource
- # Convenience class for using the deploy resource with the timestamped
- # deployment strategy (provider)
- class TimestampedDeploy < Chef::Resource::Deploy
+ class User
+ class AixUser < Chef::Resource::User
+ resource_name :aix_user
+
+ provides :aix_user
+ provides :user, os: "aix"
+ end
end
end
end
diff --git a/lib/chef/provider/deploy/timestamped.rb b/lib/chef/resource/user/dscl_user.rb
index 5486b092d3..6eb1c953f4 100644
--- a/lib/chef/provider/deploy/timestamped.rb
+++ b/lib/chef/resource/user/dscl_user.rb
@@ -1,6 +1,5 @@
#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2009-2016, Daniel DeLeo
+# Copyright:: Copyright 2016-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +15,16 @@
# limitations under the License.
#
-class Chef
- class Provider
- class Deploy
- class Timestamped < Chef::Provider::Deploy
- provides :timestamped_deploy
- provides :deploy
+require "chef/resource/user"
- protected
+class Chef
+ class Resource
+ class User
+ class DsclUser < Chef::Resource::User
+ resource_name :dscl_user
- def release_slug
- Time.now.utc.strftime("%Y%m%d%H%M%S")
- end
+ provides :dscl_user
+ provides :user, os: "darwin"
end
end
end
diff --git a/lib/chef/resource/deploy_revision.rb b/lib/chef/resource/user/linux_user.rb
index 41046ec288..056a421197 100644
--- a/lib/chef/resource/deploy_revision.rb
+++ b/lib/chef/resource/user/linux_user.rb
@@ -1,6 +1,5 @@
#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2009-2016, Daniel DeLeo
+# Copyright:: Copyright 2016-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,16 +15,18 @@
# limitations under the License.
#
+require "chef/resource/user"
+
class Chef
class Resource
+ class User
+ class LinuxUser < Chef::Resource::User
+ resource_name :linux_user
- # Convenience class for using the deploy resource with the revision
- # deployment strategy (provider)
- class DeployRevision < Chef::Resource::Deploy
- end
+ provides :linux_user
+ provides :user, os: "linux"
- class DeployBranch < Chef::Resource::DeployRevision
+ end
end
-
end
end
diff --git a/lib/chef/mixin/language_include_recipe.rb b/lib/chef/resource/user/pw_user.rb
index 97e384c7c4..3672943f5c 100644
--- a/lib/chef/mixin/language_include_recipe.rb
+++ b/lib/chef/resource/user/pw_user.rb
@@ -1,6 +1,5 @@
#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2016-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,16 +15,17 @@
# limitations under the License.
#
-require "chef/dsl/include_recipe"
-require "chef/mixin/deprecation"
+require "chef/resource/user"
class Chef
- module Mixin
-
- deprecate_constant(:LanguageIncludeRecipe, Chef::DSL::IncludeRecipe, <<-EOM)
-Chef::Mixin::LanguageIncludeRecipe is deprecated, use Chef::DSL::IncludeRecipe
-instead.
-EOM
+ class Resource
+ class User
+ class PwUser < Chef::Resource::User
+ resource_name :pw_user
+ provides :pw_user
+ provides :user, os: "freebsd"
+ end
+ end
end
end
diff --git a/lib/chef/resource/user/solaris_user.rb b/lib/chef/resource/user/solaris_user.rb
new file mode 100644
index 0000000000..cca90e6743
--- /dev/null
+++ b/lib/chef/resource/user/solaris_user.rb
@@ -0,0 +1,31 @@
+#
+# Copyright:: Copyright 2016-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource/user"
+
+class Chef
+ class Resource
+ class User
+ class SolarisUser < Chef::Resource::User
+ resource_name :solaris_user
+
+ provides :solaris_user
+ provides :user, os: %w{omnios solaris2}
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/user/windows_user.rb b/lib/chef/resource/user/windows_user.rb
new file mode 100644
index 0000000000..baedc14f5e
--- /dev/null
+++ b/lib/chef/resource/user/windows_user.rb
@@ -0,0 +1,31 @@
+#
+# Copyright:: Copyright 2016-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource/user"
+
+class Chef
+ class Resource
+ class User
+ class WindowsUser < Chef::Resource::User
+ resource_name :windows_user
+
+ provides :windows_user
+ provides :user, os: "windows"
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_ad_join.rb b/lib/chef/resource/windows_ad_join.rb
new file mode 100644
index 0000000000..7c026dba28
--- /dev/null
+++ b/lib/chef/resource/windows_ad_join.rb
@@ -0,0 +1,92 @@
+#
+# Author:: John Snow (<jsnow@chef.io>)
+# Copyright:: 2016-2018, John Snow
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+require "chef/mixin/powershell_out"
+
+class Chef
+ class Resource
+ class WindowsAdJoin < Chef::Resource
+ resource_name :windows_ad_join
+ provides :windows_ad_join
+
+ include Chef::Mixin::PowershellOut
+
+ description "Use the windows_ad_join resource to join a Windows Active Directory domain."
+ introduced "14.0"
+
+ property :domain_name, String,
+ description: "The FQDN of the AD domain to join.",
+ validation_message: "The 'domain_name' property must be a FQDN.",
+ regex: /.\../, # anything.anything
+ name_property: true
+
+ property :domain_user, String,
+ description: "The domain user to use to join the host to the domain.",
+ required: true
+
+ property :domain_password, String,
+ description: "The password for the domain user.",
+ required: true
+
+ property :ou_path, String,
+ description: "The path to the OU where you would like to place the host."
+
+ property :reboot, Symbol,
+ equal_to: [:immediate, :delayed, :never],
+ validation_message: "The reboot property accepts :immediate (reboot as soon as the resource completes), :delayed (reboot once the Chef run completes), and :never (Don't reboot)",
+ description: "Controls the system reboot behavior post domain joining. Reboot immediately, after the Chef run completes, or never. Note that a reboot is necessary for changes to take effect.",
+ default: :immediate
+
+ # define this again so we can default it to true. Otherwise failures print the password
+ property :sensitive, [TrueClass, FalseClass],
+ default: true
+
+ action :join do
+ description "Join the Active Directory domain."
+
+ unless on_domain?
+ cmd = "$pswd = ConvertTo-SecureString \'#{new_resource.domain_password}\' -AsPlainText -Force;"
+ cmd << "$credential = New-Object System.Management.Automation.PSCredential (\"#{new_resource.domain_user}\",$pswd);"
+ cmd << "Add-Computer -DomainName #{new_resource.domain_name} -Credential $credential"
+ cmd << " -OUPath \"#{new_resource.ou_path}\"" if new_resource.ou_path
+ cmd << " -Force"
+
+ converge_by("join Active Directory domain #{new_resource.domain_name}") do
+ ps_run = powershell_out(cmd)
+ raise "Failed to join the domain #{new_resource.domain_name}: #{ps_run.stderr}}" if ps_run.error?
+
+ unless new_resource.reboot == :never
+ declare_resource(:reboot, "Reboot to join domain #{new_resource.domain_name}") do
+ action new_resource.reboot
+ reason "Reboot to join domain #{new_resource.domain_name}"
+ end
+ end
+ end
+ end
+ end
+
+ action_class do
+ def on_domain?
+ node_domain = powershell_out!("(Get-WmiObject Win32_ComputerSystem).Domain")
+ raise "Failed to check if the system is joined to the domain #{new_resource.domain_name}: #{node_domain.stderr}}" if node_domain.error?
+ node_domain.stdout.downcase.strip == new_resource.domain_name.downcase
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_auto_run.rb b/lib/chef/resource/windows_auto_run.rb
new file mode 100644
index 0000000000..e483462670
--- /dev/null
+++ b/lib/chef/resource/windows_auto_run.rb
@@ -0,0 +1,87 @@
+#
+# Author:: Paul Morton (<pmorton@biaprotect.com>)
+# Copyright:: 2011-2018, Business Intelligence Associates, Inc.
+# Copyright:: 2017-2018, Chef Software, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsAutorun < Chef::Resource
+ resource_name :windows_auto_run
+ provides(:windows_auto_run) { true }
+
+ description "Use the windows_auto_run resource to set applications to run at logon."
+ introduced "14.0"
+
+ property :program_name, String,
+ description: "The name of the program to run at login if different from the resource name.",
+ name_property: true
+
+ property :path, String,
+ coerce: proc { |x| x.tr("/", "\\") }, # make sure we have windows paths for the registry
+ description: "The path to the program to be run at login."
+
+ property :args, String,
+ description: "Any arguments for the program."
+
+ property :root, Symbol,
+ description: "The registry root key to put the entry under.",
+ equal_to: %i{machine user},
+ default: :machine
+
+ alias_method :program, :path
+
+ action :create do
+ description "Create an item to be run at login."
+
+ data = "\"#{new_resource.path}\""
+ data << " #{new_resource.args}" if new_resource.args
+
+ declare_resource(:registry_key, registry_path) do
+ values [{
+ name: new_resource.program_name,
+ type: :string,
+ data: data,
+ }]
+ action :create
+ end
+ end
+
+ action :remove do
+ description "Remove an item that was previously setup to run at login"
+
+ declare_resource(:registry_key, registry_path) do
+ values [{
+ name: new_resource.program_name,
+ type: :string,
+ data: "",
+ }]
+ action :delete
+ end
+ end
+
+ action_class do
+ # determine the full registry path based on the root property
+ # @return [String]
+ def registry_path
+ { machine: "HKLM", user: "HKCU" }[new_resource.root] + \
+ '\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/env.rb b/lib/chef/resource/windows_env.rb
index 7fac8af40b..3f1c13ecef 100644
--- a/lib/chef/resource/env.rb
+++ b/lib/chef/resource/windows_env.rb
@@ -17,49 +17,26 @@
# limitations under the License.
#
+require "chef/resource"
+
class Chef
class Resource
- class Env < Chef::Resource
-
- identity_attr :key_name
-
- state_attrs :value
+ class WindowsEnv < Chef::Resource
+ resource_name :windows_env
+ provides :windows_env
+ provides :env # backwards compat with the pre-Chef 14 resource name
- provides :env, os: "windows"
+ description "Use the env resource to manage environment keys in Microsoft Windows."\
+ " After an environment key is set, Microsoft Windows must be restarted"\
+ " before the environment key will be available to the Task Scheduler."
default_action :create
allowed_actions :create, :delete, :modify
- def initialize(name, run_context = nil)
- super
- @key_name = name
- @value = nil
- @delim = nil
- end
-
- def key_name(arg = nil)
- set_or_return(
- :key_name,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def value(arg = nil)
- set_or_return(
- :value,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def delim(arg = nil)
- set_or_return(
- :delim,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :key_name, String, identity: true, name_property: true
+ property :value, String, required: true
+ property :delim, [ String, nil, false ], desired_state: false
+ property :user, String, default: "<System>"
end
end
end
diff --git a/lib/chef/resource/windows_feature.rb b/lib/chef/resource/windows_feature.rb
new file mode 100644
index 0000000000..c80b875fbd
--- /dev/null
+++ b/lib/chef/resource/windows_feature.rb
@@ -0,0 +1,95 @@
+#
+# Author:: Seth Chisamore (<schisamo@chef.io>)
+#
+# Copyright:: 2011-2018, Chef Software, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsFeature < Chef::Resource
+ resource_name :windows_feature
+ provides(:windows_feature) { true }
+
+ description "Use the windows_feature resource to add, remove or delete Windows features and roles. This resource calls"\
+ " the 'windows_feature_dism' or 'windows_feature_powershell' resources depending on the specified installation"\
+ " method and defaults to dism, which is available on both Workstation and Server editions of Windows."
+ introduced "14.0"
+
+ property :feature_name, [Array, String],
+ description: "The name of the feature/role(s) to install. The same feature may have different"\
+ " names depending on the underlying resource being used (ie DHCPServer vs DHCP;"\
+ " DNS-Server-Full-Role vs DNS).",
+ name_property: true
+
+ property :source, String,
+ description: "Use a local repository for the feature install."
+
+ property :all, [TrueClass, FalseClass],
+ description: "Install all sub features.",
+ default: false
+
+ property :management_tools, [TrueClass, FalseClass],
+ description: "Install all applicable management tools of the roles, role services, or features (PowerShell only).",
+ default: false
+
+ property :install_method, Symbol,
+ description: "If DISM or PowerShell should be used for the installation. Note feature names differ"\
+ " between the two installation methods.",
+ equal_to: [:windows_feature_dism, :windows_feature_powershell, :windows_feature_servermanagercmd]
+
+ property :timeout, Integer,
+ description: "Specifies a timeout (in seconds) for feature install.",
+ default: 600
+
+ action :install do
+ description "Install a Windows role/feature"
+
+ run_default_subresource :install
+ end
+
+ action :remove do
+ description "Remove a Windows role/feature"
+
+ run_default_subresource :remove
+ end
+
+ action :delete do
+ description "Remove a Windows role/feature from the image"
+
+ run_default_subresource :delete
+ end
+
+ action_class do
+ # call the appropriate windows_feature resource based on the specified subresource
+ # @return [void]
+ def run_default_subresource(desired_action)
+ raise "Support for Windows feature installation via servermanagercmd.exe has been removed as this support is no longer needed in Windows 2008 R2 and above. You will need to update your cookbook to install either via dism or powershell (preferred)." if new_resource.install_method == :windows_feature_servermanagercmd
+
+ subresource = new_resource.install_method || :windows_feature_dism
+ declare_resource(subresource, new_resource.name) do
+ action desired_action
+ feature_name new_resource.feature_name
+ source new_resource.source if new_resource.source
+ all new_resource.all
+ timeout new_resource.timeout
+ management_tools new_resource.management_tools if subresource == :windows_feature_powershell
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_feature_dism.rb b/lib/chef/resource/windows_feature_dism.rb
new file mode 100644
index 0000000000..ebd52c4db7
--- /dev/null
+++ b/lib/chef/resource/windows_feature_dism.rb
@@ -0,0 +1,226 @@
+#
+# Author:: Seth Chisamore (<schisamo@chef.io>)
+#
+# Copyright:: 2011-2018, Chef Software, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsFeatureDism < Chef::Resource
+ resource_name :windows_feature_dism
+ provides(:windows_feature_dism) { true }
+
+ description "Using the windows_feature_dism resource to add, remove or"\
+ " delete Windows features and roles using DISM"
+ introduced "14.0"
+
+ property :feature_name, [Array, String],
+ description: "The name of the feature/role(s) to install if it differs from the resource name.",
+ coerce: proc { |x| to_lowercase_array(x) },
+ name_property: true
+
+ property :source, String,
+ description: "Use a local repository for the feature install."
+
+ property :all, [TrueClass, FalseClass],
+ description: "Install all sub features. This is the equivalent of specifying the /All switch to dism.exe",
+ default: false
+
+ property :timeout, Integer,
+ description: "Specifies a timeout (in seconds) for feature install.",
+ default: 600
+
+ def to_lowercase_array(x)
+ x = x.split(/\s*,\s*/) if x.is_a?(String) # split multiple forms of a comma separated list
+
+ # dism on windows < 2012 is case sensitive so only downcase when on 2012+
+ # @todo when we're really ready to remove support for Windows 2008 R2 this check can go away
+ node["platform_version"].to_f < 6.2 ? x : x.map(&:downcase)
+ end
+
+ action :install do
+ description "Install a Windows role/feature using DISM"
+
+ reload_cached_dism_data unless node["dism_features_cache"]
+ fail_if_unavailable # fail if the features don't exist
+ fail_if_removed # fail if the features are in removed state
+
+ logger.trace("Windows features needing installation: #{features_to_install.empty? ? 'none' : features_to_install.join(',')}")
+ unless features_to_install.empty?
+ message = "install Windows feature#{'s' if features_to_install.count > 1} #{features_to_install.join(',')}"
+ converge_by(message) do
+ install_command = "dism.exe /online /enable-feature #{features_to_install.map { |f| "/featurename:#{f}" }.join(' ')} /norestart"
+ install_command << " /LimitAccess /Source:\"#{new_resource.source}\"" if new_resource.source
+ install_command << " /All" if new_resource.all
+
+ shell_out!(install_command, returns: [0, 42, 127, 3010], timeout: new_resource.timeout)
+
+ reload_cached_dism_data # Reload cached dism feature state
+ end
+ end
+ end
+
+ action :remove do
+ description "Remove a Windows role/feature using DISM"
+
+ reload_cached_dism_data unless node["dism_features_cache"]
+
+ logger.trace("Windows features needing removal: #{features_to_remove.empty? ? 'none' : features_to_remove.join(',')}")
+ unless features_to_remove.empty?
+ message = "remove Windows feature#{'s' if features_to_remove.count > 1} #{features_to_remove.join(',')}"
+
+ converge_by(message) do
+ shell_out!("dism.exe /online /disable-feature #{features_to_remove.map { |f| "/featurename:#{f}" }.join(' ')} /norestart", returns: [0, 42, 127, 3010], timeout: new_resource.timeout)
+
+ reload_cached_dism_data # Reload cached dism feature state
+ end
+ end
+ end
+
+ action :delete do
+ description "Remove a Windows role/feature from the image using DISM"
+
+ raise_if_delete_unsupported
+
+ reload_cached_dism_data unless node["dism_features_cache"]
+
+ fail_if_unavailable # fail if the features don't exist
+
+ logger.trace("Windows features needing deletion: #{features_to_delete.empty? ? 'none' : features_to_delete.join(',')}")
+ unless features_to_delete.empty?
+ message = "delete Windows feature#{'s' if features_to_delete.count > 1} #{features_to_delete.join(',')} from the image"
+ converge_by(message) do
+ shell_out!("dism.exe /online /disable-feature #{features_to_delete.map { |f| "/featurename:#{f}" }.join(' ')} /Remove /norestart", returns: [0, 42, 127, 3010], timeout: new_resource.timeout)
+
+ reload_cached_dism_data # Reload cached dism feature state
+ end
+ end
+ end
+
+ action_class do
+ # @return [Array] features the user has requested to install which need installation
+ def features_to_install
+ @install ||= begin
+ # disabled features are always available to install
+ available_for_install = node["dism_features_cache"]["disabled"]
+
+ # if the user passes a source then removed features are also available for installation
+ available_for_install.concat(node["dism_features_cache"]["removed"]) if new_resource.source
+
+ # the intersection of the features to install & disabled/removed(if passing source) features are what needs installing
+ new_resource.feature_name & available_for_install
+ end
+ end
+
+ # @return [Array] features the user has requested to remove which need removing
+ def features_to_remove
+ # the intersection of the features to remove & enabled features are what needs removing
+ @remove ||= new_resource.feature_name & node["dism_features_cache"]["enabled"]
+ end
+
+ # @return [Array] features the user has requested to delete which need deleting
+ def features_to_delete
+ # the intersection of the features to remove & enabled/disabled features are what needs removing
+ @remove ||= begin
+ all_available = node["dism_features_cache"]["enabled"] +
+ node["dism_features_cache"]["disabled"]
+ new_resource.feature_name & all_available
+ end
+ end
+
+ # if any features are not supported on this release of Windows or
+ # have been deleted raise with a friendly message. At one point in time
+ # we just warned, but this goes against the behavior of ever other package
+ # provider in Chef and it isn't clear what you'd want if you passed an array
+ # and some features were available and others were not.
+ # @return [void]
+ def fail_if_unavailable
+ all_available = node["dism_features_cache"]["enabled"] +
+ node["dism_features_cache"]["disabled"] +
+ node["dism_features_cache"]["removed"]
+
+ # the difference of desired features to install to all features is what's not available
+ unavailable = (new_resource.feature_name - all_available)
+ raise "The Windows feature#{'s' if unavailable.count > 1} #{unavailable.join(',')} #{unavailable.count > 1 ? 'are' : 'is'} not available on this version of Windows. Run 'dism /online /Get-Features' to see the list of available feature names." unless unavailable.empty?
+ end
+
+ # run dism.exe to get a list of all available features and their state
+ # and save that to the node at node.override level.
+ # We do this because getting a list of features in dism takes at least a second
+ # and this data will be persisted across multiple resource runs which gives us
+ # a much faster run when no features actually need to be installed / removed.
+ # @return [void]
+ def reload_cached_dism_data
+ logger.trace("Caching Windows features available via dism.exe.")
+ node.override["dism_features_cache"] = Mash.new
+ node.override["dism_features_cache"]["enabled"] = []
+ node.override["dism_features_cache"]["disabled"] = []
+ node.override["dism_features_cache"]["removed"] = []
+
+ # Grab raw feature information from dism command line
+ raw_list_of_features = shell_out("dism.exe /Get-Features /Online /Format:Table /English").stdout
+
+ # Split stdout into an array by windows line ending
+ features_list = raw_list_of_features.split("\r\n")
+ features_list.each do |feature_details_raw|
+ case feature_details_raw
+ when /Payload Removed/ # matches 'Disabled with Payload Removed'
+ add_to_feature_mash("removed", feature_details_raw)
+ when /Enable/ # matches 'Enabled' and 'Enable Pending' aka after reboot
+ add_to_feature_mash("enabled", feature_details_raw)
+ when /Disable/ # matches 'Disabled' and 'Disable Pending' aka after reboot
+ add_to_feature_mash("disabled", feature_details_raw)
+ end
+ end
+ logger.trace("The cache contains\n#{node['dism_features_cache']}")
+ end
+
+ # parse the feature string and add the values to the appropriate array
+ # in the
+ # strips trailing whitespace characters then split on n number of spaces
+ # + | + n number of spaces
+ # @return [void]
+ def add_to_feature_mash(feature_type, feature_string)
+ feature_details = feature_string.strip.split(/\s+[|]\s+/).first
+
+ # dism on windows 2012+ isn't case sensitive so it's best to compare
+ # lowercase lists so the user input doesn't need to be case sensitive
+ # @todo when we're ready to remove windows 2008R2 the gating here can go away
+ feature_details.downcase! unless node["platform_version"].to_f < 6.2
+ node.override["dism_features_cache"][feature_type] << feature_details
+ end
+
+ # Fail if any of the packages are in a removed state
+ # @return [void]
+ def fail_if_removed
+ return if new_resource.source # if someone provides a source then all is well
+ if node["platform_version"].to_f > 6.2
+ return if registry_key_exists?('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing') && registry_value_exists?('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing', name: "LocalSourcePath") # if source is defined in the registry, still fine
+ end
+ removed = new_resource.feature_name & node["dism_features_cache"]["removed"]
+ raise "The Windows feature#{'s' if removed.count > 1} #{removed.join(',')} #{removed.count > 1 ? 'are' : 'is'} have been removed from the host and cannot be installed." unless removed.empty?
+ end
+
+ # Fail unless we're on windows 8+ / 2012+ where deleting a feature is supported
+ # @return [void]
+ def raise_if_delete_unsupported
+ raise Chef::Exceptions::UnsupportedAction, "#{self} :delete action not support on Windows releases before Windows 8/2012. Cannot continue!" unless node["platform_version"].to_f >= 6.2
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_feature_powershell.rb b/lib/chef/resource/windows_feature_powershell.rb
new file mode 100644
index 0000000000..82713e3438
--- /dev/null
+++ b/lib/chef/resource/windows_feature_powershell.rb
@@ -0,0 +1,256 @@
+#
+# Author:: Greg Zapp (<greg.zapp@gmail.com>)
+#
+# Copyright:: 2015-2018, Chef Software, Inc
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/mixin/powershell_out"
+require "chef/json_compat"
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsFeaturePowershell < Chef::Resource
+ resource_name :windows_feature_powershell
+ provides(:windows_feature_powershell) { true }
+
+ description "Use the windows_feature_powershell resource to add, remove or"\
+ " delete Windows features and roles using PowerShell. This resource"\
+ " offers significant speed benefits over the windows_feature_dism resource,"\
+ " but requires installing the Remote Server Administration Tools on"\
+ " non-server releases of Windows"
+ introduced "14.0"
+
+ property :feature_name, [Array, String],
+ description: "The name of the feature/role(s) to install if it differs from the resource name.",
+ coerce: proc { |x| to_lowercase_array(x) },
+ name_property: true
+
+ property :source, String,
+ description: "Use a local repository for the feature install."
+
+ property :all, [TrueClass, FalseClass],
+ description: "Install all sub features. This is equivalent to using the"\
+ " -InstallAllSubFeatures switch with Add-WindowsFeature.",
+ default: false
+
+ property :timeout, Integer,
+ description: "Specifies a timeout (in seconds) for feature install.",
+ default: 600
+
+ property :management_tools, [TrueClass, FalseClass],
+ description: "",
+ default: false
+
+ def to_lowercase_array(x)
+ x = x.split(/\s*,\s*/) if x.is_a?(String) # split multiple forms of a comma separated list
+ x.map(&:downcase)
+ end
+
+ include Chef::Mixin::PowershellOut
+
+ action :install do
+ raise_on_old_powershell
+
+ reload_cached_powershell_data unless node["powershell_features_cache"]
+ fail_if_unavailable # fail if the features don't exist
+ fail_if_removed # fail if the features are in removed state
+
+ Chef::Log.debug("Windows features needing installation: #{features_to_install.empty? ? 'none' : features_to_install.join(',')}")
+ unless features_to_install.empty?
+ converge_by("install Windows feature#{'s' if features_to_install.count > 1} #{features_to_install.join(',')}") do
+ install_command = "#{install_feature_cmdlet} #{features_to_install.join(',')}"
+ install_command << " -IncludeAllSubFeature" if new_resource.all
+ if node["platform_version"].to_f < 6.2 && (new_resource.source || new_resource.management_tools)
+ Chef::Log.warn("The 'source' and 'management_tools' properties are not available on Windows 2012R2 or great. Skipping these properties!")
+ else
+ install_command << " -Source \"#{new_resource.source}\"" if new_resource.source
+ install_command << " -IncludeManagementTools" if new_resource.management_tools
+ end
+
+ cmd = powershell_out!(install_command, timeout: new_resource.timeout)
+ Chef::Log.info(cmd.stdout)
+
+ reload_cached_powershell_data # Reload cached powershell feature state
+ end
+ end
+ end
+
+ action :remove do
+ raise_on_old_powershell
+
+ reload_cached_powershell_data unless node["powershell_features_cache"]
+
+ Chef::Log.debug("Windows features needing removal: #{features_to_remove.empty? ? 'none' : features_to_remove.join(',')}")
+
+ unless features_to_remove.empty?
+ converge_by("remove Windows feature#{'s' if features_to_remove.count > 1} #{features_to_remove.join(',')}") do
+ cmd = powershell_out!("#{remove_feature_cmdlet} #{features_to_remove.join(',')}", timeout: new_resource.timeout)
+ Chef::Log.info(cmd.stdout)
+
+ reload_cached_powershell_data # Reload cached powershell feature state
+ end
+ end
+ end
+
+ action :delete do
+ raise_on_old_powershell
+ raise_if_delete_unsupported
+
+ reload_cached_powershell_data unless node["powershell_features_cache"]
+
+ fail_if_unavailable # fail if the features don't exist
+
+ Chef::Log.debug("Windows features needing deletion: #{features_to_delete.empty? ? 'none' : features_to_delete.join(',')}")
+
+ unless features_to_delete.empty?
+ converge_by("delete Windows feature#{'s' if features_to_delete.count > 1} #{features_to_delete.join(',')} from the image") do
+ cmd = powershell_out!("Uninstall-WindowsFeature #{features_to_delete.join(',')} -Remove", timeout: new_resource.timeout)
+ Chef::Log.info(cmd.stdout)
+
+ reload_cached_powershell_data # Reload cached powershell feature state
+ end
+ end
+ end
+
+ action_class do
+ # shellout to determine the actively installed version of powershell
+ # we have this same data in ohai, but it doesn't get updated if powershell is installed mid run
+ # @return [Integer] the powershell version or 0 for nothing
+ def powershell_version
+ cmd = powershell_out("$PSVersionTable.psversion.major")
+ return 1 if cmd.stdout.empty? # PowerShell 1.0 doesn't have a $PSVersionTable
+ Regexp.last_match(1).to_i if cmd.stdout =~ /^(\d+)/
+ rescue Errno::ENOENT
+ 0 # zero as in nothing is installed
+ end
+
+ # raise if we're running powershell less than 3.0 since we need convertto-json
+ # check the powershell version via ohai data and if we're < 3.0 also shellout to make sure as
+ # a newer version could be installed post ohai run. Yes we're double checking. It's fine.
+ # @todo this can go away when we fully remove support for Windows 2008 R2
+ # @raise [RuntimeError] Raise if powershell is < 3.0
+ def raise_on_old_powershell
+ # be super defensive about the powershell lang plugin not being there
+ return if node["languages"] && node["languages"]["powershell"] && node["languages"]["powershell"]["version"].to_i > 3
+ raise "The windows_feature_powershell resource requires PowerShell 3.0 or later. Please install PowerShell 3.0+ before running this resource." if powershell_version < 3
+ end
+
+ def install_feature_cmdlet
+ node["platform_version"].to_f < 6.2 ? "Import-Module ServerManager; Add-WindowsFeature" : "Install-WindowsFeature"
+ end
+
+ def remove_feature_cmdlet
+ node["platform_version"].to_f < 6.2 ? "Import-Module ServerManager; Remove-WindowsFeature" : "Uninstall-WindowsFeature"
+ end
+
+ # @return [Array] features the user has requested to install which need installation
+ def features_to_install
+ # the intersection of the features to install & disabled features are what needs installing
+ @install ||= new_resource.feature_name & node["powershell_features_cache"]["disabled"]
+ end
+
+ # @return [Array] features the user has requested to remove which need removing
+ def features_to_remove
+ # the intersection of the features to remove & enabled features are what needs removing
+ @remove ||= new_resource.feature_name & node["powershell_features_cache"]["enabled"]
+ end
+
+ # @return [Array] features the user has requested to delete which need deleting
+ def features_to_delete
+ # the intersection of the features to remove & enabled/disabled features are what needs removing
+ @remove ||= begin
+ all_available = node["powershell_features_cache"]["enabled"] +
+ node["powershell_features_cache"]["disabled"]
+ new_resource.feature_name & all_available
+ end
+ end
+
+ # if any features are not supported on this release of Windows or
+ # have been deleted raise with a friendly message. At one point in time
+ # we just warned, but this goes against the behavior of ever other package
+ # provider in Chef and it isn't clear what you'd want if you passed an array
+ # and some features were available and others were not.
+ # @return [void]
+ def fail_if_unavailable
+ all_available = node["powershell_features_cache"]["enabled"] +
+ node["powershell_features_cache"]["disabled"] +
+ node["powershell_features_cache"]["removed"]
+
+ # the difference of desired features to install to all features is what's not available
+ unavailable = (new_resource.feature_name - all_available)
+ raise "The Windows feature#{'s' if unavailable.count > 1} #{unavailable.join(',')} #{unavailable.count > 1 ? 'are' : 'is'} not available on this version of Windows. Run 'Get-WindowsFeature' to see the list of available feature names." unless unavailable.empty?
+ end
+
+ # run Get-WindowsFeature to get a list of all available features and their state
+ # and save that to the node at node.override level.
+ # @return [void]
+ def reload_cached_powershell_data
+ Chef::Log.debug("Caching Windows features available via Get-WindowsFeature.")
+ node.override["powershell_features_cache"] = Mash.new
+ node.override["powershell_features_cache"]["enabled"] = []
+ node.override["powershell_features_cache"]["disabled"] = []
+ node.override["powershell_features_cache"]["removed"] = []
+
+ parsed_feature_list.each do |feature_details_raw|
+ case feature_details_raw["InstallState"]
+ when 5 # matches 'Removed' InstallState
+ add_to_feature_mash("removed", feature_details_raw["Name"])
+ when 1, 3 # matches 'Installed' or 'InstallPending' states
+ add_to_feature_mash("enabled", feature_details_raw["Name"])
+ when 0, 2 # matches 'Available' or 'UninstallPending' states
+ add_to_feature_mash("disabled", feature_details_raw["Name"])
+ end
+ end
+ Chef::Log.debug("The powershell cache contains\n#{node['powershell_features_cache']}")
+ end
+
+ # fetch the list of available feature names and state in JSON and parse the JSON
+ def parsed_feature_list
+ # Grab raw feature information from dism command line
+ raw_list_of_features = if node["platform_version"].to_f < 6.2
+ powershell_out!("Import-Module ServerManager; Get-WindowsFeature | Select-Object -Property Name,InstallState | ConvertTo-Json -Compress", timeout: new_resource.timeout).stdout
+ else
+ powershell_out!("Get-WindowsFeature | Select-Object -Property Name,InstallState | ConvertTo-Json -Compress", timeout: new_resource.timeout).stdout
+ end
+
+ Chef::JSONCompat.from_json(raw_list_of_features)
+ end
+
+ # add the features values to the appropriate array
+ # @return [void]
+ def add_to_feature_mash(feature_type, feature_details)
+ node.override["powershell_features_cache"][feature_type] << feature_details.downcase # lowercase so we can compare properly
+ end
+
+ # Fail if any of the packages are in a removed state
+ # @return [void]
+ def fail_if_removed
+ return if new_resource.source # if someone provides a source then all is well
+ if node["platform_version"].to_f > 6.2
+ return if registry_key_exists?('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing') && registry_value_exists?('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing', name: "LocalSourcePath") # if source is defined in the registry, still fine
+ end
+ removed = new_resource.feature_name & node["powershell_features_cache"]["removed"]
+ raise "The Windows feature#{'s' if removed.count > 1} #{removed.join(',')} #{removed.count > 1 ? 'are' : 'is'} have been removed from the host and cannot be installed." unless removed.empty?
+ end
+
+ # Fail unless we're on windows 8+ / 2012+ where deleting a feature is supported
+ def raise_if_delete_unsupported
+ raise Chef::Exceptions::UnsupportedAction, "#{self} :delete action not support on Windows releases before Windows 8/2012. Cannot continue!" unless node["platform_version"].to_f >= 6.2
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_font.rb b/lib/chef/resource/windows_font.rb
new file mode 100644
index 0000000000..9f4366338a
--- /dev/null
+++ b/lib/chef/resource/windows_font.rb
@@ -0,0 +1,127 @@
+#
+# Copyright:: 2014-2018, Schuberg Philis BV.
+# Copyright:: 2017-2018, Chef Software, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsFont < Chef::Resource
+ require "chef/util/path_helper"
+
+ resource_name :windows_font
+ provides(:windows_font) { true }
+
+ description "Use the windows_font resource to install font files on Windows."\
+ " By default, the font is sourced from the cookbook using the resource, but a URI"\
+ " source can be specified as well."
+ introduced "14.0"
+
+ property :font_name, String,
+ description: "The file name of the font file name to install if different than the resource name.",
+ name_property: true
+
+ property :source, String,
+ description: "A local filesystem path or URI to source the font file from.",
+ coerce: proc { |x| x =~ /^.:.*/ ? x.tr('\\', "/").gsub("//", "/") : x }
+
+ action :install do
+ description "Install a font to the system fonts directory."
+
+ if font_exists?
+ logger.trace("Not installing font: #{new_resource.font_name} as font already installed.")
+ else
+ retrieve_cookbook_font
+ install_font
+ del_cookbook_font
+ end
+ end
+
+ action_class do
+ # if a source is specified fetch using remote_file. If not use cookbook_file
+ def retrieve_cookbook_font
+ font_file = new_resource.font_name
+ if new_resource.source
+ declare_resource(:remote_file, font_file) do
+ action :nothing
+ source source_uri
+ path Chef::Util::PathHelper.join(ENV["TEMP"], font_file)
+ end.run_action(:create)
+ else
+ declare_resource(:cookbook_file, font_file) do
+ action :nothing
+ cookbook cookbook_name.to_s unless cookbook_name.nil?
+ path Chef::Util::PathHelper.join(ENV["TEMP"], font_file)
+ end.run_action(:create)
+ end
+ end
+
+ # delete the temp cookbook file
+ def del_cookbook_font
+ file Chef::Util::PathHelper.join(ENV["TEMP"], new_resource.font_name) do
+ action :delete
+ end
+ end
+
+ # install the font into the appropriate fonts directory
+ def install_font
+ require "win32ole" if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ fonts_dir = Chef::Util::PathHelper.join(ENV["windir"], "fonts")
+ folder = WIN32OLE.new("Shell.Application").Namespace(fonts_dir)
+ converge_by("install font #{new_resource.font_name} to #{fonts_dir}") do
+ folder.CopyHere(Chef::Util::PathHelper.join(ENV["TEMP"], new_resource.font_name))
+ end
+ end
+
+ # Check to see if the font is installed in the fonts dir
+ #
+ # @return [Boolean] Is the font is installed?
+ def font_exists?
+ require "win32ole" if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ fonts_dir = WIN32OLE.new("WScript.Shell").SpecialFolders("Fonts")
+ logger.trace("Seeing if the font at #{Chef::Util::PathHelper.join(fonts_dir, new_resource.font_name)} exists")
+ ::File.exist?(Chef::Util::PathHelper.join(fonts_dir, new_resource.font_name))
+ end
+
+ # Parse out the schema provided to us to see if it's one we support via remote_file.
+ # We do this because URI will parse C:/foo as schema 'c', which won't work with remote_file
+ #
+ # @return [Boolean]
+ def remote_file_schema?(schema)
+ return true if %w{http https ftp}.include?(schema)
+ end
+
+ # return new_resource.source if we have a proper URI specified
+ # if it's a local file listed as a source return it in file:// format
+ #
+ # @return [String] path to the font
+ def source_uri
+ begin
+ require "uri"
+ if remote_file_schema?(URI.parse(new_resource.source).scheme)
+ logger.trace("source property starts with ftp/http. Using source property unmodified")
+ return new_resource.source
+ end
+ rescue URI::InvalidURIError
+ Chef::Log.warn("source property of #{new_resource.source} could not be processed as a URI. Check the format you provided.")
+ end
+ logger.trace("source property does not start with ftp/http. Prepending with file:// as it appears to be a local file.")
+ "file://#{new_resource.source}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_package.rb b/lib/chef/resource/windows_package.rb
index 0e8dd39672..49496aed9a 100644
--- a/lib/chef/resource/windows_package.rb
+++ b/lib/chef/resource/windows_package.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,9 +27,13 @@ class Chef
include Chef::Mixin::Uris
resource_name :windows_package
- provides :windows_package, os: "windows"
+ provides(:windows_package) { true }
provides :package, os: "windows"
+ description "Use the windows_package resource to manage Microsoft Installer Package"\
+ " (MSI) packages for the Microsoft Windows platform."
+ introduced "11.12"
+
allowed_actions :install, :remove
def initialize(name, run_context = nil)
@@ -37,6 +41,9 @@ class Chef
@source ||= source(@package_name) if @package_name.downcase.end_with?(".msi")
end
+ # windows can't take array options yet
+ property :options, String
+
# Unique to this resource
property :installer_type, Symbol
property :timeout, [ String, Integer ], default: 600
diff --git a/lib/chef/resource/windows_pagefile.rb b/lib/chef/resource/windows_pagefile.rb
new file mode 100644
index 0000000000..9c77c1f560
--- /dev/null
+++ b/lib/chef/resource/windows_pagefile.rb
@@ -0,0 +1,206 @@
+#
+# Copyright:: 2012-2018, Nordstrom, Inc.
+# Copyright:: 2017-2018, Chef Software, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsPagefile < Chef::Resource
+ resource_name :windows_pagefile
+ provides(:windows_pagefile) { true }
+
+ description "Use the windows_pagefile resource to configure pagefile settings on Windows."
+ introduced "14.0"
+
+ property :path, String,
+ coerce: proc { |x| x.tr("/", '\\') },
+ description: "The path to the pagefile if different from the resource name.",
+ name_property: true
+
+ property :system_managed, [TrueClass, FalseClass],
+ description: "Configures whether the system manages the pagefile size."
+
+ property :automatic_managed, [TrueClass, FalseClass],
+ description: "Enable automatic management of pagefile initial and maximum size. Setting this to true ignores 'initial_size' and 'maximum_size' properties.",
+ default: false
+
+ property :initial_size, Integer,
+ description: "Initial size of the pagefile in megabytes."
+
+ property :maximum_size, Integer,
+ description: "Maximum size of the pagefile in megabytes."
+
+ action :set do
+ description "Configures the default pagefile, creating if it doesn't exist."
+
+ pagefile = new_resource.path
+ initial_size = new_resource.initial_size
+ maximum_size = new_resource.maximum_size
+ system_managed = new_resource.system_managed
+ automatic_managed = new_resource.automatic_managed
+
+ if automatic_managed
+ set_automatic_managed unless automatic_managed?
+ else
+ unset_automatic_managed if automatic_managed?
+
+ # Check that the resource is not just trying to unset automatic managed, if it is do nothing more
+ if (initial_size && maximum_size) || system_managed
+ validate_name
+ create(pagefile) unless exists?(pagefile)
+
+ if system_managed
+ set_system_managed(pagefile) unless max_and_min_set?(pagefile, 0, 0)
+ else
+ unless max_and_min_set?(pagefile, initial_size, maximum_size)
+ set_custom_size(pagefile, initial_size, maximum_size)
+ end
+ end
+ end
+ end
+ end
+
+ action :delete do
+ description "Deletes the specified pagefile."
+
+ validate_name
+ delete(new_resource.path) if exists?(new_resource.path)
+ end
+
+ action_class do
+ # make sure the provided name property matches the appropriate format
+ # we do this here and not in the property itself because if automatic_managed
+ # is set then this validation is not necessary / doesn't make sense at all
+ def validate_name
+ return if /^.:.*.sys/ =~ new_resource.path
+ raise "#{new_resource.path} does not match the format DRIVE:\\path\\file.sys for pagefiles. Example: C:\\pagefile.sys"
+ end
+
+ # See if the pagefile exists
+ #
+ # @param [String] pagefile path to the pagefile
+ # @return [Boolean]
+ def exists?(pagefile)
+ @exists ||= begin
+ logger.trace("Checking if #{pagefile} exists by runing: wmic.exe pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" list /format:list")
+ cmd = shell_out("wmic.exe pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" list /format:list", returns: [0])
+ cmd.stderr.empty? && (cmd.stdout =~ /SettingID=#{get_setting_id(pagefile)}/i)
+ end
+ end
+
+ # is the max/min pagefile size set?
+ #
+ # @param [String] pagefile path to the pagefile
+ # @param [String] min the minimum size of the pagefile
+ # @param [String] max the minimum size of the pagefile
+ # @return [Boolean]
+ def max_and_min_set?(pagefile, min, max)
+ @max_and_min_set ||= begin
+ logger.trace("Checking if #{pagefile} min: #{min} and max #{max} are set")
+ cmd = shell_out("wmic.exe pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" list /format:list", returns: [0])
+ cmd.stderr.empty? && (cmd.stdout =~ /InitialSize=#{min}/i) && (cmd.stdout =~ /MaximumSize=#{max}/i)
+ end
+ end
+
+ # create a pagefile
+ #
+ # @param [String] pagefile path to the pagefile
+ def create(pagefile)
+ converge_by("create pagefile #{pagefile}") do
+ logger.trace("Running wmic.exe pagefileset create name=\"#{pagefile}\"")
+ cmd = shell_out("wmic.exe pagefileset create name=\"#{pagefile}\"")
+ check_for_errors(cmd.stderr)
+ end
+ end
+
+ # delete a pagefile
+ #
+ # @param [String] pagefile path to the pagefile
+ def delete(pagefile)
+ converge_by("remove pagefile #{pagefile}") do
+ logger.trace("Running wmic.exe pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" delete")
+ cmd = shell_out("wmic.exe pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" delete")
+ check_for_errors(cmd.stderr)
+ end
+ end
+
+ # see if the pagefile is automatically managed by Windows
+ #
+ # @return [Boolean]
+ def automatic_managed?
+ @automatic_managed ||= begin
+ logger.trace("Checking if pagefiles are automatically managed")
+ cmd = shell_out("wmic.exe computersystem where name=\"%computername%\" get AutomaticManagedPagefile /format:list")
+ cmd.stderr.empty? && (cmd.stdout =~ /AutomaticManagedPagefile=TRUE/i)
+ end
+ end
+
+ # turn on automatic management of all pagefiles by Windows
+ def set_automatic_managed
+ converge_by("set pagefile to Automatic Managed") do
+ logger.trace("Running wmic.exe computersystem where name=\"%computername%\" set AutomaticManagedPagefile=True")
+ cmd = shell_out("wmic.exe computersystem where name=\"%computername%\" set AutomaticManagedPagefile=True")
+ check_for_errors(cmd.stderr)
+ end
+ end
+
+ # turn off automatic management of all pagefiles by Windows
+ def unset_automatic_managed
+ converge_by("set pagefile to User Managed") do
+ logger.trace("Running wmic.exe computersystem where name=\"%computername%\" set AutomaticManagedPagefile=False")
+ cmd = shell_out("wmic.exe computersystem where name=\"%computername%\" set AutomaticManagedPagefile=False")
+ check_for_errors(cmd.stderr)
+ end
+ end
+
+ # set a custom size for the pagefile (vs the defaults)
+ #
+ # @param [String] pagefile path to the pagefile
+ # @param [String] min the minimum size of the pagefile
+ # @param [String] max the minimum size of the pagefile
+ def set_custom_size(pagefile, min, max)
+ converge_by("set #{pagefile} to InitialSize=#{min} & MaximumSize=#{max}") do
+ logger.trace("Running wmic.exe pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" set InitialSize=#{min},MaximumSize=#{max}")
+ cmd = shell_out("wmic.exe pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" set InitialSize=#{min},MaximumSize=#{max}", returns: [0])
+ check_for_errors(cmd.stderr)
+ end
+ end
+
+ # set a pagefile size to be system managed
+ #
+ # @param [String] pagefile path to the pagefile
+ def set_system_managed(pagefile)
+ converge_by("set #{pagefile} to System Managed") do
+ logger.trace("Running wmic.exe pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" set InitialSize=0,MaximumSize=0")
+ cmd = shell_out("wmic.exe pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" set InitialSize=0,MaximumSize=0", returns: [0])
+ check_for_errors(cmd.stderr)
+ end
+ end
+
+ def get_setting_id(pagefile)
+ split_path = pagefile.split('\\')
+ "#{split_path[1]} @ #{split_path[0]}"
+ end
+
+ # raise if there's an error on stderr on a shellout
+ def check_for_errors(stderr)
+ raise stderr.chomp unless stderr.empty?
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_path.rb b/lib/chef/resource/windows_path.rb
new file mode 100644
index 0000000000..0c5aaa9ce1
--- /dev/null
+++ b/lib/chef/resource/windows_path.rb
@@ -0,0 +1,36 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsPath < Chef::Resource
+ resource_name :windows_path
+ provides(:windows_path) { true }
+
+ description "Use the windows_path resource to manage the path environment variable on Microsoft Windows."
+ introduced "13.4"
+
+ allowed_actions :add, :remove
+ default_action :add
+
+ property :path, String, name_property: true
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_printer.rb b/lib/chef/resource/windows_printer.rb
new file mode 100644
index 0000000000..c58e02b994
--- /dev/null
+++ b/lib/chef/resource/windows_printer.rb
@@ -0,0 +1,148 @@
+#
+# Author:: Doug Ireton (<doug@1strategy.com>)
+# Copyright:: 2012-2018, Nordstrom, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# See here for more info:
+# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394492(v=vs.85).aspx
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsPrinter < Chef::Resource
+ require "resolv"
+
+ resource_name :windows_printer
+ provides(:windows_printer) { true }
+
+ description "Use the windows_printer resource to setup Windows printers. Note"\
+ " that this doesn't currently install a printer driver. You must"\
+ " already have the driver installed on the system."
+ introduced "14.0"
+
+ property :device_id, String,
+ description: "Printer queue name, e.g. 'HP LJ 5200 in fifth floor copy room'.",
+ name_property: true
+
+ property :comment, String,
+ description: "Optional descriptor for the printer queue."
+
+ property :default, [TrueClass, FalseClass],
+ description: "Should this be the system's default printer.",
+ default: false
+
+ property :driver_name, String,
+ description: "Exact name of printer driver. Note that the printer driver must already be installed on the node.",
+ required: true
+
+ property :location, String,
+ description: "Printer location, e.g. 'Fifth floor copy room'."
+
+ property :shared, [TrueClass, FalseClass],
+ description: "Should the printer be shared.",
+ default: false
+
+ property :share_name, String,
+ description: "The name to share the printer as."
+
+ property :ipv4_address, String,
+ description: "Printer IPv4 address, e.g. '10.4.64.23'.",
+ validation_message: "The ipv4_address property must be in the IPv4 format of WWW.XXX.YYY.ZZZ",
+ regex: Resolv::IPv4::Regex
+
+ property :exists, [TrueClass, FalseClass],
+ desired_state: true
+
+ PRINTERS_REG_KEY = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\\'.freeze unless defined?(PRINTERS_REG_KEY)
+
+ # does the printer exist
+ #
+ # @param [String] name the name of the printer
+ # @return [Boolean]
+ def printer_exists?(name)
+ printer_reg_key = PRINTERS_REG_KEY + name
+ logger.trace "Checking to see if this reg key exists: '#{printer_reg_key}'"
+ registry_key_exists?(printer_reg_key)
+ end
+
+ # @todo Set @current_resource printer properties from registry
+ load_current_value do |desired|
+ name desired.name
+ exists printer_exists?(desired.name)
+ end
+
+ action :create do
+ description "Create a new printer and a printer port if one doesn't already exist."
+
+ if @current_resource.exists
+ Chef::Log.info "#{@new_resource} already exists - nothing to do."
+ else
+ converge_by("Create #{@new_resource}") do
+ create_printer
+ end
+ end
+ end
+
+ action :delete do
+ description "Delete an existing printer. Note this does not delete the associated printer port."
+
+ if @current_resource.exists
+ converge_by("Delete #{@new_resource}") do
+ delete_printer
+ end
+ else
+ Chef::Log.info "#{@current_resource} doesn't exist - can't delete."
+ end
+ end
+
+ action_class do
+ # creates the printer port and then the printer
+ def create_printer
+ # Create the printer port first
+ windows_printer_port new_resource.ipv4_address do
+ end
+
+ port_name = "IP_#{new_resource.ipv4_address}"
+
+ declare_resource(:powershell_script, "Creating printer: #{new_resource.name}") do
+ code <<-EOH
+
+ Set-WmiInstance -class Win32_Printer `
+ -EnableAllPrivileges `
+ -Argument @{ DeviceID = "#{new_resource.device_id}";
+ Comment = "#{new_resource.comment}";
+ Default = "$#{new_resource.default}";
+ DriverName = "#{new_resource.driver_name}";
+ Location = "#{new_resource.location}";
+ PortName = "#{port_name}";
+ Shared = "$#{new_resource.shared}";
+ ShareName = "#{new_resource.share_name}";
+ }
+ EOH
+ end
+ end
+
+ def delete_printer
+ declare_resource(:powershell_script, "Deleting printer: #{new_resource.name}") do
+ code <<-EOH
+ $printer = Get-WMIObject -class Win32_Printer -EnableAllPrivileges -Filter "name = '#{new_resource.name}'"
+ $printer.Delete()
+ EOH
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_printer_port.rb b/lib/chef/resource/windows_printer_port.rb
new file mode 100644
index 0000000000..75b18a41dd
--- /dev/null
+++ b/lib/chef/resource/windows_printer_port.rb
@@ -0,0 +1,136 @@
+#
+# Author:: Doug Ireton <doug@1strategy.com>
+# Copyright:: 2012-2018, Nordstrom, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# See here for more info:
+# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394492(v=vs.85).aspx
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsPrinterPort < Chef::Resource
+ require "resolv"
+
+ resource_name :windows_printer_port
+ provides(:windows_printer_port) { true }
+
+ description "Use the windows_printer_port resource to create and delete TCP/IPv4 printer ports on Windows."
+ introduced "14.0"
+
+ property :ipv4_address, String,
+ name_property: true,
+ regex: Resolv::IPv4::Regex,
+ validation_message: "The ipv4_address property must be in the format of WWW.XXX.YYY.ZZZ!",
+ description: "The IPv4 address of the printer port."
+
+ property :port_name, String,
+ description: "The port name."
+
+ property :port_number, Integer,
+ description: "The port number.",
+ default: 9100
+
+ property :port_description, String,
+ description: "The description of the port."
+
+ property :snmp_enabled, [TrueClass, FalseClass],
+ description: "Should SNMP be enabled on the port.",
+ default: false
+
+ property :port_protocol, Integer,
+ description: "The printer port protocol, 1 (RAW), or 2 (LPR).",
+ validation_message: "port_protocol must be either 1 for RAW or 2 for LPR!",
+ default: 1, equal_to: [1, 2]
+
+ property :exists, [TrueClass, FalseClass],
+ desired_state: true
+
+ PORTS_REG_KEY = 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Monitors\Standard TCP/IP Port\Ports\\'.freeze unless defined?(PORTS_REG_KEY)
+
+ def port_exists?(name)
+ port_reg_key = PORTS_REG_KEY + name
+
+ logger.trace "Checking to see if this reg key exists: '#{port_reg_key}'"
+ registry_key_exists?(port_reg_key)
+ end
+
+ # @todo Set @current_resource port properties from registry
+ load_current_value do |desired|
+ name desired.name
+ ipv4_address desired.ipv4_address
+ port_name desired.port_name || "IP_#{desired.ipv4_address}"
+ exists port_exists?(desired.port_name || "IP_#{desired.ipv4_address}")
+ end
+
+ action :create do
+ description "Create the new printer port if it does not already exist."
+
+ if current_resource.exists
+ Chef::Log.info "#{@new_resource} already exists - nothing to do."
+ else
+ converge_by("Create #{@new_resource}") do
+ create_printer_port
+ end
+ end
+ end
+
+ action :delete do
+ description "Delete an existing printer port."
+
+ if current_resource.exists
+ converge_by("Delete #{@new_resource}") do
+ delete_printer_port
+ end
+ else
+ Chef::Log.info "#{@current_resource} doesn't exist - can't delete."
+ end
+ end
+
+ action_class do
+ def create_printer_port
+ port_name = new_resource.port_name || "IP_#{new_resource.ipv4_address}"
+
+ # create the printer port using PowerShell
+ declare_resource(:powershell_script, "Creating printer port #{new_resource.port_name}") do
+ code <<-EOH
+
+ Set-WmiInstance -class Win32_TCPIPPrinterPort `
+ -EnableAllPrivileges `
+ -Argument @{ HostAddress = "#{new_resource.ipv4_address}";
+ Name = "#{port_name}";
+ Description = "#{new_resource.port_description}";
+ PortNumber = "#{new_resource.port_number}";
+ Protocol = "#{new_resource.port_protocol}";
+ SNMPEnabled = "$#{new_resource.snmp_enabled}";
+ }
+ EOH
+ end
+ end
+
+ def delete_printer_port
+ port_name = new_resource.port_name || "IP_#{new_resource.ipv4_address}"
+
+ declare_resource(:powershell_script, "Deleting printer port: #{new_resource.port_name}") do
+ code <<-EOH
+ $port = Get-WMIObject -class Win32_TCPIPPrinterPort -EnableAllPrivileges -Filter "name = '#{port_name}'"
+ $port.Delete()
+ EOH
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_service.rb b/lib/chef/resource/windows_service.rb
index 405f7f6dbe..7b550ea11a 100644
--- a/lib/chef/resource/windows_service.rb
+++ b/lib/chef/resource/windows_service.rb
@@ -17,55 +17,99 @@
#
require "chef/resource/service"
+require "chef/win32_service_constants"
class Chef
class Resource
class WindowsService < Chef::Resource::Service
+ include Chef::Win32ServiceConstants
+
+ ALLOWED_START_TYPES = {
+ automatic: SERVICE_AUTO_START,
+ manual: SERVICE_DEMAND_START,
+ disabled: SERVICE_DISABLED,
+ }
# Until #1773 is resolved, you need to manually specify the windows_service resource
# to use action :configure_startup and attribute startup_type
- provides :windows_service, os: "windows"
+ provides(:windows_service) { true }
provides :service, os: "windows"
- allowed_actions :configure_startup
+ description "Use the windows_service resource to manage a service on the Microsoft Windows platform."
+ introduced "12.0"
- identity_attr :service_name
+ allowed_actions :configure_startup, :create, :delete, :configure
state_attrs :enabled, :running
- def initialize(name, run_context = nil)
- super
- @startup_type = :automatic
- @run_as_user = ""
- @run_as_password = ""
- end
-
- def startup_type(arg = nil)
- # Set-Service arguments are automatic and manual
- # Win32::Service returns 'auto start' or 'demand start' respectively, which the provider currently uses
- set_or_return(
- :startup_type,
- arg,
- :equal_to => [ :automatic, :manual, :disabled ]
- )
- end
-
- def run_as_user(arg = nil)
- set_or_return(
- :run_as_user,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def run_as_password(arg = nil)
- set_or_return(
- :run_as_password,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :service_name, name_property: true, identity: true
+
+ # The display name to be used by user interface programs to identify the
+ # service. This string has a maximum length of 256 characters.
+ property :display_name, String, regex: /^.{1,256}$/
+
+ # https://github.com/djberg96/win32-service/blob/ffi/lib/win32/windows/constants.rb#L19-L29
+ property :desired_access, Integer, default: SERVICE_ALL_ACCESS
+
+ # https://github.com/djberg96/win32-service/blob/ffi/lib/win32/windows/constants.rb#L31-L41
+ property :service_type, Integer, default: SERVICE_WIN32_OWN_PROCESS
+
+ # Valid options:
+ # - :automatic
+ # - :manual
+ # - :disabled
+ # Reference: https://github.com/djberg96/win32-service/blob/ffi/lib/win32/windows/constants.rb#L49-L54
+ property :startup_type, [Symbol], equal_to: [:automatic, :manual, :disabled], default: :automatic, coerce: proc { |x|
+ if x.is_a?(Integer)
+ ALLOWED_START_TYPES.invert.fetch(x) do
+ Chef::Log.warn("Unsupported startup_type #{x}, falling back to :automatic")
+ :automatic
+ end
+ elsif x.is_a?(String)
+ x.to_sym
+ else
+ x
+ end
+ }
+
+ # This only applies if startup_type is :automatic
+ # 1 == delayed start is enabled
+ # 0 == NO delayed start
+ property :delayed_start, [TrueClass, FalseClass], default: false, coerce: proc { |x|
+ if x.is_a?(Integer)
+ x == 0 ? false : true
+ else
+ x
+ end
+ }
+
+ # https://github.com/djberg96/win32-service/blob/ffi/lib/win32/windows/constants.rb#L43-L47
+ property :error_control, Integer, default: SERVICE_ERROR_NORMAL
+
+ # The fully qualified path to the service binary file. The path can also
+ # include arguments for an auto-start service.
+ #
+ # This is required for :create and :configure actions -- intentionally
+ # not setting required: true here to support other actions
+ property :binary_path_name, String
+
+ # The names of the load ordering group of which this service is a member.
+ # Specify nil or an empty string if the service does not belong to a group.
+ property :load_order_group, String
+
+ # A pointer to a double null-terminated array of null-separated names of
+ # services or load ordering groups that the system must start before this
+ # service. Specify nil or an empty string if the service has no
+ # dependencies. Dependency on a group means that this service can run if
+ # at least one member of the group is running after an attempt to start
+ # all members of the group.
+ property :dependencies, [String, Array]
+
+ property :description, String
+
+ property :run_as_user, String, default: "LocalSystem"
+ property :run_as_password, String, default: ""
end
end
end
diff --git a/lib/chef/resource/windows_shortcut.rb b/lib/chef/resource/windows_shortcut.rb
new file mode 100644
index 0000000000..60eb9f9b24
--- /dev/null
+++ b/lib/chef/resource/windows_shortcut.rb
@@ -0,0 +1,79 @@
+#
+# Author:: Doug MacEachern <dougm@vmware.com>
+# Copyright:: 2010-2018, VMware, Inc.
+# Copyright:: 2017-2018, Chef Software, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsShortcut < Chef::Resource
+ resource_name :windows_shortcut
+ provides(:windows_shortcut) { true }
+
+ description "Use the windows_shortcut resource to create shortcut files on Windows"
+ introduced "14.0"
+
+ property :shortcut_name, String,
+ description: "The name for the shortcut if it differs from the resource name.",
+ name_property: true
+
+ property :target, String,
+ description: "Where the shortcut links to."
+
+ property :arguments, String,
+ description: "Arguments to pass to the target when the shortcut is executed."
+
+ property :description, String,
+ description: "The description of the shortcut"
+
+ property :cwd, String,
+ description: "Working directory to use when the target is executed."
+
+ property :iconlocation, String,
+ description: "Icon to use for the shortcut, in the format of 'path, index'. Index is the icon file to use. See https://msdn.microsoft.com/en-us/library/3s9bx7at.aspx for details"
+
+ load_current_value do |desired|
+ require "win32ole" if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+
+ link = WIN32OLE.new("WScript.Shell").CreateShortcut(desired.shortcut_name)
+ name desired.shortcut_name
+ target(link.TargetPath)
+ arguments(link.Arguments)
+ description(link.Description)
+ cwd(link.WorkingDirectory)
+ iconlocation(link.IconLocation)
+ end
+
+ action :create do
+ description "Create or modify a Windows shortcut."
+
+ converge_if_changed do
+ converge_by "creating shortcut #{new_resource.shortcut_name}" do
+ link = WIN32OLE.new("WScript.Shell").CreateShortcut(new_resource.shortcut_name)
+ link.TargetPath = new_resource.target unless new_resource.target.nil?
+ link.Arguments = new_resource.arguments unless new_resource.arguments.nil?
+ link.Description = new_resource.description unless new_resource.description.nil?
+ link.WorkingDirectory = new_resource.cwd unless new_resource.cwd.nil?
+ link.IconLocation = new_resource.iconlocation unless new_resource.iconlocation.nil?
+ # ignoring: WindowStyle, Hotkey
+ link.Save
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_task.rb b/lib/chef/resource/windows_task.rb
new file mode 100644
index 0000000000..ec376c9578
--- /dev/null
+++ b/lib/chef/resource/windows_task.rb
@@ -0,0 +1,281 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsTask < Chef::Resource
+ resource_name :windows_task
+ provides(:windows_task) { true }
+
+ description "Use the windows_task resource to create, delete or run a Windows"\
+ " scheduled task. Requires Windows Server 2008 or later due to API usage."
+ introduced "13.0"
+
+ allowed_actions :create, :delete, :run, :end, :enable, :disable, :change
+ default_action :create
+
+ property :task_name, String, regex: [/\A[^\/\:\*\?\<\>\|]+\z/], name_property: true
+ property :command, String
+ property :cwd, String
+ property :user, String, default: "SYSTEM"
+ property :password, String
+ property :run_level, equal_to: [:highest, :limited], default: :limited
+ property :force, [TrueClass, FalseClass], default: false
+ property :interactive_enabled, [TrueClass, FalseClass], default: false
+ property :frequency_modifier, [Integer, String], default: 1
+ property :frequency, equal_to: [:minute,
+ :hourly,
+ :daily,
+ :weekly,
+ :monthly,
+ :once,
+ :on_logon,
+ :onstart,
+ :on_idle,
+ :none]
+ property :start_day, String
+ property :start_time, String
+ property :day, [String, Integer]
+ property :months, String
+ property :idle_time, Integer
+ property :random_delay, [String, Integer]
+ property :execution_time_limit, [String, Integer], default: "PT72H" # 72 hours in ISO8601 duration format
+ property :minutes_duration, [String, Integer]
+ property :minutes_interval, [String, Integer]
+
+ attr_accessor :exists, :task
+
+ SYSTEM_USERS = ['NT AUTHORITY\SYSTEM', "SYSTEM", 'NT AUTHORITY\LOCALSERVICE', 'NT AUTHORITY\NETWORKSERVICE', 'BUILTIN\USERS', "USERS"].freeze
+ VALID_WEEK_DAYS = %w{ mon tue wed thu fri sat sun * }
+ VALID_DAYS_OF_MONTH = ("1".."31").to_a << "last" << "lastday"
+ VALID_MONTHS = %w{JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC *}
+ VALID_WEEKS = %w{FIRST SECOND THIRD FOURTH LAST LASTDAY}
+
+ def after_created
+ if random_delay
+ validate_random_delay(random_delay, frequency)
+ random_delay(sec_to_min(random_delay))
+ end
+
+ if execution_time_limit
+ execution_time_limit(259200) if execution_time_limit == "PT72H"
+ raise ArgumentError, "Invalid value passed for `execution_time_limit`. Please pass seconds as an Integer (e.g. 60) or a String with numeric values only (e.g. '60')." unless numeric_value_in_string?(execution_time_limit)
+ execution_time_limit(sec_to_min(execution_time_limit))
+ end
+
+ validate_frequency(frequency) if action.include?(:create) || action.include?(:change)
+ validate_start_time(start_time, frequency)
+ validate_start_day(start_day, frequency) if start_day
+ validate_user_and_password(user, password)
+ validate_interactive_setting(interactive_enabled, password)
+ validate_create_frequency_modifier(frequency, frequency_modifier) if frequency_modifier
+ validate_create_day(day, frequency, frequency_modifier) if day
+ validate_create_months(months, frequency) if months
+ validate_frequency_monthly(frequency_modifier, months, day) if frequency == :monthly
+ validate_idle_time(idle_time, frequency)
+ idempotency_warning_for_frequency_weekly(day, start_day) if frequency == :weekly
+ end
+
+ private
+
+ ## Resource is not idempotent when day, start_day is not provided with frequency :weekly
+ ## we set start_day when not given by user as current date based on which we set the day property for current current date day is monday ..
+ ## we set the monday as the day so at next run when new_resource.day is nil and current_resource day is monday due to which udpate gets called
+ def idempotency_warning_for_frequency_weekly(day, start_day)
+ if start_day.nil? && day.nil?
+ logger.warn "To maintain idempotency for frequency :weekly provide start_day, start_time and day."
+ end
+ end
+
+ # Validate the passed value is numeric values only if it is a string
+ def numeric_value_in_string?(val)
+ return true if Integer(val)
+ rescue ArgumentError
+ false
+ end
+
+ def validate_frequency(frequency)
+ if frequency.nil? || !([:minute, :hourly, :daily, :weekly, :monthly, :once, :on_logon, :onstart, :on_idle, :none].include?(frequency))
+ raise ArgumentError, "Frequency needs to be provided. Valid frequencies are :minute, :hourly, :daily, :weekly, :monthly, :once, :on_logon, :onstart, :on_idle, :none."
+ end
+ end
+
+ def validate_frequency_monthly(frequency_modifier, months, day)
+ # validates the frequency :monthly and raises error if frequency_modifier is first, second, thrid etc and day is not provided
+ if (frequency_modifier != 1) && (frequency_modifier_includes_days_of_weeks?(frequency_modifier)) && !(day)
+ raise ArgumentError, "Please select day on which you want to run the task e.g. 'Mon, Tue'. Multiple values must be seprated by comma."
+ end
+
+ # frequency_modifer 2-12 is used to set every (n) months, so using :months propety with frequency_modifer is not valid since they both used to set months.
+ # Not checking value 1 here for frequecy_modifier since we are setting that as default value it won't break anything since preference is given to months property
+ if (frequency_modifier.to_i.between?(2, 12)) && !(months.nil?)
+ raise ArgumentError, "For frequency :monthly either use property months or frequency_modifier to set months."
+ end
+ end
+
+ # returns true if frequency_modifer has values First, second, third, fourth, last, lastday
+ def frequency_modifier_includes_days_of_weeks?(frequency_modifier)
+ frequency_modifier = frequency_modifier.to_s.split(",")
+ frequency_modifier.map! { |value| value.strip.upcase }
+ (frequency_modifier - VALID_WEEKS).empty?
+ end
+
+ def validate_random_delay(random_delay, frequency)
+ if [:on_logon, :onstart, :on_idle, :none].include? frequency
+ raise ArgumentError, "`random_delay` property is supported only for frequency :once, :minute, :hourly, :daily, :weekly and :monthly"
+ end
+
+ raise ArgumentError, "Invalid value passed for `random_delay`. Please pass seconds as an Integer (e.g. 60) or a String with numeric values only (e.g. '60')." unless numeric_value_in_string?(random_delay)
+ end
+
+ # @todo when we drop ruby 2.3 support this should be converted to .match?() instead of =~f
+ def validate_start_day(start_day, frequency)
+ if start_day && frequency == :none
+ raise ArgumentError, "`start_day` property is not supported with frequency: #{frequency}"
+ end
+
+ # make sure the start_day is in MM/DD/YYYY format: http://rubular.com/r/cgjHemtWl5
+ if start_day
+ raise ArgumentError, "`start_day` property must be in the MM/DD/YYYY format." unless /^(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.](19|20)\d\d$/ =~ start_day
+ end
+ end
+
+ # @todo when we drop ruby 2.3 support this should be converted to .match?() instead of =~
+ def validate_start_time(start_time, frequency)
+ if start_time
+ raise ArgumentError, "`start_time` property is not supported with `frequency :none`" if frequency == :none
+ raise ArgumentError, "`start_time` property must be in the HH:mm format (e.g. 6:20pm -> 18:20)." unless /^[0-2][0-9]:[0-5][0-9]$/ =~ start_time
+ else
+ raise ArgumentError, "`start_time` needs to be provided with `frequency :once`" if frequency == :once
+ end
+ end
+
+ def validate_user_and_password(user, password)
+ if password_required?(user) && password.nil?
+ raise ArgumentError, %q{Cannot specify a user other than the system users without specifying a password!. Valid passwordless users: 'NT AUTHORITY\SYSTEM', 'SYSTEM', 'NT AUTHORITY\LOCALSERVICE', 'NT AUTHORITY\NETWORKSERVICE', 'BUILTIN\USERS', 'USERS'}
+ end
+ end
+
+ def password_required?(user)
+ return false if user.nil?
+ @password_required ||= !SYSTEM_USERS.include?(user.upcase)
+ end
+
+ def validate_interactive_setting(interactive_enabled, password)
+ raise ArgumentError, "Please provide the password when attempting to set interactive/non-interactive." if interactive_enabled && password.nil?
+ end
+
+ def validate_create_frequency_modifier(frequency, frequency_modifier)
+ if ([:on_logon, :onstart, :on_idle, :none].include?(frequency)) && ( frequency_modifier != 1)
+ raise ArgumentError, "frequency_modifier property not supported with frequency :#{frequency}"
+ end
+
+ if frequency == :monthly
+ unless (1..12).cover?(frequency_modifier.to_i) || frequency_modifier_includes_days_of_weeks?(frequency_modifier)
+ raise ArgumentError, "frequency_modifier value #{frequency_modifier} is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST'."
+ end
+ else
+ unless frequency.nil? || frequency_modifier.nil?
+ frequency_modifier = frequency_modifier.to_i
+ min = 1
+ max = case frequency
+ when :minute
+ 1439
+ when :hourly
+ 23
+ when :daily
+ 365
+ when :weekly
+ 52
+ else
+ min
+ end
+ unless frequency_modifier.between?(min, max)
+ raise ArgumentError, "frequency_modifier value #{frequency_modifier} is invalid. Valid values for :#{frequency} frequency are #{min} - #{max}."
+ end
+ end
+ end
+ end
+
+ def validate_create_day(day, frequency, frequency_modifier)
+ raise ArgumentError, "day property is only valid for tasks that run monthly or weekly" unless [:weekly, :monthly].include?(frequency)
+
+ # This has been verified with schtask.exe https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/schtasks#d-dayday--
+ # verified with earlier code if day "*" is given with frequency it raised exception Invalid value for /D option
+ raise ArgumentError, "day wild card (*) is only valid with frequency :weekly" if frequency == :monthly && day == "*"
+
+ if day.is_a?(String) && day.to_i.to_s != day
+ days = day.split(",")
+ if days_includes_days_of_months?(days)
+ # Following error will be raise if day is set as 1-31 and frequency is selected as :weekly since those values are valid with only frequency :monthly
+ raise ArgumentError, "day values 1-31 or last is only valid with frequency :monthly" if frequency == :weekly
+ else
+ days.map! { |day| day.to_s.strip.downcase }
+ unless (days - VALID_WEEK_DAYS).empty?
+ raise ArgumentError, "day property invalid. Only valid values are: #{VALID_WEEK_DAYS.map(&:upcase).join(', ')}. Multiple values must be separated by a comma."
+ end
+ end
+ end
+ end
+
+ def validate_create_months(months, frequency)
+ raise ArgumentError, "months property is only valid for tasks that run monthly" if frequency != :monthly
+ if months.is_a?(String)
+ months = months.split(",")
+ months.map! { |month| month.strip.upcase }
+ unless (months - VALID_MONTHS).empty?
+ raise ArgumentError, "months property invalid. Only valid values are: #{VALID_MONTHS.join(', ')}. Multiple values must be separated by a comma."
+ end
+ end
+ end
+
+ # This method returns true if day has values from 1-31 which is a days of moths and used with frequency :monthly
+ def days_includes_days_of_months?(days)
+ days.map! { |day| day.to_s.strip.downcase }
+ (days - VALID_DAYS_OF_MONTH).empty?
+ end
+
+ def validate_idle_time(idle_time, frequency)
+ if !idle_time.nil? && frequency != :on_idle
+ raise ArgumentError, "idle_time property is only valid for tasks that run on_idle"
+ end
+ if idle_time.nil? && frequency == :on_idle
+ raise ArgumentError, "idle_time value should be set for :on_idle frequency."
+ end
+ unless idle_time.nil? || idle_time > 0 && idle_time <= 999
+ raise ArgumentError, "idle_time value #{idle_time} is invalid. Valid values for :on_idle frequency are 1 - 999."
+ end
+ end
+
+ # Converts the number of seconds to an ISO8601 duration format and returns it.
+ # Ref : https://github.com/arnau/ISO8601/blob/master/lib/iso8601/duration.rb#L18-L23
+ # e.g.
+ # ISO8601::Duration.new(65707200)
+ # returns 'P65707200S'
+ def sec_to_dur(seconds)
+ ISO8601::Duration.new(seconds.to_i).to_s
+ end
+
+ def sec_to_min(seconds)
+ seconds.to_i / 60
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/yum_package.rb b/lib/chef/resource/yum_package.rb
index 1f749b93c0..905cacc477 100644
--- a/lib/chef/resource/yum_package.rb
+++ b/lib/chef/resource/yum_package.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,31 +17,51 @@
#
require "chef/resource/package"
-require "chef/provider/package/yum"
class Chef
class Resource
class YumPackage < Chef::Resource::Package
resource_name :yum_package
- provides :package, os: "linux", platform_family: %w{rhel fedora}
+ provides :package, platform_family: %w{rhel fedora amazon}
+
+ description "Use the yum_package resource to install, upgrade, and remove packages with Yum"\
+ " for the Red Hat and CentOS platforms. The yum_package resource is able to resolve"\
+ " provides data for packages much like Yum can do when it is run from the command line."\
+ " This allows a variety of options for installing packages, like minimum versions,"\
+ " virtual provides, and library names."
+
+ # XXX: the coercions here are due to the provider promiscuously updating the properties on the
+ # new_resource which causes immutable modification exceptions when passed an immutable node array.
+ #
+ # <lecture>
+ # THIS is why updating the new_resource in a provider is so terrible, and is equivalent to methods scribbling over
+ # its own arguments as unintended side-effects (and why functional languages that don't allow modifcations
+ # of variables eliminate entire classes of bugs).
+ # </lecture>
+ property :package_name, [ String, Array ], identity: true, coerce: proc { |x| x.is_a?(Array) ? x.to_a : x }
+ property :version, [ String, Array ], coerce: proc { |x| x.is_a?(Array) ? x.to_a : x }
+ property :arch, [ String, Array ], coerce: proc { |x| x.is_a?(Array) ? x.to_a : x }
+
+ property :flush_cache,
+ Hash,
+ default: { before: false, after: false },
+ coerce: proc { |v|
+ if v.is_a?(Hash)
+ v
+ elsif v.is_a?(Array)
+ v.each_with_object({}) { |arg, obj| obj[arg] = true }
+ elsif v.is_a?(TrueClass) || v.is_a?(FalseClass)
+ { before: v, after: v }
+ elsif v == :before
+ { before: true, after: false }
+ elsif v == :after
+ { after: true, before: false }
+ end
+ }
- # Install a specific arch
- property :arch, [ String, Array ]
- property :flush_cache, Hash, default: { before: false, after: false }, coerce: proc { |v|
- # TODO these append rather than set. This is probably wrong behavior, but we're preserving it until we know
- if v.is_a?(Array)
- v.each { |arg| flush_cache[arg] = true }
- flush_cache
- elsif v.any?
- v
- else
- # TODO calling flush_cache({}) does a get instead of a set. This is probably wrong behavior, but we're preserving it until we know
- flush_cache
- end
- }
property :allow_downgrade, [ true, false ], default: false
- property :yum_binary, String
+ property :yum_binary, String
end
end
end
diff --git a/lib/chef/resource/yum_repository.rb b/lib/chef/resource/yum_repository.rb
new file mode 100644
index 0000000000..8d88f12ed0
--- /dev/null
+++ b/lib/chef/resource/yum_repository.rb
@@ -0,0 +1,92 @@
+#
+# Author:: Thom May (<thom@chef.io>)
+# Copyright:: Copyright (c) 2016-2017 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class YumRepository < Chef::Resource
+ resource_name :yum_repository
+ provides(:yum_repository) { true }
+
+ description "Use the yum_repository resource to manage a Yum repository configuration"\
+ " file located at /etc/yum.repos.d/repositoryid.repo on the local machine."\
+ " This configuration file specifies which repositories to reference, how to"\
+ " handle cached data, etc."
+ introduced "12.14"
+
+ # http://linux.die.net/man/5/yum.conf as well as
+ # http://dnf.readthedocs.io/en/latest/conf_ref.html
+ property :baseurl, [String, Array]
+ property :clean_headers, [TrueClass, FalseClass], default: false # deprecated
+ property :clean_metadata, [TrueClass, FalseClass], default: true
+ property :cost, String, regex: /^\d+$/
+ property :description, String, default: "Yum Repository"
+ property :enabled, [TrueClass, FalseClass], default: true
+ property :enablegroups, [TrueClass, FalseClass]
+ property :exclude, String
+ property :failovermethod, String, equal_to: %w{priority roundrobin}
+ property :fastestmirror_enabled, [TrueClass, FalseClass]
+ property :gpgcheck, [TrueClass, FalseClass], default: true
+ property :gpgkey, [String, Array]
+ property :http_caching, String, equal_to: %w{packages all none}
+ property :include_config, String
+ property :includepkgs, String
+ property :keepalive, [TrueClass, FalseClass]
+ property :make_cache, [TrueClass, FalseClass], default: true
+ property :max_retries, [String, Integer]
+ property :metadata_expire, String, regex: [/^\d+$/, /^\d+[mhd]$/, /never/]
+ property :metalink, String
+ property :mirror_expire, String, regex: [/^\d+$/, /^\d+[mhd]$/]
+ property :mirrorexpire, String
+ property :mirrorlist_expire, String, regex: [/^\d+$/, /^\d+[mhd]$/]
+ property :mirrorlist, String
+ property :mode, default: "0644"
+ property :options, Hash
+ property :password, String
+ property :priority, String, regex: /^(\d?[1-9]|[0-9][0-9])$/
+ property :proxy_password, String
+ property :proxy_username, String
+ property :proxy, String
+ property :repo_gpgcheck, [TrueClass, FalseClass]
+ property :report_instanceid, [TrueClass, FalseClass]
+
+ property :repositoryid, String,
+ regex: [/^[^\/]+$/],
+ validation_message: "repositoryid property cannot contain a forward slash '/'",
+ name_property: true
+
+ property :skip_if_unavailable, [TrueClass, FalseClass]
+ property :source, String
+ property :sslcacert, String
+ property :sslclientcert, String
+ property :sslclientkey, String
+ property :sslverify, [TrueClass, FalseClass]
+ property :throttle, [String, Integer]
+ property :timeout, String, regex: /^\d+$/
+ property :username, String
+
+ default_action :create
+ allowed_actions :create, :remove, :makecache, :add, :delete
+
+ # provide compatibility with the yum cookbook < 3.0 properties
+ alias_method :url, :baseurl
+ alias_method :keyurl, :gpgkey
+ end
+ end
+end
diff --git a/lib/chef/resource/zypper_package.rb b/lib/chef/resource/zypper_package.rb
index f9e3eef49e..23b8779f25 100644
--- a/lib/chef/resource/zypper_package.rb
+++ b/lib/chef/resource/zypper_package.rb
@@ -20,9 +20,16 @@ require "chef/resource/package"
class Chef
class Resource
+
class ZypperPackage < Chef::Resource::Package
resource_name :zypper_package
provides :package, platform_family: "suse"
+
+ description "Use the zypper_package resource to install, upgrade, and remove"\
+ " packages with Zypper for the SUSE Enterprise and OpenSUSE platforms."
+
+ property :gpg_check, [ TrueClass, FalseClass ], default: lazy { Chef::Config[:zypper_check_gpg] }
+ property :allow_downgrade, [ TrueClass, FalseClass ], default: false
end
end
end
diff --git a/lib/chef/resource/zypper_repository.rb b/lib/chef/resource/zypper_repository.rb
new file mode 100644
index 0000000000..018f03e6b0
--- /dev/null
+++ b/lib/chef/resource/zypper_repository.rb
@@ -0,0 +1,64 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 2017 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class ZypperRepository < Chef::Resource
+ resource_name :zypper_repository
+ provides(:zypper_repository) { true }
+ provides(:zypper_repo) { true }
+
+ description "Use the zypper_repository resource to create Zypper package repositories"\
+ " on SUSE Enterprise Linux and openSUSE systems. This resource maintains"\
+ " full compatibility with the zypper_repository resource in the existing"\
+ " zypper cookbook."
+ introduced "13.3"
+
+ property :repo_name, String,
+ regex: [/^[^\/]+$/],
+ validation_message: "repo_name property cannot contain a forward slash '/'",
+ name_property: true
+
+ property :description, String
+ property :type, String, default: "NONE"
+ property :enabled, [TrueClass, FalseClass], default: true
+ property :autorefresh, [TrueClass, FalseClass], default: true
+ property :gpgcheck, [TrueClass, FalseClass], default: true
+ property :gpgkey, String
+ property :baseurl, String
+ property :mirrorlist, String
+ property :path, String
+ property :priority, Integer, default: 99
+ property :keeppackages, [TrueClass, FalseClass], default: false
+ property :mode, default: "0644"
+ property :refresh_cache, [TrueClass, FalseClass], default: true
+ property :source, String
+ property :cookbook, String
+ property :gpgautoimportkeys, [TrueClass, FalseClass], default: true
+
+ default_action :create
+ allowed_actions :create, :remove, :add, :refresh
+
+ # provide compatibility with the zypper cookbook
+ alias_method :key, :gpgkey
+ alias_method :uri, :baseurl
+ end
+ end
+end
diff --git a/lib/chef/resource_builder.rb b/lib/chef/resource_builder.rb
index 138e401d5c..43a2495cba 100644
--- a/lib/chef/resource_builder.rb
+++ b/lib/chef/resource_builder.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2015-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,7 +31,7 @@ class Chef
attr_reader :resource
# FIXME (ruby-2.1 syntax): most of these are mandatory
- def initialize(type:nil, name:nil, created_at: nil, params: nil, run_context: nil, cookbook_name: nil, recipe_name: nil, enclosing_provider: nil)
+ def initialize(type: nil, name: nil, created_at: nil, params: nil, run_context: nil, cookbook_name: nil, recipe_name: nil, enclosing_provider: nil)
@type = type
@name = name
@created_at = created_at
@@ -43,8 +43,6 @@ class Chef
end
def build(&block)
- raise ArgumentError, "You must supply a name when declaring a #{type} resource" if name.nil?
-
@resource = resource_class.new(name, run_context)
if resource.resource_name.nil?
raise Chef::Exceptions::InvalidResourceSpecification, "#{resource}.resource_name is `nil`! Did you forget to put `provides :blah` or `resource_name :blah` in your resource class?"
@@ -52,14 +50,6 @@ class Chef
resource.source_line = created_at
resource.declared_type = type
- # If we have a resource like this one, we want to steal its state
- # This behavior is very counter-intuitive and should be removed.
- # See CHEF-3694, https://tickets.opscode.com/browse/CHEF-3694
- # Moved to this location to resolve CHEF-5052, https://tickets.opscode.com/browse/CHEF-5052
- if prior_resource
- resource.load_from(prior_resource)
- end
-
resource.cookbook_name = cookbook_name
resource.recipe_name = recipe_name
# Determine whether this resource is being created in the context of an enclosing Provider
@@ -79,15 +69,6 @@ class Chef
end
end
- # emit a cloned resource warning if it is warranted
- if prior_resource
- if is_trivial_resource?(prior_resource) && identicalish_resources?(prior_resource, resource)
- emit_harmless_cloning_debug
- else
- emit_cloned_resource_warning
- end
- end
-
# Run optional resource hook
resource.after_created
@@ -103,46 +84,6 @@ class Chef
@resource_class ||= Chef::Resource.resource_for_node(type, run_context.node)
end
- def is_trivial_resource?(resource)
- identicalish_resources?(resource_class.new(name, run_context), resource)
- end
-
- # this is an equality test specific to checking for 3694 cloning warnings
- def identicalish_resources?(first, second)
- skipped_ivars = [ :@source_line, :@cookbook_name, :@recipe_name, :@params, :@elapsed_time, :@declared_type ]
- checked_ivars = ( first.instance_variables | second.instance_variables ) - skipped_ivars
- non_matching_ivars = checked_ivars.reject do |iv|
- if iv == :@action && ( [first.instance_variable_get(iv)].flatten == [:nothing] || [second.instance_variable_get(iv)].flatten == [:nothing] )
- # :nothing action on either side of the comparison always matches
- true
- else
- first.instance_variable_get(iv) == second.instance_variable_get(iv)
- end
- end
- Chef::Log.debug("ivars which did not match with the prior resource: #{non_matching_ivars}")
- non_matching_ivars.empty?
- end
-
- def emit_cloned_resource_warning
- Chef::Log.warn("Cloning resource attributes for #{resource} from prior resource (CHEF-3694)")
- Chef::Log.warn("Previous #{prior_resource}: #{prior_resource.source_line}") if prior_resource.source_line
- Chef::Log.warn("Current #{resource}: #{resource.source_line}") if resource.source_line
- end
-
- def emit_harmless_cloning_debug
- Chef::Log.debug("Harmless resource cloning from #{prior_resource}:#{prior_resource.source_line} to #{resource}:#{resource.source_line}")
- end
-
- def prior_resource
- @prior_resource ||=
- begin
- key = "#{type}[#{name}]"
- run_context.resource_collection.lookup_local(key)
- rescue Chef::Exceptions::ResourceNotFound
- nil
- end
- end
-
end
end
diff --git a/lib/chef/resource_collection.rb b/lib/chef/resource_collection.rb
index 8eaa2961c4..e450f4f19e 100644
--- a/lib/chef/resource_collection.rb
+++ b/lib/chef/resource_collection.rb
@@ -117,6 +117,16 @@ class Chef
end
end
+ def self.from_hash(o)
+ collection = new()
+ { "@resource_list" => "ResourceList", "@resource_set" => "ResourceSet" }.each_pair do |name, klass|
+ obj = Chef::ResourceCollection.const_get(klass).from_hash(o["instance_vars"].delete(name))
+ collection.instance_variable_set(name.to_sym, obj)
+ end
+ collection.instance_variable_set(:@run_context, o["instance_vars"].delete("@run_context"))
+ collection
+ end
+
private
def lookup_recursive(rc, key)
diff --git a/lib/chef/resource_collection/resource_collection_serialization.rb b/lib/chef/resource_collection/resource_collection_serialization.rb
index 0e76296a4a..4da1ad5048 100644
--- a/lib/chef/resource_collection/resource_collection_serialization.rb
+++ b/lib/chef/resource_collection/resource_collection_serialization.rb
@@ -15,14 +15,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+
+require "chef/json_compat"
+
class Chef
class ResourceCollection
module ResourceCollectionSerialization
# Serialize this object as a hash
def to_hash
instance_vars = Hash.new
- self.instance_variables.each do |iv|
- instance_vars[iv] = self.instance_variable_get(iv)
+ instance_variables.each do |iv|
+ instance_vars[iv] = instance_variable_get(iv)
end
{
"json_class" => self.class.name,
@@ -39,13 +42,17 @@ class Chef
end
module ClassMethods
- def json_create(o)
- collection = self.new()
+ def from_hash(o)
+ collection = new()
o["instance_vars"].each do |k, v|
collection.instance_variable_set(k.to_sym, v)
end
collection
end
+
+ def from_json(j)
+ from_hash(Chef::JSONCompat.parse(j))
+ end
end
def is_chef_resource!(arg)
diff --git a/lib/chef/resource_collection/resource_list.rb b/lib/chef/resource_collection/resource_list.rb
index 9fe012d4c3..75305f63a6 100644
--- a/lib/chef/resource_collection/resource_list.rb
+++ b/lib/chef/resource_collection/resource_list.rb
@@ -95,6 +95,12 @@ class Chef
end
end
+ def self.from_hash(o)
+ collection = new()
+ resources = o["instance_vars"]["@resources"].map { |r| Chef::Resource.from_hash(r) }
+ collection.instance_variable_set(:@resources, resources)
+ collection
+ end
end
end
end
diff --git a/lib/chef/resource_collection/resource_set.rb b/lib/chef/resource_collection/resource_set.rb
index 99be025cd5..6ff29247a0 100644
--- a/lib/chef/resource_collection/resource_set.rb
+++ b/lib/chef/resource_collection/resource_set.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Ball (<tball@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,7 +30,10 @@ class Chef
# Matches a single resource lookup specification,
# e.g., "service[nginx]"
- SINGLE_RESOURCE_MATCH = /^(.+)\[(.+)\]$/
+ SINGLE_RESOURCE_MATCH = /^(.+)\[(.*)\]$/
+
+ # Matches e.g. "apt_update" with no name
+ NAMELESS_RESOURCE_MATCH = /^([^\[\]\s]+)$/
def initialize
@resources_by_key = Hash.new
@@ -118,7 +121,7 @@ class Chef
case query_object
when Chef::Resource
true
- when SINGLE_RESOURCE_MATCH, MULTIPLE_RESOURCE_MATCH
+ when SINGLE_RESOURCE_MATCH, MULTIPLE_RESOURCE_MATCH, NAMELESS_RESOURCE_MATCH
true
when Hash
true
@@ -132,6 +135,14 @@ class Chef
end
end
+ def self.from_hash(o)
+ collection = new()
+ rl = o["instance_vars"]["@resources_by_key"]
+ resources = rl.merge(rl) { |k, r| Chef::Resource.from_hash(r) }
+ collection.instance_variable_set(:@resources_by_key, resources)
+ collection
+ end
+
private
def create_key(resource_type, instance_name)
@@ -146,27 +157,43 @@ class Chef
results << lookup(create_key(resource_type, instance_name))
end
end
- return results
+ results
end
def find_resource_by_string(arg)
- results = Array.new
- case arg
- when MULTIPLE_RESOURCE_MATCH
- resource_type = $1
- arg =~ /^.+\[(.+)\]$/
- resource_list = $1
- resource_list.split(",").each do |instance_name|
- results << lookup(create_key(resource_type, instance_name))
- end
- when SINGLE_RESOURCE_MATCH
+ begin
+ if arg =~ SINGLE_RESOURCE_MATCH
resource_type = $1
name = $2
- results << lookup(create_key(resource_type, name))
+ return [ lookup(create_key(resource_type, name)) ]
+ end
+ rescue Chef::Exceptions::ResourceNotFound => e
+ if arg =~ MULTIPLE_RESOURCE_MATCH
+ begin
+ results = Array.new
+ resource_type = $1
+ arg =~ /^.+\[(.+)\]$/
+ resource_list = $1
+ resource_list.split(",").each do |instance_name|
+ results << lookup(create_key(resource_type, instance_name))
+ end
+ Chef.deprecated(:multiresource_match, "The resource_collection multi-resource syntax is deprecated")
+ return results
+ rescue Chef::Exceptions::ResourceNotFound
+ raise e
+ end
else
- raise ArgumentError, "Bad string format #{arg}, you must have a string like resource_type[name]!"
+ raise e
+ end
end
- return results
+
+ if arg =~ NAMELESS_RESOURCE_MATCH
+ resource_type = $1
+ name = ""
+ return [ lookup(create_key(resource_type, name)) ]
+ end
+
+ raise ArgumentError, "Bad string format #{arg}, you must have a string like resource_type[name]!"
end
end
end
diff --git a/lib/chef/resource_collection/stepable_iterator.rb b/lib/chef/resource_collection/stepable_iterator.rb
index d1165764ca..958ffa28cb 100644
--- a/lib/chef/resource_collection/stepable_iterator.rb
+++ b/lib/chef/resource_collection/stepable_iterator.rb
@@ -100,9 +100,7 @@ class Chef
end
def iterate
- while @position < size && !paused?
- step
- end
+ step while @position < size && !paused?
collection
end
diff --git a/lib/chef/resource_inspector.rb b/lib/chef/resource_inspector.rb
new file mode 100644
index 0000000000..b112cb0565
--- /dev/null
+++ b/lib/chef/resource_inspector.rb
@@ -0,0 +1,92 @@
+# Copyright:: Copyright 2018, Chef Software, Inc
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/cookbook_loader"
+require "chef/cookbook/file_vendor"
+require "chef/cookbook/file_system_file_vendor"
+require "chef/resource/lwrp_base"
+require "chef/run_context"
+require "chef/node"
+require "chef/resources"
+require "chef/json_compat"
+
+module ResourceInspector
+ def self.extract_resource(resource, complete = false)
+ data = {}
+ data[:description] = resource.description
+ # data[:deprecated] = resource.deprecated || false
+ data[:actions] = resource.allowed_actions
+ data[:examples] = resource.examples
+ data[:introduced] = resource.introduced
+
+ properties = unless complete
+ resource.properties.reject { |_, k| k.options[:declared_in] == Chef::Resource }
+ else
+ resource.properties
+ end
+
+ data[:properties] = properties.each_with_object([]) do |(n, k), acc|
+ opts = k.options
+ acc << { name: n, description: opts[:description],
+ introduced: opts[:introduced], is: opts[:is],
+ deprecated: opts[:deprecated] || false,
+ name_property: opts[:name_property] || false }
+ end
+ data
+ end
+
+ def self.extract_cookbook(path, complete)
+ path = File.expand_path(path)
+ dir, name = File.split(path)
+ Chef::Cookbook::FileVendor.fetch_from_disk(path)
+ loader = Chef::CookbookLoader.new(dir)
+ cookbooks = loader.load_cookbooks
+ resources = cookbooks[name].files_for(:resources)
+
+ resources.each_with_object({}) do |r, res|
+ pth = r["full_path"]
+ cur = Chef::Resource::LWRPBase.build_from_file(name, pth, Chef::RunContext.new(Chef::Node.new, nil, nil))
+ res[cur.resource_name] = extract_resource(cur, complete)
+ end
+ end
+
+ # If we're given no resources, dump all of Chef's built ins
+ # otherwise, if we have a path then extract all the resources from the cookbook
+ # or else do a list of built in resources
+ #
+ # @param complete [TrueClass, FalseClass] Whether to show properties defined in the base Resource class
+ def self.inspect(arguments = [], complete: false)
+ output = if arguments.empty?
+ ObjectSpace.each_object(Class).select { |k| k < Chef::Resource }.each_with_object({}) { |klass, acc| acc[klass.resource_name] = extract_resource(klass) }
+ else
+ arguments.each_with_object({}) do |arg, acc|
+ if File.directory?(arg)
+ extract_cookbook(arg, complete).each { |k, v| acc[k] = v }
+ else
+ r = Chef::ResourceResolver.resolve(arg.to_sym, canonical: nil)
+ acc[r.resource_name] = extract_resource(r, complete)
+ end
+ end
+ end
+
+ puts Chef::JSONCompat.to_json_pretty(output)
+ end
+
+ def self.start
+ inspect(ARGV, complete: true)
+ end
+
+end
diff --git a/lib/chef/resource_reporter.rb b/lib/chef/resource_reporter.rb
index 4135483441..c76ac0a4a3 100644
--- a/lib/chef/resource_reporter.rb
+++ b/lib/chef/resource_reporter.rb
@@ -3,7 +3,7 @@
# Author:: Prajakta Purohit (prajakta@chef.io>)
# Auther:: Tyler Cloke (<tyler@opscode.com>)
#
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright 2012-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,11 +26,11 @@ require "chef/event_dispatch/base"
class Chef
class ResourceReporter < EventDispatch::Base
- class ResourceReport < Struct.new(:new_resource,
- :current_resource,
- :action,
- :exception,
- :elapsed_time)
+ ResourceReport = Struct.new(:new_resource,
+ :current_resource,
+ :action,
+ :exception,
+ :elapsed_time) do
def self.new_with_current_state(new_resource, action, current_resource)
report = new
@@ -87,14 +87,13 @@ class Chef
end
def success?
- !self.exception
+ !exception
end
end # End class ResouceReport
attr_reader :updated_resources
attr_reader :status
attr_reader :exception
- attr_reader :run_id
attr_reader :error_descriptions
PROTOCOL_VERSION = "0.1.0"
@@ -154,7 +153,7 @@ class Chef
Chef::Log.info(message + reason + reporting_status)
else
reporting_status = "Disabling reporting for run."
- Chef::Log.debug(message + reason + reporting_status)
+ Chef::Log.trace(message + reason + reporting_status)
end
end
@@ -198,6 +197,17 @@ class Chef
def resource_completed(new_resource)
if @pending_update && !nested_resource?(new_resource)
@pending_update.finish
+
+ # Verify if the resource has sensitive data
+ # and create a new blank resource with only
+ # the name so we can report it back without
+ # sensitive data
+ if @pending_update.new_resource.sensitive
+ klass = @pending_update.new_resource.class
+ resource_name = @pending_update.new_resource.name
+ @pending_update.new_resource = klass.new(resource_name)
+ end
+
@updated_resources << @pending_update
@pending_update = nil
end
@@ -227,9 +237,9 @@ class Chef
run_data = prepare_run_data
resource_history_url = "reports/nodes/#{node_name}/runs/#{run_id}"
Chef::Log.info("Sending resource update report (run-id: #{run_id})")
- Chef::Log.debug run_data.inspect
+ Chef::Log.trace run_data.inspect
compressed_data = encode_gzip(Chef::JSONCompat.to_json(run_data))
- Chef::Log.debug("Sending compressed run data...")
+ Chef::Log.trace("Sending compressed run data...")
# Since we're posting compressed data we can not directly call post which expects JSON
begin
@rest_client.raw_request(:POST, resource_history_url, headers({ "Content-Encoding" => "gzip" }), compressed_data)
@@ -242,7 +252,7 @@ class Chef
end
end
else
- Chef::Log.debug("Server doesn't support resource history, skipping resource report.")
+ Chef::Log.trace("Server doesn't support resource history, skipping resource report.")
end
end
diff --git a/lib/chef/resource_resolver.rb b/lib/chef/resource_resolver.rb
index 769272d637..779377c042 100644
--- a/lib/chef/resource_resolver.rb
+++ b/lib/chef/resource_resolver.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2015-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -54,11 +54,6 @@ class Chef
# @api private
attr_reader :resource_name
# @api private
- def resource
- Chef.log_deprecation("Chef::ResourceResolver.resource deprecated. Use resource_name instead.")
- resource_name
- end
- # @api private
attr_reader :action
# @api private
attr_reader :canonical
@@ -82,14 +77,14 @@ class Chef
# @api private use Chef::ResourceResolver.resolve instead.
def resolve
# log this so we know what resources will work for the generic resource on the node (early cut)
- Chef::Log.debug "Resources for generic #{resource_name} resource enabled on node include: #{prioritized_handlers}"
+ Chef::Log.trace "Resources for generic #{resource_name} resource enabled on node include: #{prioritized_handlers}"
handler = prioritized_handlers.first
if handler
- Chef::Log.debug "Resource for #{resource_name} is #{handler}"
+ Chef::Log.trace "Resource for #{resource_name} is #{handler}"
else
- Chef::Log.debug "Dynamic resource resolver FAILED to resolve a resource for #{resource_name}"
+ Chef::Log.trace "Dynamic resource resolver FAILED to resolve a resource for #{resource_name}"
end
handler
@@ -97,7 +92,7 @@ class Chef
# @api private
def list
- Chef::Log.debug "Resources for generic #{resource_name} resource enabled on node include: #{prioritized_handlers}"
+ Chef::Log.trace "Resources for generic #{resource_name} resource enabled on node include: #{prioritized_handlers}"
prioritized_handlers
end
@@ -161,25 +156,5 @@ class Chef
def overrode_provides?(handler)
handler.method(:provides?).owner != Chef::Resource.method(:provides?).owner
end
-
- module Deprecated
- # return a deterministically sorted list of Chef::Resource subclasses
- def resources
- Chef::Resource.sorted_descendants
- end
-
- def enabled_handlers
- handlers = super
- if handlers.empty?
- handlers = resources.select { |handler| overrode_provides?(handler) && handler.provides?(node, resource_name) }
- handlers.each do |handler|
- Chef.log_deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource_name}, but provides #{resource_name.inspect} was never called!")
- Chef.log_deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
- end
- end
- handlers
- end
- end
- prepend Deprecated
end
end
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index af9c918f55..c232ce04e1 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -17,25 +17,25 @@
#
require "chef/resource/apt_package"
+require "chef/resource/apt_preference"
require "chef/resource/apt_repository"
require "chef/resource/apt_update"
require "chef/resource/bash"
require "chef/resource/batch"
require "chef/resource/breakpoint"
+require "chef/resource/build_essential"
require "chef/resource/cookbook_file"
require "chef/resource/chef_gem"
+require "chef/resource/chef_handler"
require "chef/resource/chocolatey_package"
require "chef/resource/cron"
require "chef/resource/csh"
-require "chef/resource/deploy"
-require "chef/resource/deploy_revision"
require "chef/resource/directory"
+require "chef/resource/dmg_package"
require "chef/resource/dpkg_package"
+require "chef/resource/dnf_package"
require "chef/resource/dsc_script"
require "chef/resource/dsc_resource"
-require "chef/resource/easy_install_package"
-require "chef/resource/env"
-require "chef/resource/erl_call"
require "chef/resource/execute"
require "chef/resource/file"
require "chef/resource/freebsd_package"
@@ -44,17 +44,25 @@ require "chef/resource/gem_package"
require "chef/resource/git"
require "chef/resource/group"
require "chef/resource/http_request"
+require "chef/resource/hostname"
+require "chef/resource/homebrew_cask"
require "chef/resource/homebrew_package"
+require "chef/resource/homebrew_tap"
require "chef/resource/ifconfig"
require "chef/resource/ksh"
require "chef/resource/launchd"
require "chef/resource/link"
require "chef/resource/log"
require "chef/resource/macports_package"
+require "chef/resource/macos_userdefaults"
require "chef/resource/mdadm"
require "chef/resource/mount"
require "chef/resource/ohai"
+require "chef/resource/ohai_hint"
require "chef/resource/openbsd_package"
+require "chef/resource/openssl_dhparam"
+require "chef/resource/openssl_rsa_private_key"
+require "chef/resource/openssl_rsa_public_key"
require "chef/resource/package"
require "chef/resource/pacman_package"
require "chef/resource/paludis_package"
@@ -67,6 +75,11 @@ require "chef/resource/reboot"
require "chef/resource/registry_key"
require "chef/resource/remote_directory"
require "chef/resource/remote_file"
+require "chef/resource/rhsm_errata_level"
+require "chef/resource/rhsm_errata"
+require "chef/resource/rhsm_register"
+require "chef/resource/rhsm_repo"
+require "chef/resource/rhsm_subscription"
require "chef/resource/rpm_package"
require "chef/resource/solaris_package"
require "chef/resource/route"
@@ -75,16 +88,42 @@ require "chef/resource/ruby_block"
require "chef/resource/scm"
require "chef/resource/script"
require "chef/resource/service"
+require "chef/resource/sudo"
+require "chef/resource/sysctl"
+require "chef/resource/swap_file"
require "chef/resource/systemd_unit"
require "chef/resource/windows_service"
require "chef/resource/subversion"
require "chef/resource/smartos_package"
require "chef/resource/template"
-require "chef/resource/timestamped_deploy"
require "chef/resource/user"
+require "chef/resource/user/aix_user"
+require "chef/resource/user/dscl_user"
+require "chef/resource/user/linux_user"
+require "chef/resource/user/pw_user"
+require "chef/resource/user/solaris_user"
+require "chef/resource/user/windows_user"
require "chef/resource/whyrun_safe_ruby_block"
+require "chef/resource/windows_env"
require "chef/resource/windows_package"
require "chef/resource/yum_package"
+require "chef/resource/yum_repository"
require "chef/resource/lwrp_base"
require "chef/resource/bff_package"
require "chef/resource/zypper_package"
+require "chef/resource/zypper_repository"
+require "chef/resource/cab_package"
+require "chef/resource/powershell_package"
+require "chef/resource/msu_package"
+require "chef/resource/windows_ad_join"
+require "chef/resource/windows_auto_run"
+require "chef/resource/windows_feature"
+require "chef/resource/windows_feature_dism"
+require "chef/resource/windows_feature_powershell"
+require "chef/resource/windows_font"
+require "chef/resource/windows_pagefile"
+require "chef/resource/windows_path"
+require "chef/resource/windows_printer"
+require "chef/resource/windows_printer_port"
+require "chef/resource/windows_shortcut"
+require "chef/resource/windows_task"
diff --git a/lib/chef/rest.rb b/lib/chef/rest.rb
deleted file mode 100644
index a3c5c66b8a..0000000000
--- a/lib/chef/rest.rb
+++ /dev/null
@@ -1,209 +0,0 @@
-#--
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Thom May (<thom@clearairturbulence.org>)
-# Author:: Nuo Yan (<nuo@chef.io>)
-# Author:: Christopher Brown (<cb@chef.io>)
-# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "tempfile"
-require "chef/http"
-class Chef
- class HTTP; end
- class REST < HTTP; end
-end
-
-require "chef/http/authenticator"
-require "chef/http/decompressor"
-require "chef/http/json_input"
-require "chef/http/json_to_model_output"
-require "chef/http/cookie_manager"
-require "chef/http/validate_content_length"
-require "chef/config"
-require "chef/exceptions"
-require "chef/platform/query_helpers"
-require "chef/http/remote_request_id"
-
-class Chef
-
- # == Chef::REST
- # Chef's custom REST client with built-in JSON support and RSA signed header
- # authentication.
- class REST < HTTP
-
- # Backwards compatibility for things that use
- # Chef::REST::RESTRequest or its constants
- RESTRequest = HTTP::HTTPRequest
-
- attr_accessor :url, :cookies, :sign_on_redirect, :redirect_limit
-
- attr_reader :authenticator
-
- # Create a REST client object. The supplied +url+ is used as the base for
- # all subsequent requests. For example, when initialized with a base url
- # http://localhost:4000, a call to +get_rest+ with 'nodes' will make an
- # HTTP GET request to http://localhost:4000/nodes
- def initialize(url, client_name = Chef::Config[:node_name], signing_key_filename = Chef::Config[:client_key], options = {})
- Chef.log_deprecation("Chef::REST is deprecated. Please use Chef::ServerAPI, or investigate Ridley or ChefAPI.")
-
- signing_key_filename = nil if chef_zero_uri?(url)
-
- options = options.dup
- options[:client_name] = client_name
- options[:signing_key_filename] = signing_key_filename
-
- super(url, options)
-
- @decompressor = Decompressor.new(options)
- @authenticator = Authenticator.new(options)
- @request_id = RemoteRequestID.new(options)
-
- @middlewares << JSONInput.new(options)
- @middlewares << JSONToModelOutput.new(options)
- @middlewares << CookieManager.new(options)
- @middlewares << @decompressor
- @middlewares << @authenticator
- @middlewares << @request_id
-
- # ValidateContentLength should come after Decompressor
- # because the order of middlewares is reversed when handling
- # responses.
- @middlewares << ValidateContentLength.new(options)
- end
-
- def signing_key_filename
- authenticator.signing_key_filename
- end
-
- def auth_credentials
- authenticator.auth_credentials
- end
-
- def client_name
- authenticator.client_name
- end
-
- def signing_key
- authenticator.raw_key
- end
-
- def sign_requests?
- authenticator.sign_requests?
- end
-
- # Send an HTTP GET request to the path
- #
- # Using this method to +fetch+ a file is considered deprecated.
- #
- # === Parameters
- # path:: The path to GET
- # raw:: Whether you want the raw body returned, or JSON inflated. Defaults
- # to JSON inflated.
- def get(path, raw = false, headers = {})
- if raw
- streaming_request(path, headers)
- else
- request(:GET, path, headers)
- end
- end
-
- alias :get_rest :get
-
- alias :delete_rest :delete
-
- alias :post_rest :post
-
- alias :put_rest :put
-
- # Streams a download to a tempfile, then yields the tempfile to a block.
- # After the download, the tempfile will be closed and unlinked.
- # If you rename the tempfile, it will not be deleted.
- # Beware that if the server streams infinite content, this method will
- # stream it until you run out of disk space.
- def fetch(path, headers = {})
- streaming_request(create_url(path), headers) { |tmp_file| yield tmp_file }
- end
-
- alias :api_request :request
-
- # Do a HTTP request where no middleware is loaded (e.g. JSON input/output
- # conversion) but the standard Chef Authentication headers are added to the
- # request.
- def raw_http_request(method, path, headers, data)
- url = create_url(path)
- method, url, headers, data = @authenticator.handle_request(method, url, headers, data)
- method, url, headers, data = @request_id.handle_request(method, url, headers, data)
- response, rest_request, return_value = send_http_request(method, url, headers, data)
- response.error! unless success_response?(response)
- return_value
- rescue Exception => exception
- log_failed_request(response, return_value) unless response.nil?
-
- if exception.respond_to?(:chef_rest_request=)
- exception.chef_rest_request = rest_request
- end
- raise
- end
-
- # Deprecated:
- # Responsibilities of this method have been split up. The #http_client is
- # now responsible for making individual requests, while
- # #retrying_http_errors handles error/retry logic.
- def retriable_http_request(method, url, req_body, headers)
- rest_request = Chef::HTTP::HTTPRequest.new(method, url, req_body, headers)
-
- Chef::Log.debug("Sending HTTP request via #{method} to #{url.host}:#{url.port}#{rest_request.path}")
-
- retrying_http_errors(url) do
- yield rest_request
- end
- end
-
- # Customized streaming behavior; sets the accepted content type to "*/*"
- # if not otherwise specified for compatibility purposes
- def streaming_request(url, headers, &block)
- headers["Accept"] ||= "*/*"
- super
- end
-
- alias :retriable_rest_request :retriable_http_request
-
- def follow_redirect
- unless @sign_on_redirect
- @authenticator.sign_request = false
- end
- super
- ensure
- @authenticator.sign_request = true
- end
-
- public :create_url
-
- ############################################################################
- # DEPRECATED
- ############################################################################
-
- def decompress_body(body)
- @decompressor.decompress_body(body)
- end
-
- def authentication_headers(method, url, json_body = nil)
- authenticator.authentication_headers(method, url, json_body)
- end
-
- end
-end
diff --git a/lib/chef/role.rb b/lib/chef/role.rb
index 331fa614f1..ad01e9fa26 100644
--- a/lib/chef/role.rb
+++ b/lib/chef/role.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Nuo Yan (<nuo@chef.io>)
# Author:: Christopher Brown (<cb@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,8 +33,6 @@ class Chef
include Chef::Mixin::FromFile
include Chef::Mixin::ParamsValidate
- attr_accessor :chef_server_rest
-
# Create a new Chef::Role object.
def initialize(chef_server_rest: nil)
@name = ""
@@ -168,12 +166,6 @@ class Chef
self
end
- # Create a Chef::Role from JSON
- def self.json_create(o)
- Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please use Chef::Role#from_hash")
- from_hash(o)
- end
-
def self.from_hash(o)
role = new
role.name(o["name"])
diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb
index 29c936a932..2b8c7cda30 100644
--- a/lib/chef/run_context.rb
+++ b/lib/chef/run_context.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Tim Hinderliter (<tim@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,7 +29,6 @@ require "forwardable"
class Chef
- # == Chef::RunContext
# Value object that loads and tracks the context of a Chef run
class RunContext
#
@@ -85,6 +84,17 @@ class Chef
attr_reader :parent_run_context
#
+ # The root run context.
+ #
+ # @return [Chef::RunContext] The root run context.
+ #
+ def root_run_context
+ rc = self
+ rc = rc.parent_run_context until rc.parent_run_context.nil?
+ rc
+ end
+
+ #
# The collection of resources intended to be converged (and able to be
# notified).
#
@@ -100,6 +110,11 @@ class Chef
attr_reader :audits
#
+ # Pointer back to the Chef::Runner that created this
+ #
+ attr_accessor :runner
+
+ #
# Notification handling
#
@@ -138,6 +153,13 @@ class Chef
#
attr_reader :delayed_actions
+ #
+ # A child of the root Chef::Log logging object.
+ #
+ # @return Mixlib::Log::Child A child logger
+ #
+ attr_reader :logger
+
# Creates a new Chef::RunContext object and populates its fields. This object gets
# used by the Chef Server to generate a fully compiled recipe list for a node.
#
@@ -147,10 +169,11 @@ class Chef
# @param events [EventDispatch::Dispatcher] The event dispatcher for this
# run.
#
- def initialize(node, cookbook_collection, events)
+ def initialize(node, cookbook_collection, events, logger = nil)
@node = node
@cookbook_collection = cookbook_collection
@events = events
+ @logger = logger || Chef::Log.with_child
node.run_context = self
node.set_cookbook_attribute
@@ -191,43 +214,37 @@ class Chef
#
# Adds an before notification to the +before_notification_collection+.
#
- # @param [Chef::Resource::Notification] The notification to add.
+ # @param [Chef::Resource::Notification] notification The notification to add.
#
def notifies_before(notification)
- nr = notification.notifying_resource
- if nr.instance_of?(Chef::Resource)
- before_notification_collection[nr.name] << notification
- else
- before_notification_collection[nr.declared_key] << notification
- end
+ # Note for the future, notification.notifying_resource may be an instance
+ # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
+ # with a string value.
+ before_notification_collection[notification.notifying_resource.declared_key] << notification
end
#
# Adds an immediate notification to the +immediate_notification_collection+.
#
- # @param [Chef::Resource::Notification] The notification to add.
+ # @param [Chef::Resource::Notification] notification The notification to add.
#
def notifies_immediately(notification)
- nr = notification.notifying_resource
- if nr.instance_of?(Chef::Resource)
- immediate_notification_collection[nr.name] << notification
- else
- immediate_notification_collection[nr.declared_key] << notification
- end
+ # Note for the future, notification.notifying_resource may be an instance
+ # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
+ # with a string value.
+ immediate_notification_collection[notification.notifying_resource.declared_key] << notification
end
#
# Adds a delayed notification to the +delayed_notification_collection+.
#
- # @param [Chef::Resource::Notification] The notification to add.
+ # @param [Chef::Resource::Notification] notification The notification to add.
#
def notifies_delayed(notification)
- nr = notification.notifying_resource
- if nr.instance_of?(Chef::Resource)
- delayed_notification_collection[nr.name] << notification
- else
- delayed_notification_collection[nr.declared_key] << notification
- end
+ # Note for the future, notification.notifying_resource may be an instance
+ # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
+ # with a string value.
+ delayed_notification_collection[notification.notifying_resource.declared_key] << notification
end
#
@@ -235,7 +252,7 @@ class Chef
#
def add_delayed_action(notification)
if delayed_actions.any? { |existing_notification| existing_notification.duplicates?(notification) }
- Chef::Log.info( "#{notification.notifying_resource} not queuing delayed action #{notification.action} on #{notification.resource}"\
+ logger.info( "#{notification.notifying_resource} not queuing delayed action #{notification.action} on #{notification.resource}"\
" (delayed), as it's already been queued")
else
delayed_actions << notification
@@ -245,50 +262,29 @@ class Chef
#
# Get the list of before notifications sent by the given resource.
#
- # TODO seriously, this is actually wrong. resource.name is not unique,
- # you need the type as well.
- #
# @return [Array[Notification]]
#
def before_notifications(resource)
- if resource.instance_of?(Chef::Resource)
- return before_notification_collection[resource.name]
- else
- return before_notification_collection[resource.declared_key]
- end
+ before_notification_collection[resource.declared_key]
end
#
# Get the list of immediate notifications sent by the given resource.
#
- # TODO seriously, this is actually wrong. resource.name is not unique,
- # you need the type as well.
- #
# @return [Array[Notification]]
#
def immediate_notifications(resource)
- if resource.instance_of?(Chef::Resource)
- return immediate_notification_collection[resource.name]
- else
- return immediate_notification_collection[resource.declared_key]
- end
+ immediate_notification_collection[resource.declared_key]
end
#
# Get the list of delayed (end of run) notifications sent by the given
# resource.
#
- # TODO seriously, this is actually wrong. resource.name is not unique,
- # you need the type as well.
- #
# @return [Array[Notification]]
#
def delayed_notifications(resource)
- if resource.instance_of?(Chef::Resource)
- return delayed_notification_collection[resource.name]
- else
- return delayed_notification_collection[resource.declared_key]
- end
+ delayed_notification_collection[resource.declared_key]
end
#
@@ -321,21 +317,21 @@ class Chef
# I don't see anything different beyond accepting and returning an
# array of recipes.
#
- # @param recipe_names [Array[String]] The recipe name (e.g 'my_cookbook' or
+ # @param recipe_name [Array[String]] The recipe name (e.g 'my_cookbook' or
# 'my_cookbook::my_resource').
- # @param current_cookbook The cookbook we are currently running in.
+ # @param current_cookbook [String] The cookbook we are currently running in.
#
# @return A truthy value if the load occurred; `false` if already loaded.
#
# @see DSL::IncludeRecipe#load_recipe
#
def load_recipe(recipe_name, current_cookbook: nil)
- Chef::Log.debug("Loading recipe #{recipe_name} via include_recipe")
+ logger.trace("Loading recipe #{recipe_name} via include_recipe")
cookbook_name, recipe_short_name = Chef::Recipe.parse_recipe_name(recipe_name, current_cookbook: current_cookbook)
if unreachable_cookbook?(cookbook_name) # CHEF-4367
- Chef::Log.warn(<<-ERROR_MESSAGE)
+ logger.warn(<<-ERROR_MESSAGE)
MissingCookbookDependency:
Recipe `#{recipe_name}` is not in the run_list, and cookbook '#{cookbook_name}'
is not a dependency of any cookbook in the run_list. To load this recipe,
@@ -345,7 +341,7 @@ ERROR_MESSAGE
end
if loaded_fully_qualified_recipe?(cookbook_name, recipe_short_name)
- Chef::Log.debug("I am not loading #{recipe_name}, because I have already seen it.")
+ logger.trace("I am not loading #{recipe_name}, because I have already seen it.")
false
else
loaded_recipe(cookbook_name, recipe_short_name)
@@ -369,7 +365,7 @@ ERROR_MESSAGE
raise Chef::Exceptions::RecipeNotFound, "could not find recipe file #{recipe_file}"
end
- Chef::Log.debug("Loading recipe file #{recipe_file}")
+ logger.trace("Loading recipe file #{recipe_file}")
recipe = Chef::Recipe.new("@recipe_files", recipe_file, self)
recipe.from_file(recipe_file)
recipe
@@ -563,15 +559,22 @@ ERROR_MESSAGE
# 5. raise an exception on any second call.
# 6. ?
def request_reboot(reboot_info)
- Chef::Log.info "Changing reboot status from #{self.reboot_info.inspect} to #{reboot_info.inspect}"
+ logger.info "Changing reboot status from #{self.reboot_info.inspect} to #{reboot_info.inspect}"
@reboot_info = reboot_info
end
+ #
+ # Cancels a pending reboot
+ #
def cancel_reboot
- Chef::Log.info "Changing reboot status from #{reboot_info.inspect} to {}"
+ logger.info "Changing reboot status from #{reboot_info.inspect} to {}"
@reboot_info = {}
end
+ #
+ # Checks to see if a reboot has been requested
+ # @return [Boolean]
+ #
def reboot_requested?
reboot_info.size > 0
end
@@ -592,27 +595,6 @@ ERROR_MESSAGE
attr_reader :loaded_attributes_hash
attr_reader :loaded_recipes_hash
- module Deprecated
- ###
- # These need to be settable so deploy can run a resource_collection
- # independent of any cookbooks via +recipe_eval+
- def audits=(value)
- Chef.log_deprecation("Setting run_context.audits will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
- @audits = value
- end
-
- def immediate_notification_collection=(value)
- Chef.log_deprecation("Setting run_context.immediate_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
- @immediate_notification_collection = value
- end
-
- def delayed_notification_collection=(value)
- Chef.log_deprecation("Setting run_context.delayed_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
- @delayed_notification_collection = value
- end
- end
- prepend Deprecated
-
#
# A child run context. Delegates all root context calls to its parent.
#
@@ -639,6 +621,7 @@ ERROR_MESSAGE
loaded_recipe?
loaded_recipes
loaded_recipes_hash
+ logger
node
open_stream
reboot_info
@@ -680,14 +663,19 @@ ERROR_MESSAGE
notifies_immediately
notifies_delayed
parent_run_context
+ root_run_context
resource_collection
resource_collection=
+ runner
+ runner=
}.map { |x| x.to_sym }
# Verify that we didn't miss any methods
- missing_methods = superclass.instance_methods(false) - instance_methods(false) - CHILD_STATE
- if !missing_methods.empty?
- raise "ERROR: not all methods of RunContext accounted for in ChildRunContext! All methods must be marked as child methods with CHILD_STATE or delegated to the parent_run_context. Missing #{missing_methods.join(", ")}."
+ unless @__skip_method_checking # hook specifically for compat_resource
+ missing_methods = superclass.instance_methods(false) - instance_methods(false) - CHILD_STATE
+ if !missing_methods.empty?
+ raise "ERROR: not all methods of RunContext accounted for in ChildRunContext! All methods must be marked as child methods with CHILD_STATE or delegated to the parent_run_context. Missing #{missing_methods.join(", ")}."
+ end
end
end
end
diff --git a/lib/chef/run_context/cookbook_compiler.rb b/lib/chef/run_context/cookbook_compiler.rb
index bdf3a1251c..25d32ea7e2 100644
--- a/lib/chef/run_context/cookbook_compiler.rb
+++ b/lib/chef/run_context/cookbook_compiler.rb
@@ -31,12 +31,14 @@ class Chef
class CookbookCompiler
attr_reader :events
attr_reader :run_list_expansion
+ attr_reader :logger
def initialize(run_context, run_list_expansion, events)
@run_context = run_context
@events = events
@run_list_expansion = run_list_expansion
@cookbook_order = nil
+ @logger = run_context.logger.with_child(subsystem: "cookbook_compiler")
end
# Chef::Node object for the current run.
@@ -57,6 +59,7 @@ class Chef
# Run the compile phase of the chef run. Loads files in the following order:
# * Libraries
+ # * Ohai
# * Attributes
# * LWRPs
# * Resource Definitions
@@ -69,6 +72,7 @@ class Chef
# #cookbook_order for more information.
def compile
compile_libraries
+ compile_ohai_plugins
compile_attributes
compile_lwrps
compile_resource_definitions
@@ -87,7 +91,7 @@ class Chef
cookbook = Chef::Recipe.parse_recipe_name(recipe).first
add_cookbook_with_deps(ordered_cookbooks, seen_cookbooks, cookbook)
end
- Chef::Log.debug("Cookbooks to compile: #{ordered_cookbooks.inspect}")
+ logger.debug("Cookbooks to compile: #{ordered_cookbooks.inspect}")
ordered_cookbooks
end
end
@@ -101,11 +105,32 @@ class Chef
@events.library_load_complete
end
+ # Loads Ohai Plugins from cookbooks, and ensure any old ones are
+ # properly cleaned out
+ def compile_ohai_plugins
+ ohai_plugin_count = count_files_by_segment(:ohai)
+ @events.ohai_plugin_load_start(ohai_plugin_count)
+ FileUtils.rm_rf(Chef::Config[:ohai_segment_plugin_path])
+
+ cookbook_order.each do |cookbook|
+ load_ohai_plugins_from_cookbook(cookbook)
+ end
+
+ # Doing a full ohai system check is costly, so only do so if we've loaded additional plugins
+ if ohai_plugin_count > 0
+ # FIXME(log): figure out what the ohai logger looks like here
+ ohai = Ohai::System.new.run_additional_plugins(Chef::Config[:ohai_segment_plugin_path])
+ node.consume_ohai_data(ohai)
+ end
+
+ @events.ohai_plugin_load_complete
+ end
+
# Loads attributes files from cookbooks. Attributes files are loaded
# according to #cookbook_order; within a cookbook, +default.rb+ is loaded
# first, then the remaining attributes files in lexical sort order.
def compile_attributes
- @events.attribute_load_start(count_files_by_segment(:attributes))
+ @events.attribute_load_start(count_files_by_segment(:attributes, "attributes.rb"))
cookbook_order.each do |cookbook|
load_attributes_from_cookbook(cookbook)
end
@@ -137,13 +162,14 @@ class Chef
@events.recipe_load_start(run_list_expansion.recipes.size)
run_list_expansion.recipes.each do |recipe|
begin
+ path = resolve_recipe(recipe)
@run_context.load_recipe(recipe)
+ @events.recipe_file_loaded(path, recipe)
rescue Chef::Exceptions::RecipeNotFound => e
@events.recipe_not_found(e)
raise
rescue Exception => e
- path = resolve_recipe(recipe)
- @events.recipe_file_load_failed(path, e)
+ @events.recipe_file_load_failed(path, e, recipe)
raise
end
end
@@ -165,7 +191,16 @@ class Chef
def load_attributes_from_cookbook(cookbook_name)
list_of_attr_files = files_in_cookbook_by_segment(cookbook_name, :attributes).dup
- if default_file = list_of_attr_files.find { |path| File.basename(path) == "default.rb" }
+ root_alias = cookbook_collection[cookbook_name].files_for(:root_files).find { |record| record[:name] == "attributes.rb" }
+ default_file = list_of_attr_files.find { |path| File.basename(path) == "default.rb" }
+ if root_alias
+ if default_file
+ logger.error("Cookbook #{cookbook_name} contains both attributes.rb and and attributes/default.rb, ignoring attributes/default.rb")
+ list_of_attr_files.delete(default_file)
+ end
+ # The actual root_alias path decoding is handled in CookbookVersion#attribute_filenames_by_short_filename
+ load_attribute_file(cookbook_name.to_s, "default")
+ elsif default_file
list_of_attr_files.delete(default_file)
load_attribute_file(cookbook_name.to_s, default_file)
end
@@ -176,7 +211,8 @@ class Chef
end
def load_attribute_file(cookbook_name, filename)
- Chef::Log.debug("Node #{node.name} loading cookbook #{cookbook_name}'s attribute file #{filename}")
+ # FIXME(log): should be trace
+ logger.debug("Node #{node.name} loading cookbook #{cookbook_name}'s attribute file #{filename}")
attr_file_basename = ::File.basename(filename, ".rb")
node.include_attribute("#{cookbook_name}::#{attr_file_basename}")
rescue Exception => e
@@ -188,7 +224,8 @@ class Chef
files_in_cookbook_by_segment(cookbook_name, :libraries).each do |filename|
next unless File.extname(filename) == ".rb"
begin
- Chef::Log.debug("Loading cookbook #{cookbook_name}'s library file: #{filename}")
+ # FIXME(log): should be trace
+ logger.debug("Loading cookbook #{cookbook_name}'s library file: #{filename}")
Kernel.load(filename)
@events.library_file_loaded(filename)
rescue Exception => e
@@ -208,7 +245,8 @@ class Chef
end
def load_lwrp_provider(cookbook_name, filename)
- Chef::Log.debug("Loading cookbook #{cookbook_name}'s providers from #{filename}")
+ # FIXME(log): should be trace
+ logger.debug("Loading cookbook #{cookbook_name}'s providers from #{filename}")
Chef::Provider::LWRPBase.build_from_file(cookbook_name, filename, self)
@events.lwrp_file_loaded(filename)
rescue Exception => e
@@ -217,7 +255,8 @@ class Chef
end
def load_lwrp_resource(cookbook_name, filename)
- Chef::Log.debug("Loading cookbook #{cookbook_name}'s resources from #{filename}")
+ # FIXME(log): should be trace
+ logger.debug("Loading cookbook #{cookbook_name}'s resources from #{filename}")
Chef::Resource::LWRPBase.build_from_file(cookbook_name, filename, self)
@events.lwrp_file_loaded(filename)
rescue Exception => e
@@ -225,14 +264,29 @@ class Chef
raise
end
+ def load_ohai_plugins_from_cookbook(cookbook_name)
+ target = Chef::Config[:ohai_segment_plugin_path]
+ files_in_cookbook_by_segment(cookbook_name, :ohai).each do |filename|
+ next unless File.extname(filename) == ".rb"
+
+ # FIXME(log): should be trace
+ logger.debug "Loading Ohai plugin: #{filename} from #{cookbook_name}"
+ target_name = File.join(target, cookbook_name.to_s, File.basename(filename))
+
+ FileUtils.mkdir_p(File.dirname(target_name))
+ FileUtils.cp(filename, target_name)
+ end
+ end
+
def load_resource_definitions_from_cookbook(cookbook_name)
files_in_cookbook_by_segment(cookbook_name, :definitions).each do |filename|
begin
- Chef::Log.debug("Loading cookbook #{cookbook_name}'s definitions from #{filename}")
+ # FIXME(log): should be trace
+ logger.debug("Loading cookbook #{cookbook_name}'s definitions from #{filename}")
resourcelist = Chef::ResourceDefinitionList.new
resourcelist.from_file(filename)
definitions.merge!(resourcelist.defines) do |key, oldval, newval|
- Chef::Log.info("Overriding duplicate definition #{key}, new definition found in #{filename}")
+ logger.info("Overriding duplicate definition #{key}, new definition found in #{filename}")
newval
end
@events.definition_file_loaded(filename)
@@ -258,16 +312,16 @@ class Chef
ordered_cookbooks << cookbook
end
- def count_files_by_segment(segment)
+ def count_files_by_segment(segment, root_alias = nil)
cookbook_collection.inject(0) do |count, cookbook_by_name|
- count + cookbook_by_name[1].segment_filenames(segment).size
+ count + cookbook_by_name[1].segment_filenames(segment).size + (root_alias ? cookbook_by_name[1].files_for(:root_files).select { |record| record[:name] == root_alias }.size : 0)
end
end
# Lists the local paths to files in +cookbook+ of type +segment+
# (attribute, recipe, etc.), sorted lexically.
def files_in_cookbook_by_segment(cookbook, segment)
- cookbook_collection[cookbook].segment_filenames(segment).sort
+ cookbook_collection[cookbook].files_for(segment).map { |record| record[:full_path] }.sort
end
# Yields the name, as a symbol, of each cookbook depended on by
diff --git a/lib/chef/run_list.rb b/lib/chef/run_list.rb
index 4dea938423..3ac5fab07b 100644
--- a/lib/chef/run_list.rb
+++ b/lib/chef/run_list.rb
@@ -47,13 +47,13 @@ class Chef
end
def role_names
- @run_list_items.inject([]) { |memo, run_list_item| memo << run_list_item.name if run_list_item.role? ; memo }
+ @run_list_items.inject([]) { |memo, run_list_item| memo << run_list_item.name if run_list_item.role?; memo }
end
alias :roles :role_names
def recipe_names
- @run_list_items.inject([]) { |memo, run_list_item| memo << run_list_item.name if run_list_item.recipe? ; memo }
+ @run_list_items.inject([]) { |memo, run_list_item| memo << run_list_item.name if run_list_item.recipe?; memo }
end
alias :recipes :recipe_names
diff --git a/lib/chef/run_list/run_list_item.rb b/lib/chef/run_list/run_list_item.rb
index 0abfb106ff..1c75907dd8 100644
--- a/lib/chef/run_list/run_list_item.rb
+++ b/lib/chef/run_list/run_list_item.rb
@@ -81,7 +81,7 @@ class Chef
def ==(other)
if other.kind_of?(String)
- self.to_s == other.to_s
+ to_s == other.to_s
else
other.respond_to?(:type) && other.respond_to?(:name) && other.respond_to?(:version) && other.type == @type && other.name == @name && other.version == @version
end
diff --git a/lib/chef/run_list/versioned_recipe_list.rb b/lib/chef/run_list/versioned_recipe_list.rb
index 7845568aaa..ad769ee952 100644
--- a/lib/chef/run_list/versioned_recipe_list.rb
+++ b/lib/chef/run_list/versioned_recipe_list.rb
@@ -1,7 +1,7 @@
#
# Author:: Stephen Delano (<stephen@chef.io>)
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software, Inc.
+# Copyright:: Copyright 2010-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -36,17 +36,17 @@ class Chef
end
end
@versions[name] = version if version
- self << name unless self.include?(name)
+ self << name unless include?(name)
end
def with_versions
- self.map { |recipe_name| { :name => recipe_name, :version => @versions[recipe_name] } }
+ map { |recipe_name| { :name => recipe_name, :version => @versions[recipe_name] } }
end
# Return an Array of Hashes, each of the form:
# {:name => RECIPE_NAME, :version_constraint => Chef::VersionConstraint }
def with_version_constraints
- self.map do |recipe_name|
+ map do |recipe_name|
constraint = Chef::VersionConstraint.new(@versions[recipe_name])
{ :name => recipe_name, :version_constraint => constraint }
end
@@ -55,7 +55,7 @@ class Chef
# Return an Array of Strings, each of the form:
# "NAME@VERSION"
def with_version_constraints_strings
- self.map do |recipe_name|
+ map do |recipe_name|
if @versions[recipe_name]
"#{recipe_name}@#{@versions[recipe_name]}"
else
@@ -69,7 +69,7 @@ class Chef
#
# @return [Array] Array of strings with fully-qualified recipe names
def with_fully_qualified_names_and_version_constraints
- self.map do |recipe_name|
+ map do |recipe_name|
qualified_recipe = if recipe_name.include?("::")
recipe_name
else
@@ -83,17 +83,19 @@ class Chef
end
end
- # Get an array of strings of both fully-qualified and unexpanded recipe names
- # in response to chef/chef#3767
- # Chef-13 will revert to the behaviour of just including the fully-qualified name
+ # For "foo::default" also include "foo", for "foo" also include "foo::default", for
+ # "foo::bar" just return "foo::bar". This makes it easier for people to search on
+ # default recipe names.
#
# @return [Array] Array of strings with fully-qualified and unexpanded recipe names
def with_duplicate_names
- self.map do |recipe_name|
- if recipe_name.include?("::")
+ map do |recipe_name|
+ if recipe_name.end_with?("::default")
+ [ recipe_name.sub(/::default$/, ""), recipe_name ]
+ elsif recipe_name.include?("::")
recipe_name
else
- [recipe_name, "#{recipe_name}::default"]
+ [ recipe_name, "#{recipe_name}::default" ]
end
end.flatten
end
diff --git a/lib/chef/runner.rb b/lib/chef/runner.rb
index cd5615bcec..1c82439b57 100644
--- a/lib/chef/runner.rb
+++ b/lib/chef/runner.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Tim Hinderliter (<tim@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,6 +34,7 @@ class Chef
def initialize(run_context)
@run_context = run_context
+ run_context.runner = self
end
def delayed_actions
diff --git a/lib/chef/scan_access_control.rb b/lib/chef/scan_access_control.rb
index f55a106e6d..0e6df30303 100644
--- a/lib/chef/scan_access_control.rb
+++ b/lib/chef/scan_access_control.rb
@@ -70,7 +70,7 @@ class Chef
when Integer
stat.uid
else
- Chef::Log.error("The `owner` parameter of the #@new_resource resource is set to an invalid value (#{new_resource.owner.inspect})")
+ Chef::Log.error("The `owner` parameter of the #{@new_resource} resource is set to an invalid value (#{new_resource.owner.inspect})")
raise ArgumentError, "cannot resolve #{new_resource.owner.inspect} to uid, owner must be a string or integer"
end
end
@@ -97,7 +97,7 @@ class Chef
when Integer
stat.gid
else
- Chef::Log.error("The `group` parameter of the #@new_resource resource is set to an invalid value (#{new_resource.owner.inspect})")
+ Chef::Log.error("The `group` parameter of the #{@new_resource} resource is set to an invalid value (#{new_resource.owner.inspect})")
raise ArgumentError, "cannot resolve #{new_resource.group.inspect} to gid, group must be a string or integer"
end
end
@@ -121,8 +121,8 @@ class Chef
when String, Integer, nil
"0#{(stat.mode & 07777).to_s(8)}"
else
- Chef::Log.error("The `mode` parameter of the #@new_resource resource is set to an invalid value (#{new_resource.mode.inspect})")
- raise ArgumentError, "Invalid value #{new_resource.mode.inspect} for `mode` on resource #@new_resource"
+ Chef::Log.error("The `mode` parameter of the #{@new_resource} resource is set to an invalid value (#{new_resource.mode.inspect})")
+ raise ArgumentError, "Invalid value #{new_resource.mode.inspect} for `mode` on resource #{@new_resource}"
end
end
diff --git a/lib/chef/search/query.rb b/lib/chef/search/query.rb
index ebf13bec36..b7b15765bb 100644
--- a/lib/chef/search/query.rb
+++ b/lib/chef/search/query.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,15 +21,15 @@ require "chef/exceptions"
require "chef/server_api"
require "uri"
+require "addressable/uri"
class Chef
class Search
class Query
- attr_accessor :rest
attr_reader :config
- def initialize(url = nil, config:Chef::Config)
+ def initialize(url = nil, config: Chef::Config)
@config = config
@url = url
end
@@ -38,28 +38,6 @@ class Chef
@rest ||= Chef::ServerAPI.new(@url || @config[:chef_server_url])
end
- # Backwards compatability for cookbooks.
- # This can be removed in Chef > 12.
- def partial_search(type, query = "*:*", *args, &block)
- Chef::Log.warn(<<-WARNDEP)
-DEPRECATED: The 'partial_search' API is deprecated and will be removed in
-future releases. Please use 'search' with a :filter_result argument to get
-partial search data.
-WARNDEP
-
- if !args.empty? && args.first.is_a?(Hash)
- # partial_search uses :keys instead of :filter_result for
- # result filtering.
- args_h = args.first.dup
- args_h[:filter_result] = args_h[:keys]
- args_h.delete(:keys)
-
- search(type, query, args_h, &block)
- else
- search(type, query, *args, &block)
- end
- end
-
#
# New search input, designed to be backwards compatible with the old method signature
# 'type' and 'query' are the same as before, args now will accept either a Hash of
@@ -84,6 +62,19 @@ WARNDEP
validate_type(type)
args_h = hashify_args(*args)
+ if args_h[:fuzz]
+ if type == :node
+ query = fuzzify_node_query(query)
+ end
+ # FIXME: can i haz proper ruby-2.x named parameters someday plz?
+ args_h = args_h.reject { |k, v| k == :fuzz }
+ end
+
+ # Set default rows parameter to 1000. This is the default in
+ # Chef Server, but we set it explicitly here so that we can
+ # confidently advance our start parameter.
+ args_h[:rows] ||= 1000
+
response = call_rest_service(type, query: query, **args_h)
if block
@@ -100,7 +91,7 @@ WARNDEP
# args_h[:rows] to avoid asking the search backend for
# overlapping pages (which could result in duplicates).
#
- next_start = response["start"] + (args_h[:rows] || response["rows"].length)
+ next_start = response["start"] + args_h[:rows]
unless next_start >= response["total"]
args_h[:start] = next_start
search(type, query, args_h, &block)
@@ -113,6 +104,14 @@ WARNDEP
private
+ def fuzzify_node_query(query)
+ if query !~ /:/
+ "tags:*#{query}* OR roles:*#{query}* OR fqdn:*#{query}* OR addresses:*#{query}* OR policy_name:*#{query}* OR policy_group:*#{query}*"
+ else
+ query
+ end
+ end
+
def validate_type(t)
unless t.kind_of?(String) || t.kind_of?(Symbol)
msg = "Invalid search object type #{t.inspect} (#{t.class}), must be a String or Symbol." +
@@ -127,27 +126,30 @@ WARNDEP
return args.first if args.first.is_a?(Hash)
args_h = Hash.new
- args_h[:sort] = args[0] if args[0]
- args_h[:start] = args[1] if args[1]
- args_h[:rows] = args[2]
- args_h[:filter_result] = args[3]
+ # If we have 4 arguments, the first is the now-removed sort option, so
+ # just ignore it.
+ args.pop(0) if args.length == 4
+ args_h[:start] = args[0] if args[0]
+ args_h[:rows] = args[1]
+ args_h[:filter_result] = args[2]
args_h
end
- def escape(s)
- s && URI.escape(s.to_s)
+ QUERY_PARAM_VALUE = Addressable::URI::CharacterClasses::QUERY + "\\&\\;"
+
+ def escape_value(s)
+ s && Addressable::URI.encode_component(s.to_s, QUERY_PARAM_VALUE)
end
- def create_query_string(type, query, rows, start, sort)
- qstr = "search/#{type}?q=#{escape(query)}"
- qstr += "&sort=#{escape(sort)}" if sort
- qstr += "&start=#{escape(start)}" if start
- qstr += "&rows=#{escape(rows)}" if rows
+ def create_query_string(type, query, rows, start)
+ qstr = "search/#{type}?q=#{escape_value(query)}"
+ qstr += "&start=#{escape_value(start)}" if start
+ qstr += "&rows=#{escape_value(rows)}" if rows
qstr
end
- def call_rest_service(type, query:"*:*", rows:nil, start:0, sort:"X_CHEF_id_CHEF_X asc", filter_result:nil)
- query_string = create_query_string(type, query, rows, start, sort)
+ def call_rest_service(type, query: "*:*", rows: nil, start: 0, filter_result: nil)
+ query_string = create_query_string(type, query, rows, start)
if filter_result
response = rest.post(query_string, filter_result)
diff --git a/lib/chef/server_api.rb b/lib/chef/server_api.rb
index cad8586ac8..c501544954 100644
--- a/lib/chef/server_api.rb
+++ b/lib/chef/server_api.rb
@@ -24,13 +24,15 @@ require "chef/http/json_input"
require "chef/http/json_output"
require "chef/http/remote_request_id"
require "chef/http/validate_content_length"
+require "chef/http/api_versions"
class Chef
class ServerAPI < Chef::HTTP
def initialize(url = Chef::Config[:chef_server_url], options = {})
options[:client_name] ||= Chef::Config[:node_name]
- options[:signing_key_filename] ||= Chef::Config[:client_key]
+ options[:raw_key] ||= Chef::Config[:client_key_contents]
+ options[:signing_key_filename] ||= Chef::Config[:client_key] unless options[:raw_key]
options[:signing_key_filename] = nil if chef_zero_uri?(url)
options[:inflate_json_class] = false
super(url, options)
@@ -42,6 +44,7 @@ class Chef
use Chef::HTTP::Decompressor
use Chef::HTTP::Authenticator
use Chef::HTTP::RemoteRequestID
+ use Chef::HTTP::APIVersions
# ValidateContentLength should come after Decompressor
# because the order of middlewares is reversed when handling
diff --git a/lib/chef/server_api_versions.rb b/lib/chef/server_api_versions.rb
new file mode 100644
index 0000000000..40fb6385e1
--- /dev/null
+++ b/lib/chef/server_api_versions.rb
@@ -0,0 +1,59 @@
+#--
+# Copyright:: Copyright 2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "singleton"
+
+class Chef
+ class ServerAPIVersions
+ include Singleton
+
+ def set_versions(versions)
+ @versions ||= versions
+ end
+
+ def min_server_version
+ # If we're working with a pre-api-versioning server, always claim to be zero
+ if @versions.nil?
+ unversioned? ? 0 : nil
+ else
+ Integer(@versions["min_version"])
+ end
+ end
+
+ def max_server_version
+ # If we're working with a pre-api-versioning server, always claim to be zero
+ if @versions.nil?
+ unversioned? ? 0 : nil
+ else
+ Integer(@versions["max_version"])
+ end
+ end
+
+ def unversioned!
+ @unversioned = true
+ end
+
+ def unversioned?
+ @unversioned
+ end
+
+ def reset!
+ @versions = nil
+ @unversioned = false
+ end
+ end
+end
diff --git a/lib/chef/shell.rb b/lib/chef/shell.rb
index aad5c49d00..88d26e78fc 100644
--- a/lib/chef/shell.rb
+++ b/lib/chef/shell.rb
@@ -27,6 +27,7 @@ require "chef/config"
require "chef/config_fetcher"
require "chef/shell/shell_session"
+require "chef/workstation_config_loader"
require "chef/shell/ext"
require "chef/json_compat"
require "chef/util/path_helper"
@@ -40,7 +41,6 @@ module Shell
LEADERS[Chef::Node] = ":attributes"
class << self
- attr_accessor :client_type
attr_accessor :options
attr_accessor :env
attr_writer :editor
@@ -62,6 +62,12 @@ module Shell
irb = IRB::Irb.new
+ if solo_mode?
+ # Setup the mocked ChefServer
+ Chef::Config.local_mode = true
+ Chef::LocalMode.setup_server_connectivity
+ end
+
init(irb.context.main)
irb_conf[:IRB_RC].call(irb.context) if irb_conf[:IRB_RC]
@@ -74,6 +80,13 @@ module Shell
catch(:IRB_EXIT) do
irb.eval_input
end
+ ensure
+ # We destroy the mocked ChefServer
+ Chef::LocalMode.destroy_server_connectivity if solo_mode?
+ end
+
+ def self.solo_mode?
+ Chef::Config[:solo]
end
def self.setup_logger
@@ -124,6 +137,7 @@ module Shell
def self.session
unless client_type.instance.node_built?
puts "Session type: #{client_type.session_type}"
+ client_type.instance.json_configuration = @json_attribs
client_type.instance.reset!
end
client_type.instance
@@ -148,7 +162,7 @@ module Shell
end
def self.greeting
- " #{Etc.getlogin}@#{Shell.session.node.fqdn}"
+ " #{Etc.getlogin}@#{Shell.session.node["fqdn"]}"
rescue NameError, ArgumentError
""
end
@@ -167,8 +181,9 @@ module Shell
def self.client_type
type = Shell::StandAloneSession
- type = Shell::SoloSession if Chef::Config[:shell_solo]
- type = Shell::ClientSession if Chef::Config[:client]
+ type = Shell::SoloSession if solo_mode?
+ type = Shell::SoloLegacySession if Chef::Config[:solo_legacy_shell]
+ type = Shell::ClientSession if Chef::Config[:client]
type = Shell::DoppelGangerSession if Chef::Config[:doppelganger]
type
end
@@ -196,8 +211,10 @@ module Shell
When no CONFIG is specified, chef-shell attempts to load a default configuration file:
* If a NAMED_CONF is given, chef-shell will load ~/.chef/NAMED_CONF/chef_shell.rb
* If no NAMED_CONF is given chef-shell will load ~/.chef/chef_shell.rb if it exists
-* chef-shell falls back to loading /etc/chef/client.rb or /etc/chef/solo.rb if -z or
- -s options are given and no chef_shell.rb can be found.
+* If no chef_shell.rb can be found, chef-shell falls back to load:
+ /etc/chef/client.rb if -z option is given.
+ /etc/chef/solo.rb if --solo-legacy-mode option is given.
+ .chef/knife.rb if -s option is given.
FOOTER
option :config_file,
@@ -226,7 +243,7 @@ FOOTER
:default => true,
:boolean => true
- option :shell_solo,
+ option :solo_shell,
:short => "-s",
:long => "--solo",
:description => "chef-solo session",
@@ -239,6 +256,12 @@ FOOTER
:description => "chef-client session",
:boolean => true
+ option :solo_legacy_shell,
+ :long => "--solo-legacy-mode",
+ :description => "chef-solo legacy session",
+ :boolean => true,
+ :proc => proc { Chef::Config[:solo_legacy_mode] = true }
+
option :json_attribs,
:short => "-j JSON_ATTRIBS",
:long => "--json-attributes JSON_ATTRIBS",
@@ -281,7 +304,7 @@ FOOTER
end
def self.setup!
- self.new.parse_opts
+ new.parse_opts
end
def parse_opts
@@ -313,10 +336,12 @@ FOOTER
config_file_to_try
elsif dot_chef_dir && ::File.exist?(File.join(dot_chef_dir, "chef_shell.rb"))
File.join(dot_chef_dir, "chef_shell.rb")
- elsif config[:solo]
+ elsif config[:solo_legacy_shell]
Chef::Config.platform_specific_path("/etc/chef/solo.rb")
elsif config[:client]
Chef::Config.platform_specific_path("/etc/chef/client.rb")
+ elsif config[:solo_shell]
+ Chef::WorkstationConfigLoader.new(nil, Chef::Log).config_location
else
nil
end
diff --git a/lib/chef/shell/ext.rb b/lib/chef/shell/ext.rb
index 40d7e10d2e..fb0733e96e 100644
--- a/lib/chef/shell/ext.rb
+++ b/lib/chef/shell/ext.rb
@@ -39,13 +39,13 @@ module Shell
# irb breaks if you prematurely define IRB::JobMangager
# so these methods need to be defined at the latest possible time.
unless jobs.respond_to?(:select_session_by_context)
- def jobs.select_session_by_context(&block)
+ def jobs.select_session_by_context(&block) # rubocop:disable Lint/NestedMethodDefinition
@jobs.select { |job| block.call(job[1].context.main) }
end
end
unless jobs.respond_to?(:session_select)
- def jobs.select_shell_session(target_context)
+ def jobs.select_shell_session(target_context) # rubocop:disable Lint/NestedMethodDefinition
session = if target_context.kind_of?(Class)
select_session_by_context { |main| main.kind_of?(target_context) }
else
@@ -73,7 +73,7 @@ module Shell
banner << "| " + "Command".ljust(25) + "| " + "Description"
banner << "".ljust(80, "=")
- self.all_help_descriptions.each do |help_text|
+ all_help_descriptions.each do |help_text|
banner << "| " + help_text.cmd.ljust(25) + "| " + help_text.desc
end
banner << "".ljust(80, "=")
@@ -84,7 +84,7 @@ module Shell
end
def explain_command(method_name)
- help = self.all_help_descriptions.find { |h| h.cmd.to_s == method_name.to_s }
+ help = all_help_descriptions.find { |h| h.cmd.to_s == method_name.to_s }
if help
puts ""
puts "Command: #{method_name}"
@@ -159,7 +159,7 @@ module Shell
module Symbol
def on_off_to_bool
- self.to_s.on_off_to_bool
+ to_s.on_off_to_bool
end
end
@@ -211,8 +211,8 @@ module Shell
def version
puts "This is the chef-shell.\n" +
" Chef Version: #{::Chef::VERSION}\n" +
- " http://www.chef.io/\n" +
- " http://docs.chef.io/"
+ " https://www.chef.io/\n" +
+ " https://docs.chef.io/"
:ucanhaz_automation
end
alias :shell :version
diff --git a/lib/chef/shell/model_wrapper.rb b/lib/chef/shell/model_wrapper.rb
index 8c3e456a9b..4394caa395 100644
--- a/lib/chef/shell/model_wrapper.rb
+++ b/lib/chef/shell/model_wrapper.rb
@@ -17,7 +17,7 @@
#
require "chef/mixin/convert_to_class_name"
-require "chef/mixin/language"
+require "chef/dsl/data_query"
module Shell
class ModelWrapper
diff --git a/lib/chef/shell/shell_session.rb b/lib/chef/shell/shell_session.rb
index 6d8b6f14ae..aec2152063 100644
--- a/lib/chef/shell/shell_session.rb
+++ b/lib/chef/shell/shell_session.rb
@@ -2,7 +2,7 @@
# Author:: Daniel DeLeo (<dan@kallistec.com>)
# Author:: Tim Hinderliter (<tim@chef.io>)
# Copyright:: Copyright 2009-2016, Daniel DeLeo
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright 2011-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -38,7 +38,7 @@ module Shell
@session_type
end
- attr_accessor :node, :compile, :recipe, :run_context
+ attr_accessor :node, :compile, :recipe, :json_configuration
attr_reader :node_attributes, :client
def initialize
@node_built = false
@@ -73,6 +73,7 @@ module Shell
run_context.resource_collection
end
+ attr_writer :run_context
def run_context
@run_context ||= rebuild_context
end
@@ -126,8 +127,8 @@ module Shell
end
def shorten_node_inspect
- def @node.inspect
- "<Chef::Node:0x#{self.object_id.to_s(16)} @name=\"#{self.name}\">"
+ def @node.inspect # rubocop:disable Lint/NestedMethodDefinition
+ "<Chef::Node:0x#{object_id.to_s(16)} @name=\"#{name}\">"
end
end
@@ -150,8 +151,8 @@ module Shell
private
def rebuild_node
- Chef::Config[:solo] = true
- @client = Chef::Client.new(nil, Chef::Config[:shell_config])
+ Chef::Config[:solo_legacy_mode] = true
+ @client = Chef::Client.new(json_configuration, Chef::Config[:shell_config])
@client.run_ohai
@client.load_node
@client.build_node
@@ -159,9 +160,9 @@ module Shell
end
- class SoloSession < ShellSession
+ class SoloLegacySession < ShellSession
- session_type :solo
+ session_type :solo_legacy_mode
def definitions
@run_context.definitions
@@ -182,8 +183,8 @@ module Shell
def rebuild_node
# Tell the client we're chef solo so it won't try to contact the server
- Chef::Config[:solo] = true
- @client = Chef::Client.new(nil, Chef::Config[:shell_config])
+ Chef::Config[:solo_legacy_mode] = true
+ @client = Chef::Client.new(json_configuration, Chef::Config[:shell_config])
@client.run_ohai
@client.load_node
@client.build_node
@@ -191,10 +192,14 @@ module Shell
end
- class ClientSession < SoloSession
+ class ClientSession < ShellSession
session_type :client
+ def definitions
+ @run_context.definitions
+ end
+
def save_node
@client.save_node
end
@@ -213,8 +218,8 @@ module Shell
def rebuild_node
# Make sure the client knows this is not chef solo
- Chef::Config[:solo] = false
- @client = Chef::Client.new(nil, Chef::Config[:shell_config])
+ Chef::Config[:solo_legacy_mode] = false
+ @client = Chef::Client.new(json_configuration, Chef::Config[:shell_config])
@client.run_ohai
@client.register
@client.load_node
@@ -223,6 +228,12 @@ module Shell
end
+ class SoloSession < ClientSession
+
+ session_type :solo
+
+ end
+
class DoppelGangerClient < Chef::Client
attr_reader :node_name
@@ -241,7 +252,7 @@ module Shell
# DoppelGanger implementation of build_node. preserves as many of the node's
# attributes, and does not save updates to the server
def build_node
- Chef::Log.debug("Building node object for #{@node_name}")
+ Chef::Log.trace("Building node object for #{@node_name}")
@node = Chef::Node.find_or_create(node_name)
ohai_data = @ohai.data.merge(@node.automatic_attrs)
@node.consume_external_attrs(ohai_data, nil)
diff --git a/lib/chef/shell_out.rb b/lib/chef/shell_out.rb
deleted file mode 100644
index 54ff718e8e..0000000000
--- a/lib/chef/shell_out.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require "mixlib/shellout"
-
-class Chef
- class ShellOut < Mixlib::ShellOut
-
- def initialize(*args)
- Chef::Log.warn("Chef::ShellOut is deprecated, please use Mixlib::ShellOut")
- called_from = caller[0..3].inject("Called from:\n") { |msg, trace_line| msg << " #{trace_line}\n" }
- Chef::Log.warn(called_from)
- super
- end
- end
-end
diff --git a/lib/chef/tasks/chef_repo.rake b/lib/chef/tasks/chef_repo.rake
deleted file mode 100644
index 543bd8d864..0000000000
--- a/lib/chef/tasks/chef_repo.rake
+++ /dev/null
@@ -1,200 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-TOPDIR = "."
-require "rake"
-
-desc "By default, print deprecation notice"
-task :default do
- puts deprecation_notice
-end
-
-desc "Install the latest copy of the repository on this Chef Server"
-task :install do
- puts deprecation_notice
- puts "The `install` rake task, which included the `update`, `roles`, and"
- puts "`upload_cookbooks` rake tasks is replaced by the `knife upload`"
- puts 'sub-command. The notion of "installing" the chef-repo to the Chef'
- puts "Server. Previously the `install` task would manage server and"
- puts "client configuration. This will not work at all on Chef Server 11+"
- puts "and client configuration should be managed with the `chef-client`"
- puts "cookbook."
-end
-
-desc "Update your repository from source control"
-task :update do
- puts deprecation_notice
- puts "The `update` rake task previously updated the chef-repo from"
- puts "the detected version control system, either svn or git. However,"
- puts "it has not been recommended for users for years. Most users in"
- puts "the community use `git`, so the Subversion functionality is not"
- puts "required, and `git pull` is sufficient for many workflows. The"
- puts "world of git workflows is rather different now than it was when"
- puts "this rake task was created."
-end
-
-desc "Create a new cookbook (with COOKBOOK=name, optional CB_PREFIX=site-)"
-task :new_cookbook do
- cb = ENV["COOKBOOK"] || "my_cookbook_name"
- puts deprecation_notice
- puts "The `new_cookbook` rake task is replaced by the ChefDK cookbook"
- puts "generator. To generate a new cookbook run:"
- puts
- puts "chef generate cookbook #{ENV['COOKBOOK']}"
- puts
- puts "Or, if you are not using ChefDK, use `knife cookbook create`:"
- puts
- puts "knife cookbook create #{ENV['COOKBOOK']}"
-end
-
-desc "Create a new self-signed SSL certificate for FQDN=foo.example.com"
-task :ssl_cert do
- puts deprecation_notice
- puts "The `ssl_cert` rake task is superseded by using the CHEF-maintained"
- puts '`openssl` cookbook\'s `openssl_x509` resource which can generate'
- puts "self-signed certificate chains as convergent resources."
- puts
- puts "https://supermarket.getchef.com/cookbooks/openssl"
-end
-
-desc "Build cookbook metadata.json from metadata.rb"
-task :metadata do
- puts deprecation_notice
- puts "The `metadata` rake task is not recommended. Cookbook"
- puts "`metadata.json` is automatically generated from `metadata.rb`"
- puts "by `knife` when uploading cookbooks to the Chef Server."
-end
-
-desc "Update roles"
-task :roles do
- puts deprecation_notice
- puts "The `roles` rake task is not recommended. If you are using Ruby"
- puts "role files (roles/*.rb), you can upload them all with:"
- puts
- puts "knife role from file roles/*"
- puts
- puts "If you are using JSON role files (roles/*.json), you can upload"
- puts "them all with:"
- puts
- puts "knife upload roles/*.json"
-end
-
-desc "Update a specific role"
-task :role do
- puts deprecation_notice
- puts "The `role` rake task is not recommended. If you are using Ruby"
- puts "role files, you can upload a single role with:"
- puts
- puts "knife role from file rolename.rb"
- puts
- puts "If you are using JSON role files, you can upload a single role with"
- puts
- puts "knife upload roles/rolename.json"
-end
-
-desc "Upload all cookbooks"
-task :upload_cookbooks do
- puts deprecation_notice
- puts deprecated_cookbook_upload
-end
-
-desc "Upload a single cookbook"
-task :upload_cookbook do
- puts deprecation_notice
- puts deprecated_cookbook_upload
-end
-
-desc "Test all cookbooks"
-task :test_cookbooks do
- puts deprecation_notice
- puts "The `test_cookbooks` rake task is no longer recommended. Previously"
- puts "it only performed a syntax check, and did no other kind of testing,"
- puts "and the Chef Community has a rich ecosystem of testing tools for"
- puts "various purposes:"
- puts
- puts "- knife cookbook test will perform a syntax check, as this task did"
- puts " before."
- puts "- rubocop and foodcritic will perform lint checking for Ruby and"
- puts " Chef cookbook style according to community standards."
- puts "- ChefSpec will perform unit testing"
- puts "- Test Kitchen will perform convergence and post-convergence"
- puts " testing on virtual machines."
-end
-
-desc "Test a single cookbook"
-task :test_cookbook => [:test_cookbooks]
-
-namespace :databag do
- desc "Upload a single databag"
- task :upload do
- puts deprecation_notice
- puts "The `data_bags:upload` task is not recommended. You should use"
- puts "the `knife upload` sub-command for uploading data bag items."
- puts
- puts "knife upload data_bags/bagname/itemname.json"
- end
-
- desc "Upload all databags"
- task :upload_all do
- puts deprecation_notice
- puts "The `data_bags:upload_all` task is not recommended. You should"
- puts "use the `knife upload` sub-command for uploading data bag items."
- puts
- puts "knife upload data_bags/*"
- end
-
- desc "Create a databag"
- task :create do
- puts deprecation_notice
- puts deprecated_data_bag_creation
- end
-
- desc "Create a databag item stub"
- task :create_item do
- puts deprecation_notice
- puts deprecated_data_bag_creation
- end
-end
-
-def deprecation_notice
- %Q{*************************************************
-NOTICE: Chef Repository Rake Tasks Are Deprecated
-*************************************************
-}
-end
-
-def deprecated_cookbook_upload
- %Q{
-The `upload_cookbook` and `upload_cookbooks` rake tasks are not
-recommended. These tasks are replaced by other, better workflow
-tools, such as `knife cookbook upload`, `knife upload`, or `berks`
-}
-end
-
-def deprecated_data_bag_creation
- %Q{
-The `data_bags:create` and `data_bags:create_item` tasks are not
-recommended. You should create data bag items as JSON files in the data_bags
-directory, with a sub-directory for each bag, and use `knife upload` to
-upload them. For example, if you have a data bags named `users`, with
-`finn`, and `jake` items, you would have:
-
-./data_bags/users/finn.json
-./data-bags/users/jake.json
-}
-end
diff --git a/lib/chef/user.rb b/lib/chef/user.rb
index a6fc21646d..f52d0e2555 100644
--- a/lib/chef/user.rb
+++ b/lib/chef/user.rb
@@ -26,13 +26,13 @@ require "chef/server_api"
# TODO
# DEPRECATION NOTE
# This class will be replaced by Chef::UserV1 in Chef 13. It is the code to support the User object
-# corrosponding to the Open Source Chef Server 11 and only still exists to support
+# corresponding to the Open Source Chef Server 11 and only still exists to support
# users still on OSC 11.
#
# Chef::UserV1 now supports Chef Server 12 and will be moved to this namespace in Chef 13.
#
# New development should occur in Chef::UserV1.
-# This file and corrosponding osc_user knife files
+# This file and corresponding osc_user knife files
# should be removed once client support for Open Source Chef Server 11 expires.
class Chef
class User
@@ -97,10 +97,10 @@ class Chef
end
def create
- payload = { :name => self.name, :admin => self.admin, :password => self.password }
+ payload = { :name => name, :admin => admin, :password => password }
payload[:public_key] = public_key if public_key
new_user = chef_rest_v0.post("users", payload)
- Chef::User.from_hash(self.to_hash.merge(new_user))
+ Chef::User.from_hash(to_hash.merge(new_user))
end
def update(new_key = false)
@@ -108,18 +108,16 @@ class Chef
payload[:private_key] = new_key if new_key
payload[:password] = password if password
updated_user = chef_rest_v0.put("users/#{name}", payload)
- Chef::User.from_hash(self.to_hash.merge(updated_user))
+ Chef::User.from_hash(to_hash.merge(updated_user))
end
def save(new_key = false)
- begin
- create
- rescue Net::HTTPServerException => e
- if e.response.code == "409"
- update(new_key)
- else
- raise e
- end
+ create
+ rescue Net::HTTPServerException => e
+ if e.response.code == "409"
+ update(new_key)
+ else
+ raise e
end
end
@@ -154,11 +152,6 @@ class Chef
Chef::User.from_hash(Chef::JSONCompat.from_json(json))
end
- def self.json_create(json)
- Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please use Chef::User#from_json or Chef::User#load.")
- Chef::User.from_json(json)
- end
-
def self.list(inflate = false)
response = Chef::ServerAPI.new(Chef::Config[:chef_server_url], { :api_version => "0" }).get("users")
users = if response.is_a?(Array)
diff --git a/lib/chef/user_v1.rb b/lib/chef/user_v1.rb
index db44ced9d4..0ca17c6ed6 100644
--- a/lib/chef/user_v1.rb
+++ b/lib/chef/user_v1.rb
@@ -180,7 +180,7 @@ class Chef
new_user = chef_root_rest_v0.post("users", payload)
end
- Chef::UserV1.from_hash(self.to_hash.merge(new_user))
+ Chef::UserV1.from_hash(to_hash.merge(new_user))
end
def update(new_key = false)
@@ -213,25 +213,23 @@ class Chef
end
updated_user = chef_root_rest_v0.put("users/#{username}", payload)
end
- Chef::UserV1.from_hash(self.to_hash.merge(updated_user))
+ Chef::UserV1.from_hash(to_hash.merge(updated_user))
end
def save(new_key = false)
- begin
- create
- rescue Net::HTTPServerException => e
- if e.response.code == "409"
- update(new_key)
- else
- raise e
- end
+ create
+ rescue Net::HTTPServerException => e
+ if e.response.code == "409"
+ update(new_key)
+ else
+ raise e
end
end
# Note: remove after API v0 no longer supported by client (and knife command).
def reregister
begin
- payload = self.to_hash.merge({ "private_key" => true })
+ payload = to_hash.merge({ "private_key" => true })
reregistered_self = chef_root_rest_v0.put("users/#{username}", payload)
private_key(reregistered_self["private_key"])
# only V0 supported for reregister
@@ -276,11 +274,6 @@ class Chef
Chef::UserV1.from_hash(Chef::JSONCompat.from_json(json))
end
- def self.json_create(json)
- Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please use Chef::UserV1#from_json or Chef::UserV1#load.")
- Chef::UserV1.from_json(json)
- end
-
def self.list(inflate = false)
response = Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("users")
users = if response.is_a?(Array)
diff --git a/lib/chef/util/diff.rb b/lib/chef/util/diff.rb
index bb1b4e2b95..b5c85df56d 100644
--- a/lib/chef/util/diff.rb
+++ b/lib/chef/util/diff.rb
@@ -64,7 +64,7 @@ class Chef
def use_tempfile_if_missing(file)
tempfile = nil
unless File.exists?(file)
- Chef::Log.debug("File #{file} does not exist to diff against, using empty tempfile")
+ Chef::Log.trace("File #{file} does not exist to diff against, using empty tempfile")
tempfile = Tempfile.new("chef-diff")
file = tempfile.path
end
@@ -117,7 +117,7 @@ class Chef
end
end
diff_str << old_hunk.diff(:unified) << "\n"
- return diff_str
+ diff_str
end
private
@@ -139,7 +139,7 @@ class Chef
return "(new content is binary, diff output suppressed)" if is_binary?(new_file)
begin
- Chef::Log.debug("Running: diff -u #{old_file} #{new_file}")
+ Chef::Log.trace("Running: diff -u #{old_file} #{new_file}")
diff_str = udiff(old_file, new_file)
rescue Exception => e
diff --git a/lib/chef/util/dsc/configuration_generator.rb b/lib/chef/util/dsc/configuration_generator.rb
index 739a463ad5..3e9475bac2 100644
--- a/lib/chef/util/dsc/configuration_generator.rb
+++ b/lib/chef/util/dsc/configuration_generator.rb
@@ -26,7 +26,7 @@ class Chef::Util::DSC
end
def configuration_document_from_script_code(code, configuration_flags, imports, shellout_flags)
- Chef::Log.debug("DSC: DSC code:\n '#{code}'")
+ Chef::Log.trace("DSC: DSC code:\n '#{code}'")
generated_script_path = write_document_generation_script(code, "chef_dsc", imports)
begin
configuration_document_from_script_path(generated_script_path, "chef_dsc", configuration_flags, shellout_flags)
@@ -48,7 +48,7 @@ class Chef::Util::DSC
configuration_document_location = find_configuration_document(configuration_name)
if ! configuration_document_location
- raise RuntimeError, "No DSC configuration for '#{configuration_name}' was generated from supplied DSC script"
+ raise "No DSC configuration for '#{configuration_name}' was generated from supplied DSC script"
end
configuration_document = get_configuration_document(configuration_document_location)
diff --git a/lib/chef/util/dsc/lcm_output_parser.rb b/lib/chef/util/dsc/lcm_output_parser.rb
index bdcedff7f8..658d5c7d8f 100644
--- a/lib/chef/util/dsc/lcm_output_parser.rb
+++ b/lib/chef/util/dsc/lcm_output_parser.rb
@@ -28,7 +28,7 @@ class Chef
# Parses the output from LCM and returns a list of Chef::Util::DSC::ResourceInfo objects
# that describe how the resources affected the system
#
- # Example:
+ # Example for WhatIfParser:
# parse <<-EOF
# What if: [Machine]: LCM: [Start Set ]
# What if: [Machine]: LCM: [Start Resource ] [[File]FileToNotBeThere]
@@ -53,7 +53,62 @@ class Chef
# )
# ]
#
- def self.parse(lcm_output)
+ # Example for TestDSCParser:
+ # parse <<-EOF
+ # InDesiredState : False
+ # ResourcesInDesiredState :
+ # ResourcesNotInDesiredState: {[Environment]texteditor}
+ # ReturnValue : 0
+ # PSComputerName : .
+ # EOF
+ #
+ # would return
+ #
+ # [
+ # Chef::Util::DSC::ResourceInfo.new(
+ # '{[Environment]texteditor}',
+ # true,
+ # [
+ # ]
+ # )
+ # ]
+ #
+
+ def self.parse(lcm_output, test_dsc_configuration)
+ test_dsc_configuration ? test_dsc_parser(lcm_output) : what_if_parser(lcm_output)
+ end
+
+ def self.test_dsc_parser(lcm_output)
+ lcm_output ||= ""
+ current_resource = Hash.new
+
+ resources = []
+ lcm_output.lines.each do |line|
+ op_action , op_value = line.strip.split(":")
+ op_action&.strip!
+ case op_action
+ when "InDesiredState"
+ current_resource[:skipped] = op_value.strip == "True" ? true : false
+ when "ResourcesInDesiredState"
+ current_resource[:name] = op_value.strip if op_value
+ when "ResourcesNotInDesiredState"
+ current_resource[:name] = op_value.strip if op_value
+ when "ReturnValue"
+ current_resource[:context] = nil
+ end
+ end
+ if current_resource[:name]
+ resources.push(current_resource)
+ end
+
+ if resources.length > 0
+ build_resource_info(resources)
+ else
+ raise Chef::Exceptions::LCMParser, "Could not parse:\n#{lcm_output}"
+ end
+ end
+
+ def self.what_if_parser(lcm_output)
lcm_output ||= ""
current_resource = Hash.new
@@ -75,7 +130,7 @@ class Chef
end
current_resource = { :name => info }
else
- Chef::Log.debug("Ignoring op_action #{op_action}: Read line #{line}")
+ Chef::Log.trace("Ignoring op_action #{op_action}: Read line #{line}")
end
when :end
# Make sure we log the last line
@@ -119,7 +174,7 @@ class Chef
end
end
info.strip! # Because this was formatted for humans
- return [op_action, op_type, info]
+ [op_action, op_type, info]
end
private_class_method :parse_line
diff --git a/lib/chef/util/dsc/local_configuration_manager.rb b/lib/chef/util/dsc/local_configuration_manager.rb
index 741c6a5898..81aaa098c0 100644
--- a/lib/chef/util/dsc/local_configuration_manager.rb
+++ b/lib/chef/util/dsc/local_configuration_manager.rb
@@ -29,7 +29,7 @@ class Chef::Util::DSC
def test_configuration(configuration_document, shellout_flags)
status = run_configuration_cmdlet(configuration_document, false, shellout_flags)
- log_what_if_exception(status.stderr) unless status.succeeded?
+ log_dsc_exception(status.stderr) unless status.succeeded?
configuration_update_required?(status.return_value)
end
@@ -46,16 +46,14 @@ class Chef::Util::DSC
private
def run_configuration_cmdlet(configuration_document, apply_configuration, shellout_flags)
- Chef::Log.debug("DSC: Calling DSC Local Config Manager to #{apply_configuration ? "set" : "test"} configuration document.")
- test_only_parameters = ! apply_configuration ? "-whatif; if (! $?) { exit 1 }" : ""
+ Chef::Log.trace("DSC: Calling DSC Local Config Manager to #{apply_configuration ? "set" : "test"} configuration document.")
start_operation_timing
- command_code = lcm_command_code(@configuration_path, test_only_parameters)
status = nil
begin
save_configuration_document(configuration_document)
- cmdlet = ::Chef::Util::Powershell::Cmdlet.new(@node, "#{command_code}")
+ cmdlet = ::Chef::Util::Powershell::Cmdlet.new(@node, lcm_command(apply_configuration))
if apply_configuration
status = cmdlet.run!({}, shellout_flags)
else
@@ -65,44 +63,56 @@ class Chef::Util::DSC
end_operation_timing
remove_configuration_document
if last_operation_execution_time_seconds
- Chef::Log.debug("DSC: DSC operation completed in #{last_operation_execution_time_seconds} seconds.")
+ Chef::Log.trace("DSC: DSC operation completed in #{last_operation_execution_time_seconds} seconds.")
end
end
- Chef::Log.debug("DSC: Completed call to DSC Local Config Manager")
+ Chef::Log.trace("DSC: Completed call to DSC Local Config Manager")
status
end
- def lcm_command_code(configuration_path, test_only_parameters)
- <<-EOH
-$ProgressPreference = 'SilentlyContinue';start-dscconfiguration -path #{@configuration_path} -wait -erroraction 'continue' -force #{test_only_parameters}
-EOH
+ def lcm_command(apply_configuration)
+ common_command_prefix = "$ProgressPreference = 'SilentlyContinue';"
+ ps4_base_command = "#{common_command_prefix} Start-DscConfiguration -path #{@configuration_path} -wait -erroraction 'stop' -force"
+ if apply_configuration
+ ps4_base_command
+ else
+ if ps_version_gte_5?
+ "#{common_command_prefix} Test-DscConfiguration -path #{@configuration_path} | format-list"
+ else
+ ps4_base_command + " -whatif; if (! $?) { exit 1 }"
+ end
+ end
+ end
+
+ def ps_version_gte_5?
+ Chef::Platform.supported_powershell_version?(@node, 5)
end
- def log_what_if_exception(what_if_exception_output)
- if whatif_not_supported?(what_if_exception_output)
+ def log_dsc_exception(dsc_exception_output)
+ if whatif_not_supported?(dsc_exception_output)
# LCM returns an error if any of the resources do not support the opptional What-If
Chef::Log.warn("Received error while testing configuration due to resource not supporting 'WhatIf'")
- elsif dsc_module_import_failure?(what_if_exception_output)
- Chef::Log.warn("Received error while testing configuration due to a module for an imported resource possibly not being fully installed:\n#{what_if_exception_output.gsub(/\s+/, ' ')}")
+ elsif dsc_module_import_failure?(dsc_exception_output)
+ Chef::Log.warn("Received error while testing configuration due to a module for an imported resource possibly not being fully installed:\n#{dsc_exception_output.gsub(/\s+/, ' ')}")
else
- Chef::Log.warn("Received error while testing configuration:\n#{what_if_exception_output.gsub(/\s+/, ' ')}")
+ Chef::Log.warn("Received error while testing configuration:\n#{dsc_exception_output.gsub(/\s+/, ' ')}")
end
end
- def whatif_not_supported?(what_if_exception_output)
- !! (what_if_exception_output.gsub(/[\r\n]+/, "").gsub(/\s+/, " ") =~ /A parameter cannot be found that matches parameter name 'Whatif'/i)
+ def whatif_not_supported?(dsc_exception_output)
+ !! (dsc_exception_output.gsub(/[\r\n]+/, "").gsub(/\s+/, " ") =~ /A parameter cannot be found that matches parameter name 'Whatif'/i)
end
- def dsc_module_import_failure?(what_if_output)
- !! (what_if_output =~ /\sCimException/ &&
- what_if_output =~ /ProviderOperationExecutionFailure/ &&
- what_if_output =~ /\smodule\s+is\s+installed/)
+ def dsc_module_import_failure?(command_output)
+ !! (command_output =~ /\sCimException/ &&
+ command_output =~ /ProviderOperationExecutionFailure/ &&
+ command_output =~ /\smodule\s+is\s+installed/)
end
- def configuration_update_required?(what_if_output)
- Chef::Log.debug("DSC: DSC returned the following '-whatif' output from test operation:\n#{what_if_output}")
+ def configuration_update_required?(command_output)
+ Chef::Log.trace("DSC: DSC returned the following '-whatif' output from test operation:\n#{command_output}")
begin
- Parser.parse(what_if_output)
+ Parser.parse(command_output, ps_version_gte_5?)
rescue Chef::Exceptions::LCMParser => e
Chef::Log.warn("Could not parse LCM output: #{e}")
[Chef::Util::DSC::ResourceInfo.new("Unknown DSC Resources", true, ["Unknown changes because LCM output was not parsable."])]
diff --git a/lib/chef/util/dsc/resource_store.rb b/lib/chef/util/dsc/resource_store.rb
index be8d0b301b..43b7d1bdf9 100644
--- a/lib/chef/util/dsc/resource_store.rb
+++ b/lib/chef/util/dsc/resource_store.rb
@@ -74,7 +74,7 @@ class Chef
found = rs.find_all do |r|
name_matches = r["Name"].casecmp(name) == 0
if name_matches
- module_name == nil || (r["Module"] && r["Module"]["Name"].casecmp(module_name) == 0)
+ module_name.nil? || (r["Module"] && r["Module"]["Name"].casecmp(module_name) == 0)
else
false
end
diff --git a/lib/chef/util/powershell/cmdlet.rb b/lib/chef/util/powershell/cmdlet.rb
index 6ab380c071..e300266b1e 100644
--- a/lib/chef/util/powershell/cmdlet.rb
+++ b/lib/chef/util/powershell/cmdlet.rb
@@ -62,8 +62,11 @@ class Chef
json_depth = @output_format_options[:depth]
end
- json_command = @json_format ? " | convertto-json -compress -depth #{json_depth} "\
- "> #{streams[:json].path}" : ""
+ json_command = if @json_format
+ " | convertto-json -compress -depth #{json_depth} > #{streams[:json].path}"
+ else
+ ""
+ end
redirections = "4> '#{streams[:verbose].path}'"
command_string = "powershell.exe -executionpolicy bypass -noprofile -noninteractive "\
"-command \"trap [Exception] {write-error -exception "\
diff --git a/lib/chef/util/selinux.rb b/lib/chef/util/selinux.rb
index 1aac7eeeca..0d973b0376 100644
--- a/lib/chef/util/selinux.rb
+++ b/lib/chef/util/selinux.rb
@@ -48,10 +48,11 @@ class Chef
def restore_security_context(file_path, recursive = false)
if restorecon_path
- restorecon_command = recursive ? "#{restorecon_path} -R -r" : "#{restorecon_path} -R"
- restorecon_command += " \"#{file_path}\""
- Chef::Log.debug("Restoring selinux security content with #{restorecon_command}")
- shell_out!(restorecon_command)
+ restorecon_flags = [ "-R" ]
+ restorecon_flags << "-r" if recursive
+ restorecon_flags << file_path
+ Chef::Log.trace("Restoring selinux security content with #{restorecon_path}")
+ shell_out_compact!(restorecon_path, restorecon_flags)
else
Chef::Log.warn "Can not find 'restorecon' on the system. Skipping selinux security context restore."
end
@@ -78,12 +79,12 @@ class Chef
when 0
return true
else
- raise RuntimeError, "Unknown exit code from command #{selinuxenabled_path}: #{cmd.exitstatus}"
+ raise "Unknown exit code from command #{selinuxenabled_path}: #{cmd.exitstatus}"
end
else
# We assume selinux is not enabled if selinux utils are not
# installed.
- return false
+ false
end
end
diff --git a/lib/chef/util/windows/logon_session.rb b/lib/chef/util/windows/logon_session.rb
new file mode 100644
index 0000000000..afe58ae4f9
--- /dev/null
+++ b/lib/chef/util/windows/logon_session.rb
@@ -0,0 +1,129 @@
+#
+# Author:: Adam Edwards (<adamed@chef.io>)
+#
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/win32/api/security" if Chef::Platform.windows?
+require "chef/mixin/wide_string"
+
+class Chef
+ class Util
+ class Windows
+ class LogonSession
+ include Chef::Mixin::WideString
+
+ def initialize(username, password, domain = nil, authentication = :remote)
+ if username.nil? || password.nil?
+ raise ArgumentError, "The logon session must be initialize with non-nil user name and password parameters"
+ end
+
+ @original_username = username
+ @original_password = password
+ @original_domain = domain
+ @authentication = authentication
+ @token = FFI::Buffer.new(:pointer)
+ @session_opened = false
+ @impersonating = false
+ end
+
+ def open
+ if session_opened
+ raise "Attempted to open a logon session that was already open."
+ end
+
+ username = wstring(original_username)
+ password = wstring(original_password)
+ domain = wstring(original_domain)
+
+ logon_type = (authentication == :local) ? (Chef::ReservedNames::Win32::API::Security::LOGON32_LOGON_NETWORK) : (Chef::ReservedNames::Win32::API::Security::LOGON32_LOGON_NEW_CREDENTIALS)
+ status = Chef::ReservedNames::Win32::API::Security.LogonUserW(username, domain, password, logon_type, Chef::ReservedNames::Win32::API::Security::LOGON32_PROVIDER_DEFAULT, token)
+
+ if !status
+ last_error = FFI::LastError.error
+ raise Chef::Exceptions::Win32APIError, "Logon for user `#{original_username}` failed with Win32 status #{last_error}."
+ end
+
+ @session_opened = true
+ end
+
+ def close
+ validate_session_open!
+
+ if impersonating
+ restore_user_context
+ end
+
+ Chef::ReservedNames::Win32::API::System.CloseHandle(token.read_ulong)
+ @token = nil
+ @session_opened = false
+ end
+
+ def set_user_context
+ validate_session_open!
+
+ if ! session_opened
+ raise "Attempted to set the user context before opening a session."
+ end
+
+ if impersonating
+ raise "Attempt to set the user context when the user context is already set."
+ end
+
+ status = Chef::ReservedNames::Win32::API::Security.ImpersonateLoggedOnUser(token.read_ulong)
+
+ if !status
+ last_error = FFI::LastError.error
+ raise Chef::Exceptions::Win32APIError, "Attempt to impersonate user `#{original_username}` failed with Win32 status #{last_error}."
+ end
+
+ @impersonating = true
+ end
+
+ def restore_user_context
+ validate_session_open!
+
+ if impersonating
+ status = Chef::ReservedNames::Win32::API::Security.RevertToSelf
+
+ if !status
+ last_error = FFI::LastError.error
+ raise Chef::Exceptions::Win32APIError, "Unable to restore user context with Win32 status #{last_error}."
+ end
+ end
+
+ @impersonating = false
+ end
+
+ protected
+
+ attr_reader :original_username
+ attr_reader :original_password
+ attr_reader :original_domain
+ attr_reader :authentication
+
+ attr_reader :token
+ attr_reader :session_opened
+ attr_reader :impersonating
+
+ def validate_session_open!
+ if ! session_opened
+ raise "Attempted to set the user context before opening a session."
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/util/windows/net_group.rb b/lib/chef/util/windows/net_group.rb
index 0a351fbc6f..214881df56 100644
--- a/lib/chef/util/windows/net_group.rb
+++ b/lib/chef/util/windows/net_group.rb
@@ -35,50 +35,38 @@ class Chef::Util::Windows::NetGroup
end
def local_get_members
- begin
- Chef::ReservedNames::Win32::NetUser.net_local_group_get_members(nil, groupname)
- rescue Chef::Exceptions::Win32APIError => e
- raise ArgumentError, e
- end
+ Chef::ReservedNames::Win32::NetUser.net_local_group_get_members(nil, groupname)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
def local_add
- begin
- Chef::ReservedNames::Win32::NetUser.net_local_group_add(nil, groupname)
- rescue Chef::Exceptions::Win32APIError => e
- raise ArgumentError, e
- end
+ Chef::ReservedNames::Win32::NetUser.net_local_group_add(nil, groupname)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
def local_set_members(members)
- begin
- Chef::ReservedNames::Win32::NetUser.net_local_group_set_members(nil, groupname, members)
- rescue Chef::Exceptions::Win32APIError => e
- raise ArgumentError, e
- end
+ Chef::ReservedNames::Win32::NetUser.net_local_group_set_members(nil, groupname, members)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
def local_add_members(members)
- begin
- Chef::ReservedNames::Win32::NetUser.net_local_group_add_members(nil, groupname, members)
- rescue Chef::Exceptions::Win32APIError => e
- raise ArgumentError, e
- end
+ Chef::ReservedNames::Win32::NetUser.net_local_group_add_members(nil, groupname, members)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
def local_delete_members(members)
- begin
- Chef::ReservedNames::Win32::NetUser.net_local_group_del_members(nil, groupname, members)
- rescue Chef::Exceptions::Win32APIError => e
- raise ArgumentError, e
- end
+ Chef::ReservedNames::Win32::NetUser.net_local_group_del_members(nil, groupname, members)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
def local_delete
- begin
- Chef::ReservedNames::Win32::NetUser.net_local_group_del(nil, groupname)
- rescue Chef::Exceptions::Win32APIError => e
- raise ArgumentError, e
- end
+ Chef::ReservedNames::Win32::NetUser.net_local_group_del(nil, groupname)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
end
diff --git a/lib/chef/util/windows/net_use.rb b/lib/chef/util/windows/net_use.rb
index b9c3ecc783..196ce42215 100644
--- a/lib/chef/util/windows/net_use.rb
+++ b/lib/chef/util/windows/net_use.rb
@@ -59,12 +59,10 @@ class Chef::Util::Windows::NetUse < Chef::Util::Windows
end
def get_info
- begin
- ui2 = Chef::ReservedNames::Win32::Net.net_use_get_info_l2(nil, use_name)
- from_use_info_struct(ui2)
- rescue Chef::Exceptions::Win32APIError => e
- raise ArgumentError, e
- end
+ ui2 = Chef::ReservedNames::Win32::Net.net_use_get_info_l2(nil, use_name)
+ from_use_info_struct(ui2)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
def device
@@ -72,11 +70,9 @@ class Chef::Util::Windows::NetUse < Chef::Util::Windows
end
def delete
- begin
- Chef::ReservedNames::Win32::Net.net_use_del(nil, use_name, :use_noforce)
- rescue Chef::Exceptions::Win32APIError => e
- raise ArgumentError, e
- end
+ Chef::ReservedNames::Win32::Net.net_use_del(nil, use_name, :use_noforce)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
def use_name
diff --git a/lib/chef/util/windows/net_user.rb b/lib/chef/util/windows/net_user.rb
index 009252c4c1..cf8bf3615a 100644
--- a/lib/chef/util/windows/net_user.rb
+++ b/lib/chef/util/windows/net_user.rb
@@ -78,11 +78,9 @@ class Chef::Util::Windows::NetUser < Chef::Util::Windows
end
def set_info(args)
- begin
- rc = NetUser.net_user_set_info_l3(nil, @username, transform_usri3(args))
- rescue Chef::Exceptions::Win32APIError => e
- raise ArgumentError, e
- end
+ rc = NetUser.net_user_set_info_l3(nil, @username, transform_usri3(args))
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
public
@@ -95,13 +93,11 @@ class Chef::Util::Windows::NetUser < Chef::Util::Windows
LOGON32_LOGON_NETWORK = Security::LOGON32_LOGON_NETWORK
#XXX for an extra painful alternative, see: http://support.microsoft.com/kb/180548
def validate_credentials(passwd)
- begin
- token = Security.logon_user(@username, nil, passwd,
- LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT)
- return true
- rescue Chef::Exceptions::Win32APIError
- return false
- end
+ token = Security.logon_user(@username, nil, passwd,
+ LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT)
+ true
+ rescue Chef::Exceptions::Win32APIError
+ false
end
def get_info
@@ -116,7 +112,7 @@ class Chef::Util::Windows::NetUser < Chef::Util::Windows
def add(args)
transformed_args = transform_usri3(args)
NetUser.net_user_add_l3(nil, transformed_args)
- NetUser.net_local_group_add_member(nil, "Users", args[:name])
+ NetUser.net_local_group_add_member(nil, Chef::ReservedNames::Win32::Security::SID.BuiltinUsers.account_simple_name, args[:name])
end
# FIXME: yard with @yield
@@ -137,11 +133,9 @@ class Chef::Util::Windows::NetUser < Chef::Util::Windows
end
def delete
- begin
- NetUser.net_user_del(nil, @username)
- rescue Chef::Exceptions::Win32APIError => e
- raise ArgumentError, e
- end
+ NetUser.net_user_del(nil, @username)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
def disable_account
diff --git a/lib/chef/util/windows/volume.rb b/lib/chef/util/windows/volume.rb
index a18644cece..358a3f4bb8 100644
--- a/lib/chef/util/windows/volume.rb
+++ b/lib/chef/util/windows/volume.rb
@@ -30,30 +30,20 @@ class Chef::Util::Windows::Volume < Chef::Util::Windows
end
def device
- begin
- Chef::ReservedNames::Win32::File.get_volume_name_for_volume_mount_point(mount_point)
- rescue Chef::Exceptions::Win32APIError => e
- raise ArgumentError, e
- end
+ Chef::ReservedNames::Win32::File.get_volume_name_for_volume_mount_point(mount_point)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
def delete
- begin
- Chef::ReservedNames::Win32::File.delete_volume_mount_point(mount_point)
- rescue Chef::Exceptions::Win32APIError => e
- raise ArgumentError, e
- end
+ Chef::ReservedNames::Win32::File.delete_volume_mount_point(mount_point)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
def add(args)
- begin
- Chef::ReservedNames::Win32::File.set_volume_mount_point(mount_point, args[:remote])
- rescue Chef::Exceptions::Win32APIError => e
- raise ArgumentError, e
- end
- end
-
- def mount_point
- @mount_point
+ Chef::ReservedNames::Win32::File.set_volume_mount_point(mount_point, args[:remote])
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
end
diff --git a/lib/chef/version.rb b/lib/chef/version.rb
index 7bd9f874f7..c6163f636d 100644
--- a/lib/chef/version.rb
+++ b/lib/chef/version.rb
@@ -19,9 +19,11 @@
# task instead.
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+require "chef/version_string"
+
class Chef
CHEF_ROOT = File.expand_path("../..", __FILE__)
- VERSION = "12.10.27"
+ VERSION = Chef::VersionString.new("14.1.8")
end
#
diff --git a/lib/chef/version/platform.rb b/lib/chef/version/platform.rb
index 07b1a17b11..6d8f4192fe 100644
--- a/lib/chef/version/platform.rb
+++ b/lib/chef/version/platform.rb
@@ -16,6 +16,24 @@
require "chef/version_class"
+# NOTE: this is fairly badly broken for its purpose and should not be used
+# unless it gets fixed.
+
+# this strictly wants x, x.y, or x.y.z version constraints in the target and
+# will fail hard if it does not match. the semantics that we need here is that
+# it must always do the best job that it can do and consume as much of the
+# offered version as it can. since we accept arbitrarily parsed strings into
+# node[:platform_version] out of dozens or potentially hundreds of operating
+# systems this parsing code needs to be fixed to never raise. the Gem::Version
+# class is a better model, and in fact it might be a substantially better approach
+# to base this class on Gem::Version and then do pre-mangling of things like windows
+# version strings via e.g. `.gsub(/R/, '.')`. the raising behavior of this parser
+# however, breaks the ProviderResolver in a not just buggy but a "completely unfit
+# for purpose" way.
+#
+# TL;DR: MUST follow the second part of "Be conservative in what you send,
+# be liberal in what you accept"
+#
class Chef
class Version
class Platform < Chef::Version
diff --git a/lib/chef/version_class.rb b/lib/chef/version_class.rb
index 35a9f3282f..f98673b019 100644
--- a/lib/chef/version_class.rb
+++ b/lib/chef/version_class.rb
@@ -32,10 +32,15 @@ class Chef
"#{@major}.#{@minor}.#{@patch}"
end
- def <=>(v)
+ def <=>(other)
[:major, :minor, :patch].each do |method|
- ans = (self.send(method) <=> v.send(method))
- return ans if ans != 0
+ version = send(method)
+ begin
+ ans = (version <=> other.send(method))
+ rescue NoMethodError # if the other thing isn't a version object, return nil
+ return nil
+ end
+ return ans unless ans == 0
end
0
end
diff --git a/lib/chef/version_constraint.rb b/lib/chef/version_constraint.rb
index d4fa5df775..f10325f946 100644
--- a/lib/chef/version_constraint.rb
+++ b/lib/chef/version_constraint.rb
@@ -57,8 +57,8 @@ class Chef
"#{@op} #{@raw_version}"
end
- def eql?(o)
- o.class == self.class && @op == o.op && @version == o.version
+ def eql?(other)
+ other.class == self.class && @op == other.op && @version == other.version
end
alias_method :==, :eql?
diff --git a/lib/chef/version_constraint/platform.rb b/lib/chef/version_constraint/platform.rb
index 29f4678bb5..e39da194c6 100644
--- a/lib/chef/version_constraint/platform.rb
+++ b/lib/chef/version_constraint/platform.rb
@@ -16,6 +16,8 @@
require "chef/version_constraint"
require "chef/version/platform"
+# NOTE: this is fairly badly broken for its purpose and should not be used
+# unless it gets fixed. see chef/version/platform.
class Chef
class VersionConstraint
class Platform < Chef::VersionConstraint
diff --git a/lib/chef/version_string.rb b/lib/chef/version_string.rb
new file mode 100644
index 0000000000..c307509425
--- /dev/null
+++ b/lib/chef/version_string.rb
@@ -0,0 +1,143 @@
+# Copyright:: Copyright 2017, Noah Kantrowitz
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+class Chef
+ # String-like object for version strings.
+ #
+ # @since 13.2
+ # @api internal
+ class VersionString < String
+ # Parsed version object for the string.
+ # @return [Gem::Version]
+ attr_reader :parsed_version
+
+ # Create a new VersionString from an input String.
+ #
+ # @param val [String] Version string to parse.
+ def initialize(val)
+ super
+ @parsed_version = ::Gem::Version.create(self)
+ end
+
+ # @!group Compat wrappers for String
+
+ # Compat wrapper for + to behave like a normal String.
+ #
+ # @param other [String]
+ # @return [String]
+ def +(other)
+ to_s + other
+ end
+
+ # Compat wrapper for * to behave like a normal String.
+ #
+ # @param other [Integer]
+ # @return [String]
+ def *(other)
+ to_s * other
+ end
+
+ # @!group Comparison operators
+
+ # Compare a VersionString to an object. If compared to another VersionString
+ # then sort like `Gem::Version`, otherwise try to treat the other object as
+ # a version but fall back to normal string comparison.
+ #
+ # @param other [Object]
+ # @return [Integer]
+ def <=>(other)
+ other_ver = case other
+ when VersionString
+ other.parsed_version
+ else
+ begin
+ Gem::Version.create(other.to_s)
+ rescue ArgumentError
+ # Comparing to a string that isn't a version.
+ return super
+ end
+ end
+ parsed_version <=> other_ver
+ end
+
+ # Compat wrapper for == based on <=>.
+ #
+ # @param other [Object]
+ # @return [Boolean]
+ def ==(other)
+ (self <=> other) == 0
+ end
+
+ # Compat wrapper for != based on <=>.
+ #
+ # @param other [Object]
+ # @return [Boolean]
+ def !=(other)
+ (self <=> other) != 0
+ end
+
+ # Compat wrapper for < based on <=>.
+ #
+ # @param other [Object]
+ # @return [Boolean]
+ def <(other)
+ (self <=> other) < 0
+ end
+
+ # Compat wrapper for <= based on <=>.
+ #
+ # @param other [Object]
+ # @return [Boolean]
+ def <=(other)
+ (self <=> other) < 1
+ end
+
+ # Compat wrapper for > based on <=>.
+ #
+ # @param other [Object]
+ # @return [Boolean]
+ def >(other)
+ (self <=> other) > 0
+ end
+
+ # Compat wrapper for >= based on <=>.
+ #
+ # @param other [Object]
+ # @return [Boolean]
+ def >=(other)
+ (self <=> other) > -1
+ end
+
+ # @!group Matching operators
+
+ # Matching operator to support checking against a requirement string.
+ #
+ # @param other [Regexp, String]
+ # @return [Boolean]
+ # @example Match against a Regexp
+ # Chef::VersionString.new('1.0.0') =~ /^1/
+ # @example Match against a requirement
+ # Chef::VersionString.new('1.0.0') =~ '~> 1.0'
+ def =~(other)
+ case other
+ when Regexp
+ super
+ else
+ Gem::Requirement.create(other) =~ parsed_version
+ end
+ end
+
+ end
+end
diff --git a/lib/chef/win32/api.rb b/lib/chef/win32/api.rb
index 64db9d2b63..503920a0ed 100644
--- a/lib/chef/win32/api.rb
+++ b/lib/chef/win32/api.rb
@@ -29,12 +29,10 @@ class Chef
# function into the calling module. If this fails a dummy method is
# defined which when called, raises a helpful exception to the end-user.
def safe_attach_function(win32_func, *args)
- begin
- attach_function(win32_func.to_sym, *args)
- rescue FFI::NotFoundError
- define_method(win32_func.to_sym) do |*margs|
- raise Chef::Exceptions::Win32APIFunctionNotImplemented, "This version of Windows does not implement the Win32 function [#{win32_func}]."
- end
+ attach_function(win32_func.to_sym, *args)
+ rescue FFI::NotFoundError
+ define_method(win32_func.to_sym) do |*margs|
+ raise Chef::Exceptions::Win32APIFunctionNotImplemented, "This version of Windows does not implement the Win32 function [#{win32_func}]."
end
end
diff --git a/lib/chef/win32/api/error.rb b/lib/chef/win32/api/error.rb
index dc83f9cb2b..758eb98874 100644
--- a/lib/chef/win32/api/error.rb
+++ b/lib/chef/win32/api/error.rb
@@ -194,12 +194,12 @@ class Chef
ERROR_INVALID_EXE_SIGNATURE = 191
ERROR_EXE_MARKED_INVALID = 192
ERROR_BAD_EXE_FORMAT = 193
- ERROR_ITERATED_DATA_EXCEEDS_64k = 194
+ ERROR_ITERATED_DATA_EXCEEDS_64k = 194 # rubocop:disable Naming/ConstantName
ERROR_INVALID_MINALLOCSIZE = 195
ERROR_DYNLINK_FROM_INVALID_RING = 196
ERROR_IOPL_NOT_ENABLED = 197
ERROR_INVALID_SEGDPL = 198
- ERROR_AUTODATASEG_EXCEEDS_64k = 199
+ ERROR_AUTODATASEG_EXCEEDS_64k = 199 # rubocop:disable Naming/ConstantName
ERROR_RING2SEG_MUST_BE_MOVABLE = 200
ERROR_RELOC_CHAIN_XEEDS_SEGLIM = 201
ERROR_INFLOOP_IN_RELOC_CHAIN = 202
diff --git a/lib/chef/win32/api/file.rb b/lib/chef/win32/api/file.rb
index 7489c94fd9..6aa2927e1f 100644
--- a/lib/chef/win32/api/file.rb
+++ b/lib/chef/win32/api/file.rb
@@ -67,6 +67,7 @@ class Chef
MAX_PATH = 260
SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1
+ SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2
FILE_NAME_NORMALIZED = 0x0
FILE_NAME_OPENED = 0x8
@@ -537,24 +538,22 @@ BOOL WINAPI VerQueryValue(
# ensures the handle is closed on exit of the block
# FIXME: yard with @yield
def file_search_handle(path)
- begin
# Workaround for CHEF-4419:
# Make sure paths starting with "/" has a drive letter
# assigned from the current working diretory.
# Note: With CHEF-4427 this issue will be fixed with a
# broader fix to map all the paths starting with "/" to
# SYSTEM_DRIVE on windows.
- path = ::File.expand_path(path) if path.start_with? "/"
- path = canonical_encode_path(path)
- find_data = WIN32_FIND_DATA.new
- handle = FindFirstFileW(path, find_data)
- if handle == INVALID_HANDLE_VALUE
- Chef::ReservedNames::Win32::Error.raise!
- end
- yield(handle, find_data)
- ensure
- FindClose(handle) if handle && handle != INVALID_HANDLE_VALUE
+ path = ::File.expand_path(path) if path.start_with? "/"
+ path = canonical_encode_path(path)
+ find_data = WIN32_FIND_DATA.new
+ handle = FindFirstFileW(path, find_data)
+ if handle == INVALID_HANDLE_VALUE
+ Chef::ReservedNames::Win32::Error.raise!
end
+ yield(handle, find_data)
+ ensure
+ FindClose(handle) if handle && handle != INVALID_HANDLE_VALUE
end
# retrieves a file handle and passes it
@@ -562,34 +561,30 @@ BOOL WINAPI VerQueryValue(
# ensures the handle is closed on exit of the block
# FIXME: yard with @yield
def file_handle(path)
- begin
- path = canonical_encode_path(path)
- handle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
- nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, nil)
+ path = canonical_encode_path(path)
+ handle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
+ nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, nil)
- if handle == INVALID_HANDLE_VALUE
- Chef::ReservedNames::Win32::Error.raise!
- end
- yield(handle)
- ensure
- CloseHandle(handle) if handle && handle != INVALID_HANDLE_VALUE
+ if handle == INVALID_HANDLE_VALUE
+ Chef::ReservedNames::Win32::Error.raise!
end
+ yield(handle)
+ ensure
+ CloseHandle(handle) if handle && handle != INVALID_HANDLE_VALUE
end
# FIXME: yard with @yield
def symlink_file_handle(path)
- begin
- path = encode_path(path)
- handle = CreateFileW(path, FILE_READ_EA, FILE_SHARE_READ,
- nil, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nil)
+ path = encode_path(path)
+ handle = CreateFileW(path, FILE_READ_EA, FILE_SHARE_READ,
+ nil, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nil)
- if handle == INVALID_HANDLE_VALUE
- Chef::ReservedNames::Win32::Error.raise!
- end
- yield(handle)
- ensure
- CloseHandle(handle) if handle && handle != INVALID_HANDLE_VALUE
+ if handle == INVALID_HANDLE_VALUE
+ Chef::ReservedNames::Win32::Error.raise!
end
+ yield(handle)
+ ensure
+ CloseHandle(handle) if handle && handle != INVALID_HANDLE_VALUE
end
def retrieve_file_info(file_name)
diff --git a/lib/chef/win32/api/installer.rb b/lib/chef/win32/api/installer.rb
index caf7b23f59..46930b65fc 100644
--- a/lib/chef/win32/api/installer.rb
+++ b/lib/chef/win32/api/installer.rb
@@ -107,7 +107,7 @@ UINT MsiCloseHandle(
end
msi_close_handle(pkg_ptr.read_pointer)
- return buffer.chomp(0.chr)
+ buffer.chomp(0.chr)
end
# Opens a Microsoft Installer (MSI) file from an absolute path and returns a pointer to a handle
@@ -124,7 +124,7 @@ UINT MsiCloseHandle(
else
raise Chef::Exceptions::Package, "msi_open_package: unexpected status #{status}: #{Chef::ReservedNames::Win32::Error.format_message(status)}"
end
- return pkg_ptr
+ pkg_ptr
end
# All installed product_codes should have a VersionString
diff --git a/lib/chef/win32/api/net.rb b/lib/chef/win32/api/net.rb
index bec00f638a..169c68bbdf 100644
--- a/lib/chef/win32/api/net.rb
+++ b/lib/chef/win32/api/net.rb
@@ -45,7 +45,7 @@ class Chef
USE_FORCE = 1
USE_LOTS_OF_FORCE = 2 #every windows API should support this flag
- NERR_Success = 0
+ NERR_Success = 0 # rubocop:disable Naming/ConstantName
ERROR_MORE_DATA = 234
ffi_lib "netapi32"
diff --git a/lib/chef/win32/api/security.rb b/lib/chef/win32/api/security.rb
index 64df077686..6620f321aa 100644
--- a/lib/chef/win32/api/security.rb
+++ b/lib/chef/win32/api/security.rb
@@ -182,18 +182,18 @@ class Chef
MAXDWORD = 0xffffffff
# LOGON32 constants for LogonUser
- LOGON32_LOGON_INTERACTIVE = 2;
- LOGON32_LOGON_NETWORK = 3;
- LOGON32_LOGON_BATCH = 4;
- LOGON32_LOGON_SERVICE = 5;
- LOGON32_LOGON_UNLOCK = 7;
- LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
- LOGON32_LOGON_NEW_CREDENTIALS = 9;
-
- LOGON32_PROVIDER_DEFAULT = 0;
- LOGON32_PROVIDER_WINNT35 = 1;
- LOGON32_PROVIDER_WINNT40 = 2;
- LOGON32_PROVIDER_WINNT50 = 3;
+ LOGON32_LOGON_INTERACTIVE = 2
+ LOGON32_LOGON_NETWORK = 3
+ LOGON32_LOGON_BATCH = 4
+ LOGON32_LOGON_SERVICE = 5
+ LOGON32_LOGON_UNLOCK = 7
+ LOGON32_LOGON_NETWORK_CLEARTEXT = 8
+ LOGON32_LOGON_NEW_CREDENTIALS = 9
+
+ LOGON32_PROVIDER_DEFAULT = 0
+ LOGON32_PROVIDER_WINNT35 = 1
+ LOGON32_PROVIDER_WINNT40 = 2
+ LOGON32_PROVIDER_WINNT50 = 3
# LSA access policy
POLICY_VIEW_LOCAL_INFORMATION = 0x00000001
@@ -303,6 +303,17 @@ class Chef
:SecurityDelegation,
]
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/bb530718%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
+ ELEVATION_TYPE = enum :ELEVATION_TYPE, [
+ :TokenElevationTypeDefault, 1,
+ :TokenElevationTypeFull,
+ :TokenElevationTypeLimited
+ ]
+
+ class TOKEN_ELEVATION_TYPE < FFI::Struct
+ layout :ElevationType, :ELEVATION_TYPE
+ end
+
# SECURITY_DESCRIPTOR is an opaque structure whose contents can vary. Pass the
# pointer around and free it with LocalFree.
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa379561(v=vs.85).aspx
@@ -453,6 +464,8 @@ class Chef
safe_attach_function :SetSecurityDescriptorSacl, [ :pointer, :BOOL, :pointer, :BOOL ], :BOOL
safe_attach_function :GetTokenInformation, [ :HANDLE, :TOKEN_INFORMATION_CLASS, :pointer, :DWORD, :PDWORD ], :BOOL
safe_attach_function :LogonUserW, [:LPTSTR, :LPTSTR, :LPTSTR, :DWORD, :DWORD, :PHANDLE], :BOOL
+ safe_attach_function :ImpersonateLoggedOnUser, [:HANDLE], :BOOL
+ safe_attach_function :RevertToSelf, [], :BOOL
end
end
diff --git a/lib/chef/win32/eventlog.rb b/lib/chef/win32/eventlog.rb
index 4254b8ead3..e8c63bf13a 100644
--- a/lib/chef/win32/eventlog.rb
+++ b/lib/chef/win32/eventlog.rb
@@ -16,7 +16,7 @@
# limitations under the License.
#
-if Chef::Platform.windows? && (not Chef::Platform.windows_server_2003?)
+if Chef::Platform.windows?
if !defined? Chef::Win32EventLogLoaded
if defined? Windows::Constants
[:INFINITE, :WAIT_FAILED, :FORMAT_MESSAGE_IGNORE_INSERTS, :ERROR_INSUFFICIENT_BUFFER].each do |c|
@@ -26,6 +26,6 @@ if Chef::Platform.windows? && (not Chef::Platform.windows_server_2003?)
end
require "win32/eventlog"
- Chef::Win32EventLogLoaded = true
+ Chef::Win32EventLogLoaded = true # rubocop:disable Naming/ConstantName
end
end
diff --git a/lib/chef/win32/file.rb b/lib/chef/win32/file.rb
index 2a8f453432..03d4496fa8 100644
--- a/lib/chef/win32/file.rb
+++ b/lib/chef/win32/file.rb
@@ -22,6 +22,7 @@ require "chef/win32/api/file"
require "chef/win32/api/security"
require "chef/win32/error"
require "chef/win32/unicode"
+require "chef/win32/version"
class Chef
module ReservedNames::Win32
@@ -39,7 +40,7 @@ class Chef
# returns nil as per MRI.
#
def self.link(old_name, new_name)
- raise Errno::ENOENT, "(#{old_name}, #{new_name})" unless ::File.exist?(old_name)
+ raise Errno::ENOENT, "(#{old_name}, #{new_name})" unless ::File.exist?(old_name) || ::File.symlink?(old_name)
# TODO do a check for CreateHardLinkW and
# raise NotImplemented exception on older Windows
old_name = encode_path(old_name)
@@ -56,10 +57,11 @@ class Chef
# returns nil as per MRI.
#
def self.symlink(old_name, new_name)
- # raise Errno::ENOENT, "(#{old_name}, #{new_name})" unless ::File.exist?(old_name)
+ # raise Errno::ENOENT, "(#{old_name}, #{new_name})" unless ::File.exist?(old_name) || ::File.symlink?(old_name)
# TODO do a check for CreateSymbolicLinkW and
# raise NotImplemented exception on older Windows
flags = ::File.directory?(old_name) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0
+ flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE if Chef::ReservedNames::Win32::Version.new.win_10_creators_or_higher?
old_name = encode_path(old_name)
new_name = encode_path(new_name)
unless CreateSymbolicLinkW(new_name, old_name, flags)
@@ -75,7 +77,7 @@ class Chef
def self.symlink?(file_name)
is_symlink = false
path = encode_path(file_name)
- if ::File.exists?(file_name)
+ if ::File.exists?(file_name) || ::File.symlink?(file_name)
if (GetFileAttributesW(path) & FILE_ATTRIBUTE_REPARSE_POINT) > 0
file_search_handle(file_name) do |handle, find_data|
if find_data[:dw_reserved_0] == IO_REPARSE_TAG_SYMLINK
@@ -93,7 +95,7 @@ class Chef
# will raise a NotImplementedError, as per MRI.
#
def self.readlink(link_name)
- raise Errno::ENOENT, link_name unless ::File.exists?(link_name)
+ raise Errno::ENOENT, link_name unless ::File.exists?(link_name) || ::File.symlink?(link_name)
symlink_file_handle(link_name) do |handle|
# Go to DeviceIoControl to get the symlink information
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa364571(v=vs.85).aspx
@@ -155,13 +157,11 @@ class Chef
end
def self.verify_links_supported!
- begin
- CreateSymbolicLinkW(nil)
- rescue Chef::Exceptions::Win32APIFunctionNotImplemented => e
- raise e
- rescue Exception
+ CreateSymbolicLinkW(nil)
+ rescue Chef::Exceptions::Win32APIFunctionNotImplemented => e
+ raise e
+ rescue Exception
# things are ok.
- end
end
def self.file_access_check(path, desired_access)
diff --git a/lib/chef/win32/memory.rb b/lib/chef/win32/memory.rb
index 49dcdfbd41..2a9d5d8eb5 100644
--- a/lib/chef/win32/memory.rb
+++ b/lib/chef/win32/memory.rb
@@ -35,7 +35,7 @@ class Chef
Chef::ReservedNames::Win32::Error.raise!
end
# If a block is passed, handle freeing the memory at the end
- if block != nil
+ if !block.nil?
begin
yield result
ensure
diff --git a/lib/chef/win32/mutex.rb b/lib/chef/win32/mutex.rb
index a14a160f56..2264479734 100644
--- a/lib/chef/win32/mutex.rb
+++ b/lib/chef/win32/mutex.rb
@@ -55,7 +55,7 @@ class Chef
when WAIT_ABANDONED
# Previous owner of the mutex died before it can release the
# mutex. Log a warning and continue.
- Chef::Log.debug "Existing owner of the mutex exited prematurely."
+ Chef::Log.trace "Existing owner of the mutex exited prematurely."
break
when WAIT_OBJECT_0
# Mutex is successfully acquired.
diff --git a/lib/chef/win32/net.rb b/lib/chef/win32/net.rb
index 0454b17d49..09db2af89d 100644
--- a/lib/chef/win32/net.rb
+++ b/lib/chef/win32/net.rb
@@ -207,7 +207,7 @@ class Chef
def self.members_to_lgrmi3(members)
buf = FFI::MemoryPointer.new(LOCALGROUP_MEMBERS_INFO_3, members.size)
- members.size.times.collect do |i|
+ Array.new(members.size) do |i|
member_info = LOCALGROUP_MEMBERS_INFO_3.new(
buf + i * LOCALGROUP_MEMBERS_INFO_3.size)
member_info[:lgrmi3_domainandname] = FFI::MemoryPointer.from_string(wstring(members[i]))
diff --git a/lib/chef/win32/registry.rb b/lib/chef/win32/registry.rb
index 613994295c..1193911b00 100644
--- a/lib/chef/win32/registry.rb
+++ b/lib/chef/win32/registry.rb
@@ -2,7 +2,7 @@
# Author:: Prajakta Purohit (<prajakta@chef.io>)
# Author:: Lamont Granquist (<lamont@chef.io>)
#
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright 2012-2018, Chef Software Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -40,7 +40,7 @@ class Chef
extend Chef::Mixin::WideString
attr_accessor :run_context
- attr_accessor :architecture
+ attr_reader :architecture
def initialize(run_context = nil, user_architecture = :machine)
@run_context = run_context
@@ -63,30 +63,30 @@ class Chef
def set_value(key_path, value)
data = value[:data]
data = data.to_s if value[:type] == :string
- Chef::Log.debug("Updating value #{value[:name]} in registry key #{key_path} with type #{value[:type]} and data #{data}")
+ Chef::Log.trace("Updating value #{value[:name]} in registry key #{key_path} with type #{value[:type]} and data #{data}")
key_exists!(key_path)
hive, key = get_hive_and_key(key_path)
if value_exists?(key_path, value)
if data_exists?(key_path, value)
- Chef::Log.debug("Value #{value[:name]} in registry key #{key_path} already had those values, not updated")
+ Chef::Log.trace("Value #{value[:name]} in registry key #{key_path} already had those values, not updated")
return false
else
hive.open(key, ::Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | registry_system_architecture) do |reg|
reg.write(value[:name], get_type_from_name(value[:type]), data)
end
- Chef::Log.debug("Value #{value[:name]} in registry key #{key_path} updated")
+ Chef::Log.trace("Value #{value[:name]} in registry key #{key_path} updated")
end
else
hive.open(key, ::Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | registry_system_architecture) do |reg|
reg.write(value[:name], get_type_from_name(value[:type]), data)
end
- Chef::Log.debug("Value #{value[:name]} in registry key #{key_path} created")
+ Chef::Log.trace("Value #{value[:name]} in registry key #{key_path} created")
end
true
end
def delete_value(key_path, value)
- Chef::Log.debug("Deleting value #{value[:name]} from registry key #{key_path}")
+ Chef::Log.trace("Deleting value #{value[:name]} from registry key #{key_path}")
if value_exists?(key_path, value)
begin
hive, key = get_hive_and_key(key_path)
@@ -95,38 +95,38 @@ class Chef
end
hive.open(key, ::Win32::Registry::KEY_SET_VALUE | registry_system_architecture) do |reg|
reg.delete_value(value[:name])
- Chef::Log.debug("Deleted value #{value[:name]} from registry key #{key_path}")
+ Chef::Log.trace("Deleted value #{value[:name]} from registry key #{key_path}")
end
else
- Chef::Log.debug("Value #{value[:name]} in registry key #{key_path} does not exist, not updated")
+ Chef::Log.trace("Value #{value[:name]} in registry key #{key_path} does not exist, not updated")
end
true
end
def create_key(key_path, recursive)
- Chef::Log.debug("Creating registry key #{key_path}")
+ Chef::Log.trace("Creating registry key #{key_path}")
if keys_missing?(key_path)
if recursive == true
- Chef::Log.debug("Registry key #{key_path} has missing subkeys, and recursive specified, creating them....")
+ Chef::Log.trace("Registry key #{key_path} has missing subkeys, and recursive specified, creating them....")
create_missing(key_path)
else
raise Chef::Exceptions::Win32RegNoRecursive, "Registry key #{key_path} has missing subkeys, and recursive not specified"
end
end
if key_exists?(key_path)
- Chef::Log.debug("Registry key #{key_path} already exists, doing nothing")
+ Chef::Log.trace("Registry key #{key_path} already exists, doing nothing")
else
hive, key = get_hive_and_key(key_path)
hive.create(key, ::Win32::Registry::KEY_WRITE | registry_system_architecture)
- Chef::Log.debug("Registry key #{key_path} created")
+ Chef::Log.trace("Registry key #{key_path} created")
end
true
end
def delete_key(key_path, recursive)
- Chef::Log.debug("Deleting registry key #{key_path}")
+ Chef::Log.trace("Deleting registry key #{key_path}")
unless key_exists?(key_path)
- Chef::Log.debug("Registry key #{key_path}, does not exist, not deleting")
+ Chef::Log.trace("Registry key #{key_path}, does not exist, not deleting")
return true
end
if has_subkeys?(key_path) && !recursive
@@ -142,7 +142,7 @@ class Chef
hive.open(key_parent, ::Win32::Registry::KEY_WRITE | registry_system_architecture) do |reg|
reg.delete_key(key, recursive)
end
- Chef::Log.debug("Registry key #{key_path} deleted")
+ Chef::Log.trace("Registry key #{key_path} deleted")
true
end
@@ -170,7 +170,7 @@ class Chef
rescue Chef::Exceptions::Win32RegHiveMissing => e
return false
end
- return true
+ true
end
def has_subkeys?(key_path)
@@ -179,7 +179,7 @@ class Chef
hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg|
reg.each_key { |key| return true }
end
- return false
+ false
end
def get_subkeys(key_path)
@@ -189,7 +189,7 @@ class Chef
hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg|
reg.each_key { |current_key| subkeys << current_key }
end
- return subkeys
+ subkeys
end
# 32-bit chef clients running on 64-bit machines will default to reading the 64-bit registry
@@ -204,7 +204,7 @@ class Chef
hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg|
return true if reg.any? { |val| safely_downcase(val) == safely_downcase(value[:name]) }
end
- return false
+ false
end
def data_exists?(key_path, value)
@@ -219,7 +219,7 @@ class Chef
end
end
end
- return false
+ false
end
def value_exists!(key_path, value)
@@ -249,7 +249,7 @@ class Chef
end
end
end
- return false
+ false
end
def type_matches!(key_path, value)
@@ -279,7 +279,7 @@ class Chef
if val.is_a? String
return val.downcase
end
- return val
+ val
end
def node
@@ -316,7 +316,7 @@ class Chef
raise Chef::Exceptions::Win32RegHiveMissing, "Registry Hive #{hive_name} does not exist" unless hive
- return hive, key
+ [hive, key]
end
def _type_name_map
@@ -345,7 +345,7 @@ class Chef
5 => ::Win32::Registry::REG_DWORD_BIG_ENDIAN,
11 => ::Win32::Registry::REG_QWORD,
}[val_type]
- return value
+ value
end
def create_missing(key_path)
@@ -357,7 +357,7 @@ class Chef
missing_key_arr.each do |intermediate_key|
existing_key_path = existing_key_path << "\\" << intermediate_key
if !key_exists?(existing_key_path)
- Chef::Log.debug("Recursively creating registry key #{existing_key_path}")
+ Chef::Log.trace("Recursively creating registry key #{existing_key_path}")
hive.create(get_key(existing_key_path), ::Win32::Registry::KEY_ALL_ACCESS | registry_system_architecture)
end
end
diff --git a/lib/chef/win32/security.rb b/lib/chef/win32/security.rb
index 7fc3215786..2f903d6c93 100644
--- a/lib/chef/win32/security.rb
+++ b/lib/chef/win32/security.rb
@@ -113,10 +113,7 @@ class Chef
with_lsa_policy(name) do |policy_handle, sid|
result = LsaAddAccountRights(policy_handle.read_pointer, sid, privilege_pointer, 1)
- win32_error = LsaNtStatusToWinError(result)
- if win32_error != 0
- Chef::ReservedNames::Win32::Error.raise!(nil, win32_error)
- end
+ test_and_raise_lsa_nt_status(result)
end
end
@@ -190,15 +187,14 @@ class Chef
result = LsaEnumerateAccountRights(policy_handle.read_pointer, sid, privilege_pointer, privilege_length)
win32_error = LsaNtStatusToWinError(result)
return [] if win32_error == 2 # FILE_NOT_FOUND - No rights assigned
- if win32_error != 0
- Chef::ReservedNames::Win32::Error.raise!(nil, win32_error)
- end
+ test_and_raise_lsa_nt_status(result)
privilege_length.read_ulong.times do |i|
privilege = LSA_UNICODE_STRING.new(privilege_pointer.read_pointer + i * LSA_UNICODE_STRING.size)
privileges << privilege[:Buffer].read_wstring
end
- LsaFreeMemory(privilege_pointer)
+ result = LsaFreeMemory(privilege_pointer.read_pointer)
+ test_and_raise_lsa_nt_status(result)
end
privileges
@@ -239,7 +235,7 @@ class Chef
security_descriptor = FFI::MemoryPointer.new :pointer
hr = GetNamedSecurityInfoW(path.to_wstring, type, info, nil, nil, nil, nil, security_descriptor)
if hr != ERROR_SUCCESS
- Chef::ReservedNames::Win32::Error.raise!("get_named_security_info(#{path}, #{type}, #{info})")
+ Chef::ReservedNames::Win32::Error.raise!("get_named_security_info(#{path}, #{type}, #{info})", hr)
end
result_pointer = security_descriptor.read_pointer
@@ -341,6 +337,22 @@ class Chef
SID.new(group_result[:PrimaryGroup], group_result_storage)
end
+ def self.get_token_information_elevation_type(token)
+ token_result_size = FFI::MemoryPointer.new(:ulong)
+ if GetTokenInformation(token.handle.handle, :TokenElevationType, nil, 0, token_result_size)
+ raise "Expected ERROR_INSUFFICIENT_BUFFER from GetTokenInformation, and got no error!"
+ elsif FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER
+ Chef::ReservedNames::Win32::Error.raise!
+ end
+ info_ptr = FFI::MemoryPointer.new(:pointer)
+ token_info_pointer = TOKEN_ELEVATION_TYPE.new info_ptr
+ token_info_length = 4
+ unless GetTokenInformation(token.handle.handle, :TokenElevationType, token_info_pointer, token_info_length, token_result_size)
+ Chef::ReservedNames::Win32::Error.raise!
+ end
+ token_info_pointer[:ElevationType]
+ end
+
def self.initialize_acl(acl_size)
acl = FFI::MemoryPointer.new acl_size
unless InitializeAcl(acl, acl_size, ACL_REVISION)
@@ -538,7 +550,7 @@ class Chef
hr = SetNamedSecurityInfoW(path.to_wstring, type, security_information, owner, group, dacl, sacl)
if hr != ERROR_SUCCESS
- Chef::ReservedNames::Win32::Error.raise!
+ Chef::ReservedNames::Win32::Error.raise! nil, hr
end
end
@@ -551,7 +563,7 @@ class Chef
def set_security_descriptor_dacl(security_descriptor, acl, defaulted = false, present = nil)
security_descriptor = security_descriptor.pointer if security_descriptor.respond_to?(:pointer)
acl = acl.pointer if acl.respond_to?(:pointer)
- present = !security_descriptor.null? if present == nil
+ present = !security_descriptor.null? if present.nil?
unless SetSecurityDescriptorDacl(security_descriptor, present, acl, defaulted)
Chef::ReservedNames::Win32::Error.raise!
@@ -579,7 +591,7 @@ class Chef
def self.set_security_descriptor_sacl(security_descriptor, acl, defaulted = false, present = nil)
security_descriptor = security_descriptor.pointer if security_descriptor.respond_to?(:pointer)
acl = acl.pointer if acl.respond_to?(:pointer)
- present = !security_descriptor.null? if present == nil
+ present = !security_descriptor.null? if present.nil?
unless SetSecurityDescriptorSacl(security_descriptor, present, acl, defaulted)
Chef::ReservedNames::Win32::Error.raise!
@@ -595,18 +607,13 @@ class Chef
policy_handle = FFI::MemoryPointer.new(:pointer)
result = LsaOpenPolicy(nil, LSA_OBJECT_ATTRIBUTES.new, access, policy_handle)
- win32_error = LsaNtStatusToWinError(result)
- if win32_error != 0
- Chef::ReservedNames::Win32::Error.raise!(nil, win32_error)
- end
+ test_and_raise_lsa_nt_status(result)
begin
yield policy_handle, sid.pointer
ensure
- win32_error = LsaNtStatusToWinError(LsaClose(policy_handle.read_pointer))
- if win32_error != 0
- Chef::ReservedNames::Win32::Error.raise!(nil, win32_error)
- end
+ result = LsaClose(policy_handle.read_pointer)
+ test_and_raise_lsa_nt_status(result)
end
end
@@ -627,20 +634,26 @@ class Chef
# Checks if the caller has the admin privileges in their
# security token
def self.has_admin_privileges?
- if Chef::Platform.windows_server_2003?
- # Admin privileges do not exist on Windows Server 2003
-
- true
- else
+ # a regular user doesn't have privileges to call Chef::ReservedNames::Win32::Security.OpenProcessToken
+ # hence we return false if the open_current_process_token fails with `Access is denied.` error message.
+ begin
process_token = open_current_process_token(TOKEN_READ)
- elevation_result = FFI::Buffer.new(:ulong)
- elevation_result_size = FFI::MemoryPointer.new(:uint32)
- success = GetTokenInformation(process_token.handle.handle, :TokenElevation, elevation_result, 4, elevation_result_size)
-
- # Assume process is not elevated if the call fails.
- # Process is elevated if the result is different than 0.
- success && (elevation_result.read_ulong != 0)
+ rescue Exception => run_error
+ return false if run_error.message =~ /Access is denied/
+ Chef::ReservedNames::Win32::Error.raise!
end
+
+ # display token elevation details
+ token_elevation_type = get_token_information_elevation_type(process_token)
+ Chef::Log.trace("Token Elevation Type: #{token_elevation_type}")
+
+ elevation_result = FFI::Buffer.new(:ulong)
+ elevation_result_size = FFI::MemoryPointer.new(:uint32)
+ success = GetTokenInformation(process_token.handle.handle, :TokenElevation, elevation_result, 4, elevation_result_size)
+
+ # Assume process is not elevated if the call fails.
+ # Process is elevated if the result is different than 0.
+ success && (elevation_result.read_ulong != 0)
end
def self.logon_user(username, domain, password, logon_type, logon_provider)
@@ -654,6 +667,13 @@ class Chef
end
Token.new(Handle.new(token.read_pointer))
end
+
+ def self.test_and_raise_lsa_nt_status(result)
+ win32_error = LsaNtStatusToWinError(result)
+ if win32_error != 0
+ Chef::ReservedNames::Win32::Error.raise!(nil, win32_error)
+ end
+ end
end
end
end
diff --git a/lib/chef/win32/security/acl.rb b/lib/chef/win32/security/acl.rb
index 8a04987e44..0d9f7decd0 100644
--- a/lib/chef/win32/security/acl.rb
+++ b/lib/chef/win32/security/acl.rb
@@ -48,7 +48,7 @@ class Chef
0.upto(length - 1) do |i|
return false if self[i] != other[i]
end
- return true
+ true
end
def pointer
@@ -88,7 +88,7 @@ class Chef
end
def to_s
- "[#{self.collect { |ace| ace.to_s }.join(", ")}]"
+ "[#{collect { |ace| ace.to_s }.join(", ")}]"
end
def self.align_dword(size)
diff --git a/lib/chef/win32/security/sid.rb b/lib/chef/win32/security/sid.rb
index 9951b931c9..b551cbd2e3 100644
--- a/lib/chef/win32/security/sid.rb
+++ b/lib/chef/win32/security/sid.rb
@@ -50,7 +50,7 @@ class Chef
end
def ==(other)
- other != nil && Chef::ReservedNames::Win32::Security.equal_sid(self, other)
+ !other.nil? && Chef::ReservedNames::Win32::Security.equal_sid(self, other)
end
attr_reader :pointer
@@ -59,9 +59,14 @@ class Chef
Chef::ReservedNames::Win32::Security.lookup_account_sid(self)
end
+ def account_simple_name
+ domain, name, use = account
+ name
+ end
+
def account_name
domain, name, use = account
- (domain != nil && domain.length > 0) ? "#{domain}\\#{name}" : name
+ (!domain.nil? && domain.length > 0) ? "#{domain}\\#{name}" : name
end
def size
@@ -243,8 +248,7 @@ class Chef
# See https://technet.microsoft.com/en-us/library/cc961992.aspx
# In practice, this is SID.Administrators if the current_user is an admin (even if not
- # running elevated), and is current_user otherwise. On win2k3, it technically can be
- # current_user in all cases if a certain group policy is set.
+ # running elevated), and is current_user otherwise.
def self.default_security_object_owner
token = Chef::ReservedNames::Win32::Security.open_current_process_token
Chef::ReservedNames::Win32::Security.get_token_information_owner(token)
@@ -279,7 +283,7 @@ class Chef
status = NetUserEnum(servername, level, filter, bufptr, prefmaxlen, entriesread, totalentries, resume_handle)
if status == NERR_Success || status == ERROR_MORE_DATA
- entriesread.read_long.times.collect do |i|
+ Array.new(entriesread.read_long) do |i|
user_info = USER_INFO_3.new(bufptr.read_pointer + i * USER_INFO_3.size)
# Check if the account is the Administrator account
# RID for the Administrator account is always 500 and it's privilage is set to USER_PRIV_ADMIN
diff --git a/lib/chef/win32/unicode.rb b/lib/chef/win32/unicode.rb
index d531463be0..dd5a197f71 100644
--- a/lib/chef/win32/unicode.rb
+++ b/lib/chef/win32/unicode.rb
@@ -40,13 +40,13 @@ module FFI
last_char = nil
while last_char != "\000\000"
length += 1
- last_char = self.get_bytes(0, length * 2)[-2..-1]
+ last_char = get_bytes(0, length * 2)[-2..-1]
end
num_wchars = length
end
- wide_to_utf8(self.get_bytes(0, num_wchars * 2))
+ wide_to_utf8(get_bytes(0, num_wchars * 2))
end
end
end
diff --git a/lib/chef/win32/version.rb b/lib/chef/win32/version.rb
index 303fe1531d..944cb207f5 100644
--- a/lib/chef/win32/version.rb
+++ b/lib/chef/win32/version.rb
@@ -30,6 +30,8 @@ class Chef
include Chef::ReservedNames::Win32::API::Macros
include Chef::ReservedNames::Win32::API::System
+ attr_reader :major_version, :minor_version, :build_number
+
# Ruby implementation of
# http://msdn.microsoft.com/en-us/library/ms724833(v=vs.85).aspx
# http://msdn.microsoft.com/en-us/library/ms724358(v=vs.85).aspx
@@ -74,15 +76,8 @@ class Chef
@sp_minor_version = ver_info[:w_service_pack_minor]
# Obtain sku information for the purpose of identifying
- # datacenter, cluster, and core skus, the latter 2 only
- # exist in releases after Windows Server 2003
- if ! Chef::Platform.windows_server_2003?
- @sku = get_product_info(@major_version, @minor_version, @sp_major_version, @sp_minor_version)
- else
- # The get_product_info API is not supported on Win2k3,
- # use an alternative to identify datacenter skus
- @sku = get_datacenter_product_info_windows_server_2003(ver_info)
- end
+ # datacenter, cluster, and core skus
+ @sku = get_product_info(@major_version, @minor_version, @sp_major_version, @sp_minor_version)
end
marketing_names = Array.new
@@ -100,7 +95,7 @@ class Chef
define_method(:marketing_name) do
marketing_names.each do |mn|
- break mn[0] if self.send(mn[1])
+ break mn[0] if send(mn[1])
end
end
@@ -114,6 +109,10 @@ class Chef
end
end
+ def win_10_creators_or_higher?
+ windows_10? && build_number >= 15063
+ end
+
private
def get_version
@@ -147,12 +146,6 @@ class Chef
out.get_uint(0)
end
- def get_datacenter_product_info_windows_server_2003(ver_info)
- # The intent is not to get the actual sku, just identify
- # Windows Server 2003 datacenter
- sku = (ver_info[:w_suite_mask] & VER_SUITE_DATACENTER) ? PRODUCT_DATACENTER_SERVER : 0
- end
-
end
end
end
diff --git a/lib/chef/win32_service_constants.rb b/lib/chef/win32_service_constants.rb
new file mode 100644
index 0000000000..4b5eb34327
--- /dev/null
+++ b/lib/chef/win32_service_constants.rb
@@ -0,0 +1,143 @@
+class Chef
+ module Win32ServiceConstants
+ SC_MANAGER_ALL_ACCESS = 0xF003F
+ SC_MANAGER_CREATE_SERVICE = 0x0002
+ SC_MANAGER_CONNECT = 0x0001
+ SC_MANAGER_ENUMERATE_SERVICE = 0x0004
+ SC_MANAGER_LOCK = 0x0008
+ SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020
+ SC_MANAGER_QUERY_LOCK_STATUS = 0x0010
+ SC_STATUS_PROCESS_INFO = 0
+ SC_ENUM_PROCESS_INFO = 0
+
+ # Service control action types
+ SC_ACTION_NONE = 0
+ SC_ACTION_RESTART = 1
+ SC_ACTION_REBOOT = 2
+ SC_ACTION_RUN_COMMAND = 3
+
+ # Service access rights
+ SERVICE_ALL_ACCESS = 0xF01FF
+ SERVICE_CHANGE_CONFIG = 0x0002
+ SERVICE_ENUMERATE_DEPENDENTS = 0x0008
+ SERVICE_INTERROGATE = 0x0080
+ SERVICE_PAUSE_CONTINUE = 0x0040
+ SERVICE_QUERY_CONFIG = 0x0001
+ SERVICE_QUERY_STATUS = 0x0004
+ SERVICE_START = 0x0010
+ SERVICE_STOP = 0x0020
+ SERVICE_USER_DEFINED_CONTROL = 0x0100
+
+ # Service types
+ SERVICE_KERNEL_DRIVER = 0x00000001
+ SERVICE_FILE_SYSTEM_DRIVER = 0x00000002
+ SERVICE_ADAPTER = 0x00000004
+ SERVICE_RECOGNIZER_DRIVER = 0x00000008
+ SERVICE_WIN32_OWN_PROCESS = 0x00000010
+ SERVICE_WIN32_SHARE_PROCESS = 0x00000020
+ SERVICE_WIN32 = 0x00000030
+ SERVICE_INTERACTIVE_PROCESS = 0x00000100
+ SERVICE_DRIVER = 0x0000000B
+ SERVICE_TYPE_ALL = 0x0000013F
+
+ # Error control
+ SERVICE_ERROR_IGNORE = 0x00000000
+ SERVICE_ERROR_NORMAL = 0x00000001
+ SERVICE_ERROR_SEVERE = 0x00000002
+ SERVICE_ERROR_CRITICAL = 0x00000003
+
+ # Start types
+ SERVICE_BOOT_START = 0x00000000
+ SERVICE_SYSTEM_START = 0x00000001
+ SERVICE_AUTO_START = 0x00000002
+ SERVICE_DEMAND_START = 0x00000003
+ SERVICE_DISABLED = 0x00000004
+
+ # Service control
+
+ SERVICE_CONTROL_STOP = 0x00000001
+ SERVICE_CONTROL_PAUSE = 0x00000002
+ SERVICE_CONTROL_CONTINUE = 0x00000003
+ SERVICE_CONTROL_INTERROGATE = 0x00000004
+ SERVICE_CONTROL_SHUTDOWN = 0x00000005
+ SERVICE_CONTROL_PARAMCHANGE = 0x00000006
+ SERVICE_CONTROL_NETBINDADD = 0x00000007
+ SERVICE_CONTROL_NETBINDREMOVE = 0x00000008
+ SERVICE_CONTROL_NETBINDENABLE = 0x00000009
+ SERVICE_CONTROL_NETBINDDISABLE = 0x0000000A
+ SERVICE_CONTROL_DEVICEEVENT = 0x0000000B
+ SERVICE_CONTROL_HARDWAREPROFILECHANGE = 0x0000000C
+ SERVICE_CONTROL_POWEREVENT = 0x0000000D
+ SERVICE_CONTROL_SESSIONCHANGE = 0x0000000E
+ SERVICE_CONTROL_PRESHUTDOWN = 0x0000000F
+ SERVICE_CONTROL_TIMECHANGE = 0x00000010
+ SERVICE_CONTROL_TRIGGEREVENT = 0x00000020
+
+ # Service controls accepted
+
+ SERVICE_ACCEPT_STOP = 0x00000001
+ SERVICE_ACCEPT_PAUSE_CONTINUE = 0x00000002
+ SERVICE_ACCEPT_SHUTDOWN = 0x00000004
+ SERVICE_ACCEPT_PARAMCHANGE = 0x00000008
+ SERVICE_ACCEPT_NETBINDCHANGE = 0x00000010
+ SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 0x00000020
+ SERVICE_ACCEPT_POWEREVENT = 0x00000040
+ SERVICE_ACCEPT_SESSIONCHANGE = 0x00000080
+ SERVICE_ACCEPT_PRESHUTDOWN = 0x00000100
+ SERVICE_ACCEPT_TIMECHANGE = 0x00000200
+ SERVICE_ACCEPT_TRIGGEREVENT = 0x00000400
+
+ # Service states
+ SERVICE_ACTIVE = 0x00000001
+ SERVICE_INACTIVE = 0x00000002
+ SERVICE_STATE_ALL = 0x00000003
+
+ # Service current states
+ SERVICE_STOPPED = 0x00000001
+ SERVICE_START_PENDING = 0x00000002
+ SERVICE_STOP_PENDING = 0x00000003
+ SERVICE_RUNNING = 0x00000004
+ SERVICE_CONTINUE_PENDING = 0x00000005
+ SERVICE_PAUSE_PENDING = 0x00000006
+ SERVICE_PAUSED = 0x00000007
+
+ # Info levels
+ SERVICE_CONFIG_DESCRIPTION = 1
+ SERVICE_CONFIG_FAILURE_ACTIONS = 2
+ SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3
+ SERVICE_CONFIG_FAILURE_ACTIONS_FLAG = 4
+ SERVICE_CONFIG_SERVICE_SID_INFO = 5
+ SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO = 6
+ SERVICE_CONFIG_PRESHUTDOWN_INFO = 7
+
+ # Configuration
+ SERVICE_NO_CHANGE = 0xffffffff
+
+ # Misc
+
+ WAIT_OBJECT_0 = 0
+ WAIT_TIMEOUT = 0x00000102
+ INFINITE = 0xFFFFFFFF
+
+ IDLE_CONTROL_CODE = 0
+
+ DELETE = 0x00010000
+ FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
+ FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
+
+ NO_ERROR = 0
+
+ SE_PRIVILEGE_ENABLED = 0x00000002
+ TOKEN_ADJUST_PRIVILEGES = 0x0020
+ TOKEN_QUERY = 0x0008
+
+ # Errors
+
+ ERROR_INSUFFICIENT_BUFFER = 122
+ ERROR_MORE_DATA = 234
+ ERROR_FILE_NOT_FOUND = 2
+ ERROR_RESOURCE_TYPE_NOT_FOUND = 1813
+ ERROR_RESOURCE_NAME_NOT_FOUND = 1814
+ WAIT_FAILED = 0xFFFFFFFF
+ end
+end
diff --git a/omnibus/.kitchen.yml b/omnibus/.kitchen.yml
index 90283f8afb..56b11de3c3 100644
--- a/omnibus/.kitchen.yml
+++ b/omnibus/.kitchen.yml
@@ -11,7 +11,7 @@ driver:
cpus: 4
memory: 4096
synced_folders:
- - ['..', '/home/vagrant/chef']
+ - ['../..', '/vagrant/code']
- ['../../omnibus', '/home/vagrant/omnibus']
- ['../../omnibus-software', '/home/vagrant/omnibus-software']
@@ -24,7 +24,12 @@ provisioner:
attributes:
vagrant:
this_key_exists_so_we_have_a_vagrant_key: true
- chef_omnibus_install_options: -P angrychef
+ omnibus:
+ build_user: vagrant
+ build_user_group: vagrant
+ build_user_password: vagrant
+ product_name: angrychef
+ product_version: latest
chef_omnibus_root: /opt/angrychef
platforms:
@@ -34,10 +39,6 @@ platforms:
run_list: yum-epel::default
- name: centos-7.2
run_list: yum-epel::default
- - name: debian-6.0.8
- run_list: apt::default
- - name: debian-7.9
- run_list: apt::default
- name: debian-8.2
run_list: apt::default
- name: freebsd-9.3
@@ -68,6 +69,8 @@ platforms:
#
# KITCHEN_LOCAL_YAML=.kitchen.vmware.yml kitchen converge chefdk-macosx-109
#
+
+ # OSX
<% %w(
10.9
10.10
@@ -81,9 +84,15 @@ platforms:
- ['../../omnibus', '/Users/vagrant/omnibus']
- ['../../omnibus-software', '/Users/vagrant/omnibus-software']
<% end %>
- - name: windows-2012r2-standard
+
+ <% %w(
+ 10-enterprise
+ server-2012r2-standard
+ ).each do |win_version| %>
+ # Windows 64-bit
+ - name: windows-<%= win_version %>
driver:
- box: chef/windows-server-2012r2-standard # private
+ box: chef/windows-<%= win_version %> # private
synced_folders:
# We have to mount this repos enclosing folder as the Omnibus build
# gets cranky if the mounted Chef source folder is a symlink. This
@@ -91,12 +100,29 @@ platforms:
# at `C:\vagrant\code\chef`
- ['../..', '/vagrant/code']
provisioner:
+ attributes:
+ omnibus:
+ build_user: vagrant
+ build_user_group: Administrators
+ build_user_password: vagrant
chef_omnibus_root: /opscode/angrychef
-attribute_defaults: &attribute_defaults
- build_user: vagrant
- build_user_group: vagrant
- build_user_password: vagrant
+ # Windows 32-bit
+ # By adding an `i386` to the name the Omnibus cookbook's `load-omnibus-toolchain.bat`
+ # will load the 32-bit version of the MinGW toolchain.
+ - name: windows-<%= win_version %>-i386
+ driver:
+ box: chef/windows-<%= win_version %> # private
+ synced_folders:
+ - ['../..', '/vagrant/code']
+ provisioner:
+ attributes:
+ omnibus:
+ build_user: vagrant
+ build_user_group: Administrators
+ build_user_password: vagrant
+ chef_omnibus_root: /opscode/angrychef
+ <% end %>
suites:
# - name: angrychef
@@ -109,7 +135,6 @@ suites:
- name: chef
attributes:
omnibus:
- <<: *attribute_defaults
install_dir: /opt/chef
run_list:
- omnibus::default
diff --git a/omnibus/Gemfile b/omnibus/Gemfile
index ef1dec354d..5457f0f40b 100644
--- a/omnibus/Gemfile
+++ b/omnibus/Gemfile
@@ -1,11 +1,9 @@
source "https://rubygems.org"
-gem "omnibus", git: "https://github.com/chef/omnibus.git"
-gem "omnibus-software", git: "https://github.com/chef/omnibus-software.git"
+gem "omnibus", git: "https://github.com/chef/omnibus", branch: "master"
+gem "omnibus-software", git: "https://github.com/chef/omnibus-software", branch: "master"
-# pedump pessimistically pins multipart-post to a version from 2013 which makes
-# bundler very unhappy. Remove this when upstream has merged zed-0xff/pedump#6 .
-gem "pedump", git: "https://github.com/ksubrama/pedump.git", branch: "patch-1"
+gem "pedump"
# This development group is installed by default when you run `bundle install`,
# but if you are using Omnibus in a CI-based infrastructure, you do not need
@@ -13,12 +11,15 @@ gem "pedump", git: "https://github.com/ksubrama/pedump.git", branch: "patch-1"
# by running `bundle install --without development` to speed up build times.
group :development do
# Use Berkshelf for resolving cookbook dependencies
- gem "berkshelf", "~> 3.0"
+ gem "berkshelf", "~> 4.0"
+
+ # temp pin for chef 14 development
+ gem "ohai", "~> 13.0"
# Use Test Kitchen with Vagrant for converging the build environment
- gem "test-kitchen", "~> 1.7.1"
- gem "kitchen-vagrant", "~> 0.19.0"
- gem "winrm-fs", "~> 0.4.0"
+ gem "test-kitchen", "~> 1.13"
+ gem "kitchen-vagrant", "~> 1.3.1"
+ gem "winrm-fs", "~> 1.0"
gem "pry"
gem "pry-byebug"
gem "pry-stack_explorer"
diff --git a/omnibus/Gemfile.lock b/omnibus/Gemfile.lock
index 5214847aa1..1b241b0ed7 100644
--- a/omnibus/Gemfile.lock
+++ b/omnibus/Gemfile.lock
@@ -1,70 +1,67 @@
GIT
- remote: https://github.com/chef/omnibus-software.git
- revision: 6127be3af79941c32419228cbd9e63c4f061d76b
+ remote: https://github.com/chef/omnibus
+ revision: 7e9c9a6a71c1ada2d7f42484f13074e1ec6b7cd3
+ branch: master
specs:
- omnibus-software (4.0.0)
- omnibus (>= 5.2.0)
-
-GIT
- remote: https://github.com/chef/omnibus.git
- revision: e73722f46fbd5f302d9e1541aaae0b2f8be2110e
- specs:
- omnibus (5.3.0)
+ omnibus (5.6.12)
aws-sdk (~> 2)
chef-sugar (~> 3.3)
cleanroom (~> 1.0)
ffi-yajl (~> 2.2)
+ license_scout (~> 1.0)
mixlib-shellout (~> 2.0)
mixlib-versioning
- ohai (~> 8.0)
+ ohai (>= 8.6.0.alpha.1, < 15)
+ pedump
ruby-progressbar (~> 1.7)
thor (~> 0.18)
GIT
- remote: https://github.com/ksubrama/pedump.git
- revision: b4319556e18c80d2cba064ffe57fe0dea549dfe2
- branch: patch-1
+ remote: https://github.com/chef/omnibus-software
+ revision: 0cb3a8dfb6cebd56684c1d4609a676959389e4c5
+ branch: master
specs:
- pedump (0.5.0)
- awesome_print
- iostruct (>= 0.0.4)
- multipart-post (~> 1.2)
- progressbar
- zhexdump (>= 0.0.2)
+ omnibus-software (4.0.0)
+ chef-sugar (>= 3.4.0)
+ omnibus (>= 5.6.1)
GEM
remote: https://rubygems.org/
specs:
- addressable (2.3.8)
- artifactory (2.3.2)
- awesome_print (1.6.1)
- aws-sdk (2.2.34)
- aws-sdk-resources (= 2.2.34)
- aws-sdk-core (2.2.34)
+ addressable (2.5.2)
+ public_suffix (>= 2.0.2, < 4.0)
+ awesome_print (1.8.0)
+ aws-sdk (2.11.33)
+ aws-sdk-resources (= 2.11.33)
+ aws-sdk-core (2.11.33)
+ aws-sigv4 (~> 1.0)
jmespath (~> 1.0)
- aws-sdk-resources (2.2.34)
- aws-sdk-core (= 2.2.34)
- berkshelf (3.3.0)
- addressable (~> 2.3.4)
- berkshelf-api-client (~> 1.2)
+ aws-sdk-resources (2.11.33)
+ aws-sdk-core (= 2.11.33)
+ aws-sigv4 (1.0.2)
+ berkshelf (4.3.5)
+ addressable (~> 2.3, >= 2.3.4)
+ berkshelf-api-client (~> 2.0, >= 2.0.2)
buff-config (~> 1.0)
buff-extensions (~> 1.0)
buff-shell_out (~> 0.1)
- celluloid (~> 0.16.0)
+ celluloid (= 0.16.0)
celluloid-io (~> 0.16.1)
cleanroom (~> 1.0)
- faraday (~> 0.9.0)
- httpclient (~> 2.6.0)
- minitar (~> 0.5.4)
- octokit (~> 3.0)
+ faraday (~> 0.9)
+ httpclient (~> 2.7)
+ minitar (~> 0.5, >= 0.5.4)
+ mixlib-archive (~> 0.1)
+ octokit (~> 4.0)
retryable (~> 2.0)
- ridley (~> 4.0)
- solve (~> 1.1)
+ ridley (~> 4.5)
+ solve (~> 2.0)
thor (~> 0.19)
- berkshelf-api-client (1.3.1)
+ berkshelf-api-client (2.0.2)
faraday (~> 0.9.1)
- httpclient (~> 2.6.0)
- binding_of_caller (0.7.2)
+ httpclient (~> 2.7.0)
+ ridley (~> 4.5)
+ binding_of_caller (0.8.0)
debug_inspector (>= 0.0.1)
buff-config (1.0.1)
buff-extensions (~> 1.0)
@@ -74,194 +71,204 @@ GEM
buff-ruby_engine (0.1.0)
buff-shell_out (0.2.0)
buff-ruby_engine (~> 0.1.0)
- builder (3.2.2)
- byebug (8.2.4)
+ builder (3.2.3)
+ byebug (10.0.2)
celluloid (0.16.0)
timers (~> 4.0.0)
celluloid-io (0.16.2)
celluloid (>= 0.16.0)
nio4r (>= 1.1.0)
- chef-config (12.9.38)
- fuzzyurl (~> 0.8.0)
+ chef-config (13.8.5)
+ addressable
+ fuzzyurl
mixlib-config (~> 2.0)
mixlib-shellout (~> 2.0)
- chef-sugar (3.3.0)
+ tomlrb (~> 1.2)
+ chef-sugar (3.6.0)
+ citrus (3.0.2)
cleanroom (1.0.0)
- coderay (1.1.1)
- debug_inspector (0.0.2)
- dep-selector-libgecode (1.2.0)
- dep_selector (1.0.3)
- dep-selector-libgecode (~> 1.0)
- ffi (~> 1.9)
- diff-lcs (1.2.5)
+ coderay (1.1.2)
+ debug_inspector (0.0.3)
erubis (2.7.0)
faraday (0.9.2)
multipart-post (>= 1.2, < 3)
- ffi (1.9.10)
- ffi (1.9.10-x86-mingw32)
- ffi-yajl (2.2.3)
+ ffi (1.9.23)
+ ffi (1.9.23-x64-mingw32)
+ ffi (1.9.23-x86-mingw32)
+ ffi-yajl (2.3.1)
libyajl2 (~> 1.2)
- fuzzyurl (0.8.0)
+ fuzzyurl (0.9.0)
gssapi (1.2.0)
ffi (>= 1.0.1)
gyoku (1.3.1)
builder (>= 2.1.2)
- hashie (3.4.3)
- hitimes (1.2.3)
- hitimes (1.2.3-x86-mingw32)
- httpclient (2.6.0.1)
+ hashie (3.5.7)
+ hitimes (1.2.6)
+ hitimes (1.2.6-x86-mingw32)
+ httpclient (2.7.2)
iostruct (0.0.4)
ipaddress (0.8.3)
- jmespath (1.2.4)
- json_pure (>= 1.8.1)
- json (1.8.3)
- json_pure (1.8.3)
- kitchen-vagrant (0.19.0)
+ jmespath (1.4.0)
+ json (2.1.0)
+ kitchen-vagrant (1.3.1)
test-kitchen (~> 1.4)
libyajl2 (1.2.0)
+ license_scout (1.0.2)
+ ffi-yajl (~> 2.2)
+ mixlib-shellout (~> 2.2)
+ toml-rb (~> 1.0)
little-plugger (1.1.4)
- logging (2.1.0)
+ logging (2.2.2)
little-plugger (~> 1.1)
multi_json (~> 1.10)
- method_source (0.8.2)
- minitar (0.5.4)
- mixlib-authentication (1.4.0)
+ method_source (0.9.0)
+ minitar (0.6.1)
+ mixlib-archive (0.4.1)
mixlib-log
- rspec-core (~> 3.2)
- rspec-expectations (~> 3.2)
- rspec-mocks (~> 3.2)
- mixlib-cli (1.5.0)
- mixlib-config (2.2.1)
- mixlib-install (1.0.7)
- artifactory
+ mixlib-authentication (2.0.0)
+ mixlib-cli (1.7.0)
+ mixlib-config (2.2.6)
+ tomlrb
+ mixlib-install (3.9.3)
mixlib-shellout
mixlib-versioning
- mixlib-log (1.6.0)
- mixlib-shellout (2.2.6)
- mixlib-shellout (2.2.6-universal-mingw32)
+ thor
+ mixlib-log (1.7.1)
+ mixlib-shellout (2.3.2)
+ mixlib-shellout (2.3.2-universal-mingw32)
win32-process (~> 0.8.2)
wmi-lite (~> 1.0)
- mixlib-versioning (1.1.0)
- multi_json (1.11.2)
- multipart-post (1.2.0)
+ mixlib-versioning (1.2.2)
+ molinillo (0.4.5)
+ multi_json (1.13.1)
+ multipart-post (2.0.0)
net-scp (1.2.1)
net-ssh (>= 2.6.5)
- net-ssh (3.1.1)
- nio4r (1.2.1)
+ net-ssh (4.2.0)
+ net-ssh-gateway (1.3.0)
+ net-ssh (>= 2.6.5)
+ nio4r (2.3.0)
nori (2.6.0)
- octokit (3.8.0)
- sawyer (~> 0.6.0, >= 0.5.3)
- ohai (8.14.0)
- chef-config (>= 12.5.0.alpha.1, < 13)
+ octokit (4.8.0)
+ sawyer (~> 0.8.0, >= 0.5.3)
+ ohai (13.9.0)
+ chef-config (>= 12.5.0.alpha.1, < 14)
ffi (~> 1.9)
ffi-yajl (~> 2.2)
ipaddress
mixlib-cli
mixlib-config (~> 2.0)
- mixlib-log
+ mixlib-log (>= 1.7.1, < 2.0)
mixlib-shellout (~> 2.0)
plist (~> 3.1)
systemu (~> 2.6.4)
wmi-lite (~> 1.0)
- plist (3.2.0)
- progressbar (0.21.0)
- pry (0.10.3)
+ pedump (0.5.2)
+ awesome_print
+ iostruct (>= 0.0.4)
+ multipart-post (~> 2.0.0)
+ progressbar
+ zhexdump (>= 0.0.2)
+ plist (3.4.0)
+ progressbar (1.9.0)
+ pry (0.11.3)
coderay (~> 1.1.0)
- method_source (~> 0.8.1)
- slop (~> 3.4)
- pry-byebug (3.3.0)
- byebug (~> 8.0)
+ method_source (~> 0.9.0)
+ pry-byebug (3.6.0)
+ byebug (~> 10.0)
pry (~> 0.10)
pry-stack_explorer (0.4.9.2)
binding_of_caller (>= 0.7)
pry (>= 0.9.11)
- retryable (2.0.3)
- ridley (4.4.2)
+ public_suffix (3.0.2)
+ retryable (2.0.4)
+ ridley (4.6.1)
addressable
buff-config (~> 1.0)
buff-extensions (~> 1.0)
- buff-ignore (~> 1.1)
+ buff-ignore (~> 1.1.1)
buff-shell_out (~> 0.1)
celluloid (~> 0.16.0)
celluloid-io (~> 0.16.1)
- chef-config
+ chef-config (>= 12.5.0)
erubis
faraday (~> 0.9.0)
hashie (>= 2.0.2, < 4.0.0)
- httpclient (~> 2.6)
+ httpclient (~> 2.7)
json (>= 1.7.7)
mixlib-authentication (>= 1.3.0)
retryable (~> 2.0)
semverse (~> 1.1)
varia_model (~> 0.4.0)
- rspec-core (3.4.4)
- rspec-support (~> 3.4.0)
- rspec-expectations (3.4.0)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.4.0)
- rspec-mocks (3.4.1)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.4.0)
- rspec-support (3.4.1)
- ruby-progressbar (1.7.5)
- rubyntlm (0.6.0)
- rubyzip (1.2.0)
- safe_yaml (1.0.4)
- sawyer (0.6.0)
- addressable (~> 2.3.5)
- faraday (~> 0.8, < 0.10)
+ ruby-progressbar (1.9.0)
+ rubyntlm (0.6.2)
+ rubyzip (1.2.1)
+ sawyer (0.8.1)
+ addressable (>= 2.3.5, < 2.6)
+ faraday (~> 0.8, < 1.0)
semverse (1.2.1)
- slop (3.6.0)
- solve (1.2.1)
- dep_selector (~> 1.0)
+ solve (2.0.3)
+ molinillo (~> 0.4.2)
semverse (~> 1.1)
systemu (2.6.5)
- test-kitchen (1.7.3)
- mixlib-install (~> 1.0, >= 1.0.4)
+ test-kitchen (1.21.0)
+ mixlib-install (~> 3.6)
mixlib-shellout (>= 1.2, < 3.0)
net-scp (~> 1.1)
- net-ssh (>= 2.9, < 4.0)
- safe_yaml (~> 1.0)
- thor (~> 0.18)
+ net-ssh (>= 2.9, < 5.0)
+ net-ssh-gateway (~> 1.2)
+ thor (~> 0.19, < 0.19.2)
+ winrm (~> 2.0)
+ winrm-elevated (~> 1.0)
+ winrm-fs (~> 1.1)
thor (0.19.1)
timers (4.0.4)
hitimes
+ toml-rb (1.1.1)
+ citrus (~> 3.0, > 3.0)
+ tomlrb (1.2.6)
varia_model (0.4.1)
buff-extensions (~> 1.0)
hashie (>= 2.0.2, < 4.0.0)
win32-process (0.8.3)
ffi (>= 1.0.0)
- winrm (1.7.3)
+ winrm (2.2.3)
builder (>= 2.1.2)
+ erubis (~> 2.7)
gssapi (~> 1.2)
gyoku (~> 1.0)
httpclient (~> 2.2, >= 2.2.0.2)
logging (>= 1.6.1, < 3.0)
nori (~> 2.0)
- rubyntlm (~> 0.6.0)
- winrm-fs (0.4.2)
+ rubyntlm (~> 0.6.0, >= 0.6.1)
+ winrm-elevated (1.1.0)
+ winrm (~> 2.0)
+ winrm-fs (~> 1.0)
+ winrm-fs (1.2.0)
erubis (~> 2.7)
logging (>= 1.6.1, < 3.0)
rubyzip (~> 1.1)
- winrm (~> 1.5)
+ winrm (~> 2.0)
wmi-lite (1.0.0)
zhexdump (0.0.2)
PLATFORMS
ruby
+ x64-mingw32
x86-mingw32
DEPENDENCIES
- berkshelf (~> 3.0)
- kitchen-vagrant (~> 0.19.0)
+ berkshelf (~> 4.0)
+ kitchen-vagrant (~> 1.3.1)
+ ohai (~> 13.0)
omnibus!
omnibus-software!
- pedump!
+ pedump
pry
pry-byebug
pry-stack_explorer
- test-kitchen (~> 1.7.1)
- winrm-fs (~> 0.4.0)
+ test-kitchen (~> 1.13)
+ winrm-fs (~> 1.0)
BUNDLED WITH
- 1.11.2
+ 1.16.1
diff --git a/omnibus/README.md b/omnibus/README.md
index 98a9094325..5f325aa164 100644
--- a/omnibus/README.md
+++ b/omnibus/README.md
@@ -75,7 +75,7 @@ changing the list found in the `.kitchen.yml` `platforms` YAML stanza.
This build environment is designed to get you up-and-running quickly. However,
there is nothing that restricts you to building on other platforms. Simply use
-the [omnibus cookbook](https://github.com/opscode-cookbooks/omnibus) to setup
+the [omnibus cookbook](https://github.com/chef-cookbooks/omnibus) to setup
your desired platform and execute the build steps listed above.
The default build environment requires Test Kitchen and VirtualBox for local
diff --git a/omnibus/config/projects/angrychef.rb b/omnibus/config/projects/angrychef.rb
index 48902e6607..ff83f41327 100644
--- a/omnibus/config/projects/angrychef.rb
+++ b/omnibus/config/projects/angrychef.rb
@@ -21,7 +21,7 @@
#
current_file = __FILE__
chef_project_contents = IO.read(File.expand_path("../chef.rb", __FILE__))
-self.instance_eval chef_project_contents
+instance_eval chef_project_contents
name "angrychef"
friendly_name "Angry Chef Client"
diff --git a/omnibus/config/projects/chef.rb b/omnibus/config/projects/chef.rb
index 561dea8901..10032b44a4 100644
--- a/omnibus/config/projects/chef.rb
+++ b/omnibus/config/projects/chef.rb
@@ -1,5 +1,5 @@
#
-# Copyright 2012-2016, Chef Software, Inc.
+# Copyright 2012-2017, Chef Software Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -39,25 +39,52 @@ else
install_dir "#{default_root}/#{name}"
end
-# Global FIPS override flag.
-if windows? || rhel?
- override :fips, enabled: true
-end
+override :chef, version: "local_source"
# Load dynamically updated overrides
overrides_path = File.expand_path("../../../../omnibus_overrides.rb", current_file)
instance_eval(IO.read(overrides_path), overrides_path)
-override :"ruby-windows-devkit", version: "4.5.2-20111229-1559" if windows? && windows_arch_i386?
-
dependency "preparation"
-# All actual dependencies are in chef-complete, so that the addition
-# or removal of a dependency doesn't dirty the entire project file
-dependency "chef-complete"
+# InSpec 2 depends on unf_ext, which doesn't currently build on solaris on aix. There exists a fork
+# of unf_ext which fixes this, so let's use that in Chef for now.
+# FIXME: must remove this ASAP.
+dependency "unf_ext"
+
+dependency "chef"
+
+#
+# addons which require omnibus software defns (not direct deps of chef itself - RFC-063)
+#
+dependency "nokogiri" # (nokogiri cannot go in the Gemfile, see wall of text in the software defn)
+
+# FIXME?: might make sense to move dependencies below into the omnibus-software chef
+# definition or into a chef-complete definition added to omnibus-software.
+dependency "gem-permissions"
+dependency "shebang-cleanup"
+dependency "version-manifest"
+dependency "openssl-customization"
+
+# devkit needs to come dead last these days so we do not use it to compile any gems
+if windows?
+ override :"ruby-windows-devkit", version: "4.5.2-20111229-1559" if windows_arch_i386?
+ dependency "ruby-windows-devkit"
+ dependency "ruby-windows-devkit-bash"
+end
package :rpm do
signing_passphrase ENV["OMNIBUS_RPM_SIGNING_PASSPHRASE"]
+
+ unless rhel? && platform_version.satisfies?("< 6")
+ compression_level 1
+ compression_type :xz
+ end
+end
+
+package :deb do
+ compression_level 1
+ compression_type :xz
end
proj_to_work_around_cleanroom = self
@@ -74,7 +101,11 @@ package :msi do
upgrade_code msi_upgrade_code
wix_candle_extension "WixUtilExtension"
wix_light_extension "WixUtilExtension"
- signing_identity "F74E1A68005E8A9C465C3D2FF7B41F3988F0EA09", machine_store: true
+ signing_identity "E05FF095D07F233B78EB322132BFF0F035E11B5B", machine_store: true
parameters ChefLogDllPath: windows_safe_path(gem_path("chef-[0-9]*-mingw32/ext/win32-eventlog/chef-log.dll")),
ProjectLocationDir: project_location_dir
end
+
+package :appx do
+ signing_identity "E05FF095D07F233B78EB322132BFF0F035E11B5B", machine_store: true
+end
diff --git a/omnibus/config/software/chef-appbundle.rb b/omnibus/config/software/chef-appbundle.rb
deleted file mode 100644
index 19228738c3..0000000000
--- a/omnibus/config/software/chef-appbundle.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-name "chef-appbundle"
-default_version "local_source"
-source path: project.files_path
-
-dependency "chef"
-
-build do
- # This is where we get the definitions below
- require_relative "../../files/chef-appbundle/build-chef-appbundle"
- extend BuildChefAppbundle
-
- appbundle_gem "chef"
- appbundle_gem "ohai"
-end
diff --git a/omnibus/config/software/chef-complete.rb b/omnibus/config/software/chef-complete.rb
deleted file mode 100644
index 46fc8046e4..0000000000
--- a/omnibus/config/software/chef-complete.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-name "chef-complete"
-
-license :project_license
-
-dependency "chef"
-dependency "chef-appbundle"
-dependency "chef-remove-docs"
-
-dependency "shebang-cleanup"
-dependency "version-manifest"
-dependency "openssl-customization"
-
-if windows?
- # TODO can this be safely moved to before the chef?
- # It would make caching better ...
- dependency "ruby-windows-devkit"
- dependency "ruby-windows-devkit-bash"
-end
-
-dependency "clean-static-libs"
diff --git a/omnibus/config/software/chef-gem-binding_of_caller.rb b/omnibus/config/software/chef-gem-binding_of_caller.rb
deleted file mode 100644
index a9f8b758c9..0000000000
--- a/omnibus/config/software/chef-gem-binding_of_caller.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# gem installs this gem from the version specified in chef's Gemfile.lock
-# so we can take advantage of omnibus's caching. Just duplicate this file and
-# add the new software def to chef software def if you want to separate
-# another gem's installation.
-require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def"
-BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
diff --git a/omnibus/config/software/chef-gem-byebug.rb b/omnibus/config/software/chef-gem-byebug.rb
deleted file mode 100644
index a9f8b758c9..0000000000
--- a/omnibus/config/software/chef-gem-byebug.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# gem installs this gem from the version specified in chef's Gemfile.lock
-# so we can take advantage of omnibus's caching. Just duplicate this file and
-# add the new software def to chef software def if you want to separate
-# another gem's installation.
-require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def"
-BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
diff --git a/omnibus/config/software/chef-gem-debug_inspector.rb b/omnibus/config/software/chef-gem-debug_inspector.rb
deleted file mode 100644
index a9f8b758c9..0000000000
--- a/omnibus/config/software/chef-gem-debug_inspector.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# gem installs this gem from the version specified in chef's Gemfile.lock
-# so we can take advantage of omnibus's caching. Just duplicate this file and
-# add the new software def to chef software def if you want to separate
-# another gem's installation.
-require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def"
-BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
diff --git a/omnibus/config/software/chef-gem-ffi-yajl.rb b/omnibus/config/software/chef-gem-ffi-yajl.rb
deleted file mode 100644
index e8279f7d81..0000000000
--- a/omnibus/config/software/chef-gem-ffi-yajl.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# gem installs this gem from the version specified in chef's Gemfile.lock
-# so we can take advantage of omnibus's caching. Just duplicate this file and
-# add the new software def to chef software def if you want to separate
-# another gem's installation.
-require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def"
-BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
-
-dependency "chef-gem-libyajl2"
diff --git a/omnibus/config/software/chef-gem-ffi.rb b/omnibus/config/software/chef-gem-ffi.rb
deleted file mode 100644
index a9f8b758c9..0000000000
--- a/omnibus/config/software/chef-gem-ffi.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# gem installs this gem from the version specified in chef's Gemfile.lock
-# so we can take advantage of omnibus's caching. Just duplicate this file and
-# add the new software def to chef software def if you want to separate
-# another gem's installation.
-require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def"
-BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
diff --git a/omnibus/config/software/chef-gem-json.rb b/omnibus/config/software/chef-gem-json.rb
deleted file mode 100644
index a9f8b758c9..0000000000
--- a/omnibus/config/software/chef-gem-json.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# gem installs this gem from the version specified in chef's Gemfile.lock
-# so we can take advantage of omnibus's caching. Just duplicate this file and
-# add the new software def to chef software def if you want to separate
-# another gem's installation.
-require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def"
-BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
diff --git a/omnibus/config/software/chef-gem-libyajl2.rb b/omnibus/config/software/chef-gem-libyajl2.rb
deleted file mode 100644
index a9f8b758c9..0000000000
--- a/omnibus/config/software/chef-gem-libyajl2.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# gem installs this gem from the version specified in chef's Gemfile.lock
-# so we can take advantage of omnibus's caching. Just duplicate this file and
-# add the new software def to chef software def if you want to separate
-# another gem's installation.
-require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def"
-BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
diff --git a/omnibus/config/software/chef-gem-mini_portile2.rb b/omnibus/config/software/chef-gem-mini_portile2.rb
deleted file mode 100644
index a9f8b758c9..0000000000
--- a/omnibus/config/software/chef-gem-mini_portile2.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# gem installs this gem from the version specified in chef's Gemfile.lock
-# so we can take advantage of omnibus's caching. Just duplicate this file and
-# add the new software def to chef software def if you want to separate
-# another gem's installation.
-require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def"
-BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
diff --git a/omnibus/config/software/chef-gem-nokogiri.rb b/omnibus/config/software/chef-gem-nokogiri.rb
deleted file mode 100644
index a18d156e11..0000000000
--- a/omnibus/config/software/chef-gem-nokogiri.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# gem installs this gem from the version specified in chef's Gemfile.lock
-# so we can take advantage of omnibus's caching. Just duplicate this file and
-# add the new software def to chef software def if you want to separate
-# another gem's installation.
-require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def"
-BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
-
-dependency "chef-gem-mini_portile2"
diff --git a/omnibus/config/software/chef-gem-ruby-prof.rb b/omnibus/config/software/chef-gem-ruby-prof.rb
deleted file mode 100644
index a9f8b758c9..0000000000
--- a/omnibus/config/software/chef-gem-ruby-prof.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# gem installs this gem from the version specified in chef's Gemfile.lock
-# so we can take advantage of omnibus's caching. Just duplicate this file and
-# add the new software def to chef software def if you want to separate
-# another gem's installation.
-require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def"
-BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
diff --git a/omnibus/config/software/chef-gem-ruby-shadow.rb b/omnibus/config/software/chef-gem-ruby-shadow.rb
deleted file mode 100644
index a9f8b758c9..0000000000
--- a/omnibus/config/software/chef-gem-ruby-shadow.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# gem installs this gem from the version specified in chef's Gemfile.lock
-# so we can take advantage of omnibus's caching. Just duplicate this file and
-# add the new software def to chef software def if you want to separate
-# another gem's installation.
-require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def"
-BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
diff --git a/omnibus/config/software/chef-remove-docs.rb b/omnibus/config/software/chef-remove-docs.rb
deleted file mode 100644
index 2e71e63792..0000000000
--- a/omnibus/config/software/chef-remove-docs.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# Copyright 2012-2014 Chef Software, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-name "chef-remove-docs"
-
-license :project_license
-
-build do
- # This is where we get the definitions below
- require_relative "../../files/chef/build-chef"
- extend BuildChef
-
- delete "#{install_dir}/embedded/docs"
- delete "#{install_dir}/embedded/share/man"
- delete "#{install_dir}/embedded/share/doc"
- delete "#{install_dir}/embedded/share/gtk-doc"
- delete "#{install_dir}/embedded/ssl/man"
- delete "#{install_dir}/embedded/man"
- delete "#{install_dir}/embedded/info"
-end
diff --git a/omnibus/config/software/chef.rb b/omnibus/config/software/chef.rb
deleted file mode 100644
index 49db136e94..0000000000
--- a/omnibus/config/software/chef.rb
+++ /dev/null
@@ -1,90 +0,0 @@
-name "chef"
-default_version "local_source"
-
-license :project_license
-
-# For the specific super-special version "local_source", build the source from
-# the local git checkout. This is what you'd want to occur by default if you
-# just ran omnibus build locally.
-version("local_source") do
- source path: File.expand_path("../..", project.files_path),
- # Since we are using the local repo, we try to not copy any files
- # that are generated in the process of bundle installing omnibus.
- # If the install steps are well-behaved, this should not matter
- # since we only perform bundle and gem installs from the
- # omnibus cache source directory, but we do this regardless
- # to maintain consistency between what a local build sees and
- # what a github based build will see.
- options: { exclude: [ "omnibus/vendor" ] }
-end
-
-# For any version other than "local_source", fetch from github.
-if version != "local_source"
- source git: "git://github.com/chef/chef.git"
-end
-
-# For nokogiri
-dependency "libxml2"
-dependency "libxslt"
-dependency "libiconv"
-dependency "liblzma"
-dependency "zlib"
-
-# ruby and bundler and friends
-dependency "ruby"
-dependency "rubygems"
-dependency "bundler"
-
-# Install all the native gems separately
-# Worst offenders first to take best advantage of cache:
-dependency "chef-gem-ffi-yajl"
-dependency "chef-gem-nokogiri"
-dependency "chef-gem-libyajl2"
-dependency "chef-gem-ruby-prof"
-dependency "chef-gem-byebug"
-dependency "chef-gem-debug_inspector"
-dependency "chef-gem-binding_of_caller"
-
-# Now everyone else, in alphabetical order because we don't care THAT much
-Dir.entries(File.dirname(__FILE__)).sort.each do |gem_software|
- if gem_software =~ /^(chef-gem-.+)\.rb$/
- dependency $1
- end
-end
-
-build do
- # This is where we get the definitions below
- require_relative "../../files/chef/build-chef"
- extend BuildChef
-
- project_env = env.dup
- project_env["BUNDLE_GEMFILE"] = project_gemfile
-
- # Prepare to install: build config, retries, job, frozen=true
- # TODO Windows install seems to sometimes install already-installed gems such
- # as gherkin (and fail as a result) if you use jobs > 1.
- create_bundle_config(project_gemfile, retries: 4, jobs: windows? ? 1 : 7, frozen: true)
-
- # Install all the things. Arguments are specified in .bundle/config (see create_bundle_config)
- block { log.info(log_key) { "" } }
- bundle "install --verbose", env: project_env
-
- # For whatever reason, nokogiri software def deletes this (rather small) directory
- block { log.info(log_key) { "" } }
- block "Remove mini_portile test dir" do
- mini_portile = shellout!("#{bundle_bin} show mini_portile").stdout.chomp
- remove_directory File.join(mini_portile, "test")
- end
-
- # Check that it worked
- block { log.info(log_key) { "" } }
- bundle "check", env: project_env
-
- # fix up git-sourced gems
- properly_reinstall_git_and_path_sourced_gems
- install_shared_gemfile
-
- # Check that the final gemfile worked
- block { log.info(log_key) { "" } }
- bundle "check", env: env, cwd: File.dirname(shared_gemfile)
-end
diff --git a/omnibus/config/software/unf_ext.rb b/omnibus/config/software/unf_ext.rb
new file mode 100644
index 0000000000..269fa933b7
--- /dev/null
+++ b/omnibus/config/software/unf_ext.rb
@@ -0,0 +1,23 @@
+# encoding: utf-8
+# override for unf_ext until
+# https://github.com/knu/ruby-unf_ext/pull/39
+# is merged and released
+
+name "unf_ext"
+
+dependency "ruby"
+dependency "rubygems"
+dependency "bundler"
+dependency "appbundler"
+
+license :project_license
+default_version "c0b3bd922214a172976f6f368c0b4e4fbf91ed78"
+source git: "https://github.com/jquick/ruby-unf_ext.git"
+skip_transitive_dependency_licensing true
+
+build do
+ env = with_standard_compiler_flags(with_embedded_path)
+ delete "#{name}-*.gem"
+ gem "build #{name}.gemspec", env: env
+ gem "install #{name}-*.gem --no-document", env: env
+end
diff --git a/omnibus/files/chef-appbundle/build-chef-appbundle.rb b/omnibus/files/chef-appbundle/build-chef-appbundle.rb
deleted file mode 100644
index eaf4904501..0000000000
--- a/omnibus/files/chef-appbundle/build-chef-appbundle.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-require_relative "../chef-gem/build-chef-gem"
-
-module BuildChefAppbundle
- include BuildChefGem
-
- def lockdown_gem(gem_name)
- shared_gemfile = self.shared_gemfile
-
- # Update the Gemfile to restrict to built versions so that bundle installs
- # will do the right thing
- block "Lock down the #{gem_name} gem" do
- installed_path = shellout!("#{bundle_bin} show #{gem_name}", env: env, cwd: install_dir).stdout.chomp
- installed_gemfile = File.join(installed_path, "Gemfile")
-
- #
- # Include the main distribution Gemfile in the gem's Gemfile
- #
- # NOTE: if this fails and the build retries, you will see this multiple
- # times in the file.
- #
- distribution_gemfile = Pathname(shared_gemfile).relative_path_from(Pathname(installed_gemfile)).to_s
- gemfile_text = <<-EOM.gsub(/^\s+/, "")
- # Lock gems that are part of the distribution
- distribution_gemfile = File.expand_path(#{distribution_gemfile.inspect}, __FILE__)
- instance_eval(IO.read(distribution_gemfile), distribution_gemfile)
- EOM
- gemfile_text << IO.read(installed_gemfile)
- create_file(installed_gemfile) { gemfile_text }
-
- # Remove the gemfile.lock
- remove_file("#{installed_gemfile}.lock") if File.exist?("#{installed_gemfile}.lock")
-
- # If it's frozen, make it not be.
- shellout!("#{bundle_bin} config --delete frozen")
-
- # This could be changed to `bundle install` if we wanted to actually
- # install extra deps out of their gemfile ...
- shellout!("#{bundle_bin} lock", env: env, cwd: installed_path)
- # bundle lock doesn't always tell us when it fails, so we have to check :/
- unless File.exist?("#{installed_gemfile}.lock")
- raise "bundle lock failed: no #{installed_gemfile}.lock created!"
- end
-
- # Ensure all the gems we need are actually installed (if the bundle adds
- # something, we need to know about it so we can include it in the main
- # solve).
- # Save bundle config and modify to use --without development before checking
- bundle_config = File.expand_path("../.bundle/config", installed_gemfile)
- orig_config = IO.read(bundle_config) if File.exist?(bundle_config)
- # "test", "changelog" and "guard" come from berkshelf, "maintenance" comes from chef
- # "tools" and "integration" come from inspec
- shellout!("#{bundle_bin} config --local without #{without_groups.join(":")}", env: env, cwd: installed_path)
- shellout!("#{bundle_bin} config --local frozen 1")
-
- shellout!("#{bundle_bin} check", env: env, cwd: installed_path)
-
- # Restore bundle config
- if orig_config
- create_file(bundle_config) { orig_config }
- else
- remove_file bundle_config
- end
- end
- end
-
- # appbundle the gem, making /opt/chef/bin/<binary> do the superfast pinning
- # thing.
- #
- # To protect the app from loading the wrong versions of things, it uses
- # appbundler against the resulting file.
- #
- # Relocks the Gemfiles inside the specified gems (e.g. berkshelf, test-kitchen,
- # chef) to use the distribution's chosen gems.
- def appbundle_gem(gem_name)
- # First lock the gemfile down.
- lockdown_gem(gem_name)
-
- shared_gemfile = self.shared_gemfile
-
- # Ensure the main bin dir exists
- bin_dir = File.join(install_dir, "bin")
- mkdir(bin_dir)
-
- block "Lock down the #{gem_name} gem" do
- installed_path = shellout!("#{bundle_bin} show #{gem_name}", env: env, cwd: install_dir).stdout.chomp
-
- # appbundle the gem
- appbundler_args = [ installed_path, bin_dir, gem_name ]
- appbundler_args = appbundler_args.map { |a| ::Shellwords.escape(a) }
- shellout!("#{appbundler_bin} #{appbundler_args.join(" ")}", env: env, cwd: installed_path)
- end
- end
-end
diff --git a/omnibus/files/chef-gem/build-chef-gem.rb b/omnibus/files/chef-gem/build-chef-gem.rb
deleted file mode 100644
index 701461b01e..0000000000
--- a/omnibus/files/chef-gem/build-chef-gem.rb
+++ /dev/null
@@ -1,123 +0,0 @@
-require "shellwords"
-require "pathname"
-require "bundler"
-require_relative "../../../version_policy"
-
-# Common definitions and helpers (like compile environment and binary
-# locations) for all software definitions.
-module BuildChefGem
- PLATFORM_FAMILY_FAMILIES = {
- "linux" => %w{wrlinux debian fedora rhel suse gentoo slackware arch exherbo alpine},
- "bsd" => %w{dragonflybsd freebsd netbsd openbsd},
- "solaris" => %w{smartos omnios openindiana opensolaris solaris2 nextentacore},
- "aix" => %w{aix},
- "windows" => %w{windows},
- "mac_os_x" => %w{mac_os_x},
- }
- def platform_family_families
- PLATFORM_FAMILY_FAMILIES.keys
- end
-
- def platform_family_family
- PLATFORM_FAMILY_FAMILIES.
- select { |key, families| families.include?(Omnibus::Ohai["platform_family"]) }.
- first[0]
- end
-
- def embedded_bin(binary)
- windows_safe_path("#{install_dir}/embedded/bin/#{binary}")
- end
-
- def appbundler_bin
- embedded_bin("appbundler")
- end
-
- def bundle_bin
- embedded_bin("bundle")
- end
-
- def gem_bin
- embedded_bin("gem")
- end
-
- def rake_bin
- embedded_bin("rake")
- end
-
- def without_groups
- # Add --without for every known OS except the one we're in.
- exclude_os_groups = platform_family_families - [ platform_family_family ]
- (INSTALL_WITHOUT_GROUPS + exclude_os_groups).map { |g| g.to_sym }
- end
-
- #
- # Get the path to the top level shared Gemfile included by all individual
- # Gemfiles
- #
- def shared_gemfile
- File.join(install_dir, "Gemfile")
- end
-
- # A common env for building everything including nokogiri and dep-selector-libgecode
- def env
- env = with_standard_compiler_flags(with_embedded_path, bfd_flags: true)
-
- # From dep-selector-libgecode
- # On some RHEL-based systems, the default GCC that's installed is 4.1. We
- # need to use 4.4, which is provided by the gcc44 and gcc44-c++ packages.
- # These do not use the gcc binaries so we set the flags to point to the
- # correct version here.
- if File.exist?("/usr/bin/gcc44")
- env["CC"] = "gcc44"
- env["CXX"] = "g++44"
- end
-
- # From dep-selector-libgecode
- # Ruby DevKit ships with BSD Tar
- env["PROG_TAR"] = "bsdtar" if windows?
- env["ARFLAGS"] = "rv #{env["ARFLAGS"]}" if env["ARFLAGS"]
-
- # Set up nokogiri environment and args
- env["NOKOGIRI_USE_SYSTEM_LIBRARIES"] = "true"
- env
- end
-
- #
- # Install arguments for various gems (to be passed to `gem install` or set in
- # `bundle config build.<gemname>`).
- #
- def all_install_args
- @all_install_args = {
- "nokogiri" => %W{
- --use-system-libraries
- --with-xml2-lib=#{Shellwords.escape("#{install_dir}/embedded/lib")}
- --with-xml2-include=#{Shellwords.escape("#{install_dir}/embedded/include/libxml2")}
- --with-xslt-lib=#{Shellwords.escape("#{install_dir}/embedded/lib")}
- --with-xslt-include=#{Shellwords.escape("#{install_dir}/embedded/include/libxslt")}
- --with-iconv-dir=#{Shellwords.escape("#{install_dir}/embedded")}
- --with-zlib-dir=#{Shellwords.escape("#{install_dir}/embedded")}
- }.join(" "),
- }
- end
-
- # gem install arguments for a particular gem. "" if no special args.
- def install_args_for(gem_name)
- all_install_args[gem_name] || ""
- end
-
- # Give block all the variables
- def block(*args, &block)
- super do
- extend BuildChefGem
- instance_eval(&block)
- end
- end
-
- # Give build all the variables
- def build(*args, &block)
- super do
- extend BuildChefGem
- instance_eval(&block)
- end
- end
-end
diff --git a/omnibus/files/chef-gem/build-chef-gem/gem-install-software-def.rb b/omnibus/files/chef-gem/build-chef-gem/gem-install-software-def.rb
deleted file mode 100644
index 3022bf448e..0000000000
--- a/omnibus/files/chef-gem/build-chef-gem/gem-install-software-def.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-require "bundler"
-require "omnibus"
-require_relative "../build-chef-gem"
-require_relative "../../../../tasks/gemfile_util"
-
-module BuildChefGem
- class GemInstallSoftwareDef
- def self.define(software, software_filename)
- new(software, software_filename).send(:define)
- end
-
- include BuildChefGem
- include Omnibus::Logging
-
- protected
-
- def initialize(software, software_filename)
- @software = software
- @software_filename = software_filename
- end
-
- attr_reader :software, :software_filename
-
- def define
- software.name "#{File.basename(software_filename)[0..-4]}"
- software.default_version gem_version
-
- # If the source directory for building stuff changes, tell omnibus to
- # de-cache us
- software.source path: File.expand_path("../..", __FILE__)
-
- # ruby and bundler and friends
- software.dependency "ruby"
- software.dependency "rubygems"
-
- gem_name = self.gem_name
- gem_version = self.gem_version
- gem_metadata = self.gem_metadata
- lockfile_path = self.lockfile_path
-
- software.build do
- extend BuildChefGem
-
- if gem_version == "<skip>"
- if gem_metadata
- block do
- log.info(log_key) { "#{gem_name} has source #{gem_metadata} in #{lockfile_path}. We only cache rubygems.org installs in omnibus to keep things simple. The chef step will build #{gem_name} ..." }
- end
- else
- block do
- log.info(log_key) { "#{gem_name} is not in the #{lockfile_path}. This can happen if your OS doesn't build it, or if chef no longer depends on it. Skipping ..." }
- end
- end
- else
- block do
- log.info(log_key) { "Found version #{gem_version} of #{gem_name} in #{lockfile_path}. Building early to take advantage of omnibus caching ..." }
- end
- gem "install #{gem_name} -v #{gem_version} --no-doc --no-ri --ignore-dependencies --verbose -- #{install_args_for(gem_name)}", env: env
- end
- end
- end
-
- # Path above omnibus (where Gemfile is)
- def root_path
- File.expand_path("../../../../..", __FILE__)
- end
-
- def gemfile_path
- File.join(root_path, "Gemfile")
- end
-
- def lockfile_path
- "#{gemfile_path}.lock"
- end
-
- def gem_name
- @gem_name ||= begin
- # File must be named chef-<gemname>.rb
- # Will look at chef/Gemfile.lock and install that version of the gem using "gem install"
- # (and only that version)
- if File.basename(software_filename) =~ /^chef-gem-(.+)\.rb$/
- $1
- else
- raise "#{software_filename} must be named chef-<gemname>.rb to build a gem automatically"
- end
- end
- end
-
- def gem_metadata
- @gem_metadata ||= begin
- bundle = GemfileUtil::Bundle.parse(gemfile_path, lockfile_path)
- result = bundle.gems[gem_name]
- if result
- if bundle.select_gems(without_groups: without_groups).include?(gem_name)
- log.info(software.name) { "Using #{gem_name} version #{result[:version]} from #{gemfile_path}" }
- result
- else
- log.info(software.name) { "#{gem_name} not loaded from #{gemfile_path} because it was only in groups #{without_groups.join(", ")}. Skipping ..." }
- nil
- end
- else
- log.info(software.name) { "#{gem_name} was not found in #{lockfile_path}. Skipping ..." }
- nil
- end
- end
- end
-
- def gem_version
- @gem_version ||= begin
- if gem_metadata && URI(gem_metadata[:source]) == URI("https://rubygems.org/")
- gem_metadata[:version]
- else
- "<skip>"
- end
- end
- end
- end
-end
diff --git a/omnibus/files/chef/build-chef.rb b/omnibus/files/chef/build-chef.rb
deleted file mode 100644
index 6ff9ca755a..0000000000
--- a/omnibus/files/chef/build-chef.rb
+++ /dev/null
@@ -1,134 +0,0 @@
-require "shellwords"
-require "pathname"
-require "bundler"
-require_relative "../chef-gem/build-chef-gem"
-require_relative "../../../version_policy"
-
-# We use this to break up the `build` method into readable parts
-module BuildChef
- include BuildChefGem
-
- def create_bundle_config(gemfile, without: without_groups, retries: nil, jobs: nil, frozen: nil)
- bundle_config = File.expand_path("../.bundle/config", gemfile)
-
- block "Put build config into #{bundle_config}: #{ { without: without, retries: retries, jobs: jobs, frozen: frozen } }" do
- # bundle config build.nokogiri #{nokogiri_build_config} messes up the line,
- # so we write it directly ourselves.
- new_bundle_config = "---\n"
- new_bundle_config << "BUNDLE_WITHOUT: #{Array(without).join(":")}\n" if without
- new_bundle_config << "BUNDLE_RETRY: #{retries}\n" if retries
- new_bundle_config << "BUNDLE_JOBS: #{jobs}\n" if jobs
- new_bundle_config << "BUNDLE_FROZEN: '1'\n" if frozen
- all_install_args.each do |gem_name, install_args|
- new_bundle_config << "BUNDLE_BUILD__#{gem_name.upcase}: #{install_args}\n"
- end
- create_file(bundle_config) { new_bundle_config }
- end
- end
-
- #
- # Get the (possibly platform-specific) path to the Gemfile.
- #
- def project_gemfile
- File.join(project_dir, "Gemfile")
- end
-
- #
- # Some gems we installed don't end up in the `gem list` due to the fact that
- # they have git sources (`gem 'chef', github: 'chef/chef'`) or paths (`gemspec`
- # or `gem 'chef-config', path: 'chef-config'`). To get them in there, we need
- # to go through these gems, run `rake install` from their top level, and
- # then delete the git cached versions.
- #
- # Once we finish with all this, we update the Gemfile that will end up in the
- # top-level install so that it doesn't have git or path references anymore.
- #
- def properly_reinstall_git_and_path_sourced_gems
- # Emit blank line to separate different tasks
- block { log.info(log_key) { "" } }
- project_env = env.dup.merge("BUNDLE_GEMFILE" => project_gemfile)
-
- # Reinstall git-sourced or path-sourced gems, and delete the originals
- block "Reinstall git-sourced gems properly" do
- # Grab info about the gem environment so we can make decisions
- gemdir = shellout!("#{gem_bin} environment gemdir", env: env).stdout.chomp
- gem_install_dir = File.join(gemdir, "gems")
-
- # bundle list --paths gets us the list of gem install paths. Get the ones
- # that are installed local (git and path sources like `gem :x, github: 'chef/x'`
- # or `gem :x, path: '.'` or `gemspec`). To do this, we just detect which ones
- # have properly-installed paths (in the `gems` directory that shows up when
- # you run `gem list`).
- locally_installed_gems = shellout!("#{bundle_bin} list --paths", env: project_env, cwd: project_dir).
- stdout.lines.select { |gem_path| !gem_path.start_with?(gem_install_dir) }
-
- # Install the gems for real using `rake install` in their directories
- locally_installed_gems.each do |gem_path|
- gem_path = gem_path.chomp
- # We use the already-installed bundle to rake install, because (hopefully)
- # just rake installing doesn't require anything special.
- # Emit blank line to separate different tasks
- log.info(log_key) { "" }
- log.info(log_key) { "Properly installing git or path sourced gem #{gem_path} using rake install" }
- shellout!("#{bundle_bin} exec #{rake_bin} install", env: project_env, cwd: gem_path)
- end
- end
- end
-
- def install_shared_gemfile
- # Emit blank line to separate different tasks
- block { log.info(log_key) { "" } }
-
- shared_gemfile = self.shared_gemfile
- project_env = env.dup.merge("BUNDLE_GEMFILE" => project_gemfile)
-
- # Show the config for good measure
- bundle "config", env: project_env
-
- # Make `Gemfile` point to these by removing path and git sources and pinning versions.
- block "Rewrite Gemfile using all properly-installed gems" do
- gem_pins = ""
- result = []
- shellout!("#{bundle_bin} list", env: project_env).stdout.lines.map do |line|
- if line =~ /^\s*\*\s*(\S+)\s+\((\S+).*\)\s*$/
- name, version = $1, $2
- # rubocop is an exception, since different projects disagree
- next if GEMS_ALLOWED_TO_FLOAT.include?(name)
- gem_pins << "gem #{name.inspect}, #{version.inspect}, override: true\n"
- end
- end
-
- # Find the installed chef gem by looking for lib/chef.rb
- chef_gem = File.expand_path("../..", shellout!("#{gem_bin} which chef", env: project_env).stdout.chomp)
- # Figure out the path to gemfile_util from there
- gemfile_util = Pathname.new(File.join(chef_gem, "tasks", "gemfile_util"))
- gemfile_util = gemfile_util.relative_path_from(Pathname.new(shared_gemfile).dirname)
-
- create_file(shared_gemfile) { <<-EOM }
- # Meant to be included in component Gemfiles at the beginning with:
- #
- # instance_eval(IO.read("#{install_dir}/Gemfile"), "#{install_dir}/Gemfile")
- #
- # Override any existing gems with our own.
- require_relative "#{gemfile_util}"
- extend GemfileUtil
- #{gem_pins}
- EOM
- end
-
- shared_gemfile_env = env.dup.merge("BUNDLE_GEMFILE" => shared_gemfile)
-
- # Create a `Gemfile.lock` at the final location
- bundle "lock", env: shared_gemfile_env
-
- # Freeze the location's Gemfile.lock.
- create_bundle_config(shared_gemfile, frozen: true)
-
- # Clear the now-unnecessary git caches, cached gems, and git-checked-out gems
- block "Delete bundler git cache and git installs" do
- gemdir = shellout!("#{gem_bin} environment gemdir", env: env).stdout.chomp
- remove_file "#{gemdir}/cache"
- remove_file "#{gemdir}/bundler"
- end
- end
-end
diff --git a/omnibus/files/mapfiles/solaris b/omnibus/files/mapfiles/solaris
deleted file mode 100644
index c0ca5c1ae3..0000000000
--- a/omnibus/files/mapfiles/solaris
+++ /dev/null
@@ -1,18 +0,0 @@
-$mapfile_version 2
-DEPEND_VERSIONS libnsl.so {
- ALLOW = SUNW_1.1;
- ALLOW = SUNWprivate_1.1;
-};
-DEPEND_VERSIONS libsocket.so {
- ALLOW = SUNW_1.4;
- ALLOW = SUNWprivate_1.1;
-};
-DEPEND_VERSIONS libdl.so {
- ALLOW = SUNW_1.4;
- ALLOW = SUNWprivate_1.1;
-};
-DEPEND_VERSIONS libc.so {
- ALLOW = SUNW_1.22.1;
- ALLOW = SUNW_1.4;
- ALLOW = SUNWprivate_1.1;
-};
diff --git a/omnibus/omnibus.rb b/omnibus/omnibus.rb
index 4eeee4abcb..dfbd2bc338 100644
--- a/omnibus/omnibus.rb
+++ b/omnibus/omnibus.rb
@@ -26,8 +26,10 @@
# Windows architecture defaults - set to x86 unless otherwise specified.
# ------------------------------
-windows_arch %w{x86 x64}.include?((ENV["OMNIBUS_WINDOWS_ARCH"] || "").downcase) ?
- ENV["OMNIBUS_WINDOWS_ARCH"].downcase.to_sym : :x86
+env_omnibus_windows_arch = (ENV["OMNIBUS_WINDOWS_ARCH"] || "").downcase
+env_omnibus_windows_arch = :x86 unless %w{x86 x64}.include?(env_omnibus_windows_arch)
+
+windows_arch env_omnibus_windows_arch
# Disable git caching
# ------------------------------
@@ -48,3 +50,6 @@ fetcher_read_timeout 120
# ------------------------------
# software_gems ['omnibus-software', 'my-company-software']
# local_software_dirs ['/path/to/local/software']
+
+fatal_transitive_dependency_licensing_warnings true
+fips_mode (ENV["OMNIBUS_FIPS_MODE"] || "").casecmp("true") >= 0
diff --git a/omnibus/resources/chef/ips/chef-symlinks.erb b/omnibus/resources/chef/ips/chef-symlinks.erb
new file mode 100644
index 0000000000..419d52ddf7
--- /dev/null
+++ b/omnibus/resources/chef/ips/chef-symlinks.erb
@@ -0,0 +1,6 @@
+link path=usr/bin/chef-solo target=<%= projectdir %>/bin/chef-solo
+link path=usr/bin/chef-client target=<%= projectdir %>/bin/chef-client
+link path=usr/bin/chef-apply target=<%= projectdir %>/bin/chef-apply
+link path=usr/bin/chef-shell target=<%= projectdir %>/bin/chef-shell
+link path=usr/bin/knife target=<%= projectdir %>/bin/knife
+link path=usr/bin/ohai target=<%= projectdir %>/bin/ohai
diff --git a/omnibus/resources/chef/msi/localization-en-us.wxl.erb b/omnibus/resources/chef/msi/localization-en-us.wxl.erb
index 62c27b99f6..c5e8e5ddab 100644
--- a/omnibus/resources/chef/msi/localization-en-us.wxl.erb
+++ b/omnibus/resources/chef/msi/localization-en-us.wxl.erb
@@ -16,15 +16,27 @@
<String Id="VerifyReadyDlgInstallTitle">{\WixUI_Font_Title_White}Ready to install [ProductName]</String>
+ <!-- Scheduled Task -->
+ <String Id="SchTaskDisplayName"><%= friendly_name %> Scheduled Task</String>
+ <String Id="SchTaskDescription">Schedule <%= friendly_name %> to run at a pre-defined time intervals.</String>
<!-- Service -->
<!-- Keep these in sync with the name and description in chef-service-manager -->
<String Id="ServiceDisplayName"><%= friendly_name %> Service</String>
<String Id="ServiceDescription">Runs <%= friendly_name %> on regular, configurable intervals.</String>
<String Id="FeatureMainName"><%= friendly_name %></String>
+ <String Id="FeatureSchTaskName"><%= friendly_name %> Scheduled Task</String>
<String Id="FeatureServiceName"><%= friendly_name %> Service</String>
<String Id="FeaturePSModuleName"><%= friendly_name %> PowerShell wrappers</String>
<String Id="MinimumOSVersionMessage">This package requires minimum OS version: Windows 7/Windows Server 2008 R2 or greater.</String>
<String Id="DowngradeErrorMessage">A newer version of [ProductName] is already installed.</String>
<String Id="FileExtractionProgress">Extracting files, please wait...</String>
+
+ <String Id="CustomizeDlgTextMsg">Select an option to change between the Chef's unattended execution options.</String>
+ <String Id="CustomizeDlgTextTitle">Chef Unattended Execution Options</String>
+ <String Id="CustomizeDlgFirstRadioButtonText">Chef Client Scheduled Task</String>
+ <String Id="CustomizeDlgSecondRadioButtonText">Chef Client Service</String>
+ <String Id="CustomizeDlgThirdRadioButtonText">None</String>
+
+ <String Id="CustomizeDlgOptionsMsg">The installer can configure the Chef Client to run periodically as either a scheduled task or a service. Using a scheduled task is recommended. For more information, see https://docs.chef.io/windows.html.</String>
</WixLocalization>
diff --git a/omnibus/resources/chef/msi/source.wxs.erb b/omnibus/resources/chef/msi/source.wxs.erb
index bdde02687e..c87c400624 100644
--- a/omnibus/resources/chef/msi/source.wxs.erb
+++ b/omnibus/resources/chef/msi/source.wxs.erb
@@ -29,6 +29,15 @@
<Media Id="1" Cabinet="ChefClient.cab" EmbedCab="yes" CompressionLevel="high" />
<!--
+ Take advantage of Windows Installer 5.0 feature (if available) to disable
+ checkpointing and other costings that take significant amounts of time
+ ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd408005(v=vs.85).aspx
+ -->
+ <Property Id="MSIFASTINSTALL" Value="7" />
+
+ <Property Id="CHEF_SERVICE_OPTIONS_RADIO_BUTTON_GROUP" Value="None" />
+
+ <!--
Uncomment launch condition below to check for minimum OS
601 = Windows 7/Server 2008R2.
-->
@@ -67,9 +76,42 @@
Impersonate="no"
Return="ignore" />
+ <CustomAction Id="CreateChefClientScheduledTask"
+ Directory="TARGETDIR"
+ ExeCommand="&quot;[SystemFolder]SCHTASKS.EXE&quot; /CREATE /TN &quot;chef-client&quot; /SC &quot;MINUTE&quot; /MO &quot;30&quot; /F /TR &quot;cmd /c \&quot;[EMBEDDEDBIN]ruby.exe [PROJECTLOCATIONBIN]chef-client -L [CONFIGLOCATION]chef-client.log -c [CONFIGLOCATION]client.rb\&quot;&quot; /RU &quot;NT Authority\System&quot; /RP /RL &quot;HIGHEST&quot;"
+ Execute="deferred"
+ Impersonate="no"
+ Return="check" />
+
+ <CustomAction Id="RemoveChefClientScheduledTask"
+ Directory="TARGETDIR"
+ ExeCommand="&quot;[SystemFolder]SCHTASKS.EXE&quot; /DELETE /TN &quot;chef-client&quot; /F"
+ Execute="deferred"
+ Impersonate="no"
+ Return="ignore" />
+
+ <CustomAction Id="RemoveChefClientService"
+ Directory="TARGETDIR"
+ ExeCommand="&quot;[SystemFolder]SC.EXE&quot; DELETE &quot;chef-client&quot;"
+ Execute="deferred"
+ Impersonate="no"
+ Return="ignore" />
+
<InstallExecuteSequence>
- <Custom Action="FastUnzip" After="InstallFiles">NOT Installed</Custom>
+ <Custom Action="FastUnzip" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
<Custom Action="Cleanup" After="RemoveFiles">REMOVE~="ALL"</Custom>
+
+ <Custom Action="CreateChefClientScheduledTask" After="InstallFiles">
+ <![CDATA[&ChefSchTaskFeature=3]]>
+ </Custom>
+
+ <Custom Action="RemoveChefClientScheduledTask" Before="RemoveFiles">
+ <![CDATA[(Installed AND (&NoneFeature=3 OR &ChefServiceFeature=3)) OR (REMOVE="ALL")]]>
+ </Custom>
+
+ <Custom Action="RemoveChefClientService" Before="RemoveFiles">
+ <![CDATA[Installed AND (&NoneFeature=3 OR &ChefSchTaskFeature=3) OR (REMOVE="ALL")]]>
+ </Custom>
</InstallExecuteSequence>
<UI>
@@ -109,6 +151,35 @@
<Environment Id="ChefPSModulePathEnvironment"
Name="PSModulePath" Action="set" Part="last" System="yes" Value="[PSMODULES]" />
</Component>
+ <Component Id="ChefPowerShellRegistryEntries" Guid="{3715B7AB-C3CA-4C69-8FAF-79C8CF58FD89}">
+ <RegistryKey Root="HKCR" Key="Chef.PowerShell">
+ <RegistryValue Type="string" Value="Chef.PowerShell" />
+ </RegistryKey>
+ <RegistryKey Root="HKCR" Key="Chef.PowerShell\CLSID">
+ <RegistryValue Type="string" Value="{9008CA83-83E4-41FF-9C07-696E2CC47B52}" />
+ </RegistryKey>
+ <RegistryKey Root="HKCR" Key="CLSID\{9008CA83-83E4-41FF-9C07-696E2CC47B52}">
+ <RegistryValue Type="string" Value="Chef.PowerShell" />
+ </RegistryKey>
+ <RegistryKey Root="HKCR" Key="CLSID\{9008CA83-83E4-41FF-9C07-696E2CC47B52}\InprocServer32">
+ <RegistryValue Type="string" Value="[System64Folder]mscoree.dll" />
+ <RegistryValue Type="string" Name="ThreadingModel" Value="Both" />
+ <RegistryValue Type="string" Name="Class" Value="Chef.PowerShell" />
+ <RegistryValue Type="string" Name="Assembly" Value="Chef.PowerShell, Version=1.0.14.0, Culture=neutral, PublicKeyToken=7def9f799d039a95" />
+ <RegistryValue Type="string" Name="RuntimeVersion" Value="v4.0.30319" />
+ <RegistryValue Type="string" Name="CodeBase" Value="[PSMODULES]chef\Chef.PowerShell.dll" />
+ </RegistryKey>
+ <RegistryKey Root="HKCR" Key="CLSID\{9008CA83-83E4-41FF-9C07-696E2CC47B52}\InprocServer32\1.0.0.0">
+ <RegistryValue Type="string" Name="Class" Value="Chef.PowerShell" />
+ <RegistryValue Type="string" Name="Assembly" Value="Chef.PowerShell, Version=1.0.14.0, Culture=neutral, PublicKeyToken=7def9f799d039a95" />
+ <RegistryValue Type="string" Name="RuntimeVersion" Value="v4.0.30319" />
+ <RegistryValue Type="string" Name="CodeBase" Value="[PSMODULES]chef\Chef.PowerShell.dll" />
+ </RegistryKey>
+ <RegistryKey Root="HKCR" Key="CLSID\{9008CA83-83E4-41FF-9C07-696E2CC47B52}\ProgId">
+ <RegistryValue Type="string" Value="Chef.PowerShell" />
+ </RegistryKey>
+ <RegistryKey Root="HKCR" Key="CLSID\{9008CA83-83E4-41FF-9C07-696E2CC47B52}\Implemented Categories\{62C8FE65-4EBB-45E7-B440-6E39B2CDBF29}" />
+ </Component>
</Directory>
<Directory Id="EMBEDDED" Name="embedded" >
<Directory Id="EMBEDDEDBIN" Name="bin" >
@@ -135,6 +206,16 @@
</Directory>
</Directory>
</Directory>
+ <Directory Id="ChefSchTaskFeatureTempDir">
+ <Component Id="ChefSchTask" Guid="{7f9f917a-952c-41d8-baa1-037269eecb50}">
+ <CreateFolder />
+ </Component>
+ </Directory>
+ <Directory Id="NoneFeatureTempDir">
+ <Component Id="None" Guid="{d8f3eba5-cecb-436c-a4ef-540dba3c5ccf}">
+ <CreateFolder />
+ </Component>
+ </Directory>
</Directory>
</Directory>
@@ -145,14 +226,31 @@
<ComponentRef Id="ChefClientPath" />
<ComponentRef Id="CONFIGLOCATIONDIR" />
<ComponentRef Id="ChefClientLog" />
+ <ComponentRef Id="ChefPowerShellRegistryEntries" />
</Feature>
<Feature Id="ChefPSModuleFeature" Title="!(loc.FeaturePSModuleName)" Level="1000" AllowAdvertise="no">
<ComponentRef Id="ChefPSModulePath" />
</Feature>
- <Feature Id="ChefServiceFeature" Title="!(loc.FeatureServiceName)" Level="1000" AllowAdvertise="no">
- <ComponentRef Id="ChefClientService" />
+ <Feature Id="ChefUnattendedExecutionOptions" Title="Chef Unattended Execution Options" Level="1000" AllowAdvertise="no">
+ <Feature Id="ChefSchTaskFeature" Title="!(loc.FeatureSchTaskName)" Level="1000" AllowAdvertise="no" Display="hidden">
+ <!-- Here, CustomAction will get executed and scheduled task for chef-client will get created -->
+
+ <!-- This is an empty component to keep track of the feature -->
+ <ComponentRef Id="ChefSchTask" />
+ </Feature>
+
+ <Feature Id="ChefServiceFeature" Title="!(loc.FeatureServiceName)" Level="1000" AllowAdvertise="no" Display="hidden">
+ <ComponentRef Id="ChefClientService" />
+ </Feature>
+
+ <Feature Id="NoneFeature" Title="None" Level="1000" AllowAdvertise="no" Display="hidden">
+ <!-- Do Nothing -->
+
+ <!-- This is an empty component to keep track of the feature -->
+ <ComponentRef Id="None" />
+ </Feature>
</Feature>
<!--
@@ -173,12 +271,92 @@
<UIRef Id="ChefClientUI_InstallDir"/>
<UI Id="ChefClientUI_InstallDir">
- <UIRef Id="WixUI_FeatureTree"/>
+ <!-- WixUI_FeatureTree module's code embedded and modified here as per the requirement -->
<TextStyle Id="WixUI_Font_Normal_White" FaceName="Tahoma" Size="8" Red="255" Green="255" Blue="255" />
<TextStyle Id="WixUI_Font_Bigger_White" FaceName="Tahoma" Size="12" Red="255" Green="255" Blue="255" />
<TextStyle Id="WixUI_Font_Title_White" FaceName="Tahoma" Size="9" Bold="yes" Red="255" Green="255" Blue="255" />
+ <TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
+ <TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
+ <TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />
+ <TextStyle Id="WixUI_Font_Msg" FaceName="Tahoma" Size="9" />
+
+ <Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
+ <Property Id="WixUI_Mode" Value="FeatureTree" />
+
+ <DialogRef Id="ErrorDlg" />
+ <DialogRef Id="FatalError" />
+ <DialogRef Id="FilesInUse" />
+ <DialogRef Id="MsiRMFilesInUse" />
+ <DialogRef Id="PrepareDlg" />
+ <DialogRef Id="ProgressDlg" />
+ <DialogRef Id="ResumeDlg" />
+ <DialogRef Id="UserExit" />
+
+ <Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>
+
+ <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="LicenseAgreementDlg">NOT Installed</Publish>
+ <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">Installed AND PATCH</Publish>
+
+ <Publish Dialog="LicenseAgreementDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg">1</Publish>
+ <Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog" Value="CustomizeDlg">LicenseAccepted = "1"</Publish>
+
+ <Publish Dialog="CustomizeDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="1">Installed</Publish>
+ <Publish Dialog="CustomizeDlg" Control="Back" Event="NewDialog" Value="LicenseAgreementDlg" Order="2">NOT Installed</Publish>
+
+ <Publish Dialog="CustomizeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg"><![CDATA[((NOT &ChefUnattendedExecutionOptions=3) AND NOT ((?ChefSchTask=3) OR (?ChefClientService=3) OR (?None=3)))]]></Publish>
+
+ <Publish Dialog="CustomizeDlg" Control="Next" Event="NewDialog" Value="ChefUnattendedExecutionOptionsSelectionDlg"><![CDATA[((&ChefUnattendedExecutionOptions=3) OR (?ChefSchTask=3 OR ?ChefClientService=3 OR ?None=3))]]></Publish>
+
+ <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="CustomizeDlg" Order="1"><![CDATA[NOT &ChefUnattendedExecutionOptions=3]]> AND (NOT Installed OR WixUI_InstallMode = "Change")</Publish>
+ <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="ChefUnattendedExecutionOptionsSelectionDlg" Order="1"><![CDATA[&ChefUnattendedExecutionOptions=3]]> AND (NOT Installed OR WixUI_InstallMode = "Change")</Publish>
+ <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2">Installed AND NOT PATCH</Publish>
+ <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="3">Installed AND PATCH</Publish>
+
+ <Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg">1</Publish>
+
+ <Publish Dialog="MaintenanceTypeDlg" Control="ChangeButton" Event="NewDialog" Value="CustomizeDlg">1</Publish>
+ <Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
+ <Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
+ <Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg">1</Publish>
+
+ <Dialog Id ="ChefUnattendedExecutionOptionsSelectionDlg" Width ="370" Height ="270" Title ="!(loc.ProductName) v$(var.DisplayVersionNumber) Setup" NoMinimize ="no">
+ <Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.CustomizeDlgBannerBitmap)" />
+ <Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" />
+ <Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
+ <Control Id="Title" Type="Text" X="15" Y="6" Width="210" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.CustomizeDlgTitle)" />
+ <Control Id="Description" Type="Text" X="25" Y="23" Width="280" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.CustomizeDlgDescription)" />
+ <Control Id="TextMsg" Type="Text" X="25" Y="55" Width="320" Height="20" Text="!(loc.CustomizeDlgTextMsg)" />
+
+ <Control Id="LeftBox" Type="GroupBox" X="25" Y="81" Width="175" Height="118" />
+ <Control Id="TextTitle" Type="Text" X="30" Y="100" Width="169" Height="20" Text="{\WixUI_Font_Title}!(loc.CustomizeDlgTextTitle)" />
+ <Control Id="OptionsRadioGroup" Type="RadioButtonGroup" Property="CHEF_SERVICE_OPTIONS_RADIO_BUTTON_GROUP" Height="80" Width="140" X="35" Y="110">
+ <RadioButtonGroup Property="CHEF_SERVICE_OPTIONS_RADIO_BUTTON_GROUP">
+ <RadioButton Value="SchTask" Text="!(loc.CustomizeDlgFirstRadioButtonText)" Height="17" Width="140" X="0" Y="10" />
+ <RadioButton Value="Service" Text="!(loc.CustomizeDlgSecondRadioButtonText)" Height="17" Width="140" X="0" Y="35" />
+ <RadioButton Value="None" Text="!(loc.CustomizeDlgThirdRadioButtonText)" Height="17" Width="140" X="0" Y="60" />
+ </RadioButtonGroup>
+ </Control>
+
+ <Control Id="RightBox" Type="GroupBox" X="210" Y="81" Width="150" Height="118" />
+ <Control Id="OptionsMsg" Type="Text" X="214" Y="100" Width="146" Height="80" Text="{\WixUI_Font_Msg}!(loc.CustomizeDlgOptionsMsg)" />
+
+ <Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Default="no" Text="Back">
+ <Publish Event="NewDialog" Value="CustomizeDlg">1</Publish>
+ </Control>
+ <Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="Next">
+ <Publish Event="AddLocal" Value="ChefSchTaskFeature">CHEF_SERVICE_OPTIONS_RADIO_BUTTON_GROUP = "SchTask"</Publish>
+ <Publish Event="AddLocal" Value="ChefServiceFeature">CHEF_SERVICE_OPTIONS_RADIO_BUTTON_GROUP = "Service"</Publish>
+ <Publish Event="AddLocal" Value="NoneFeature">CHEF_SERVICE_OPTIONS_RADIO_BUTTON_GROUP = "None"</Publish>
+ <Publish Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
+ </Control>
+ <Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Default="no" Text="Cancel" Cancel="yes">
+ <Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
+ </Control>
+ </Dialog>
</UI>
+ <UIRef Id="WixUI_Common" />
+
<WixVariable Id="WixUILicenseRtf" Value="Resources\assets\LICENSE.rtf" />
<WixVariable Id="WixUIDialogBmp" Value="Resources\assets\dialog_background.bmp" />
<WixVariable Id="WixUIBannerBmp" Value="Resources\assets\banner_background.bmp" />
diff --git a/omnibus_overrides.rb b/omnibus_overrides.rb
index 17d0dc4b89..1d2b46bdbe 100644
--- a/omnibus_overrides.rb
+++ b/omnibus_overrides.rb
@@ -1,18 +1,22 @@
-# DO NOT EDIT. Generated by "rake dependencies". Edit version_policy.rb instead.
-override :rubygems, version: "2.6.3"
-override :bundler, version: "1.11.2"
+# THIS IS NOW HAND MANAGED, JUST EDIT THE THING
+# .travis.yml and appveyor.yml consume this,
+# try to keep it machine-parsable.
+override :rubygems, version: "2.7.6"
+override :bundler, version: "1.16.1"
+override "nokogiri", version: "1.8.2"
override "libffi", version: "3.2.1"
-override "libiconv", version: "1.14"
-override "liblzma", version: "5.2.2"
+override "libiconv", version: "1.15"
+override "liblzma", version: "5.2.3"
override "libtool", version: "2.4.2"
-override "libxml2", version: "2.9.3"
-override "libxslt", version: "1.1.28"
-override "libyaml", version: "0.1.6"
+override "libxml2", version: "2.9.7"
+override "libxslt", version: "1.1.30"
+override "libyaml", version: "0.1.7"
override "makedepend", version: "1.0.5"
override "ncurses", version: "5.9"
override "pkg-config-lite", version: "0.28-1"
-override "ruby", version: "2.1.8"
+override "ruby", version: "2.5.1"
override "ruby-windows-devkit-bash", version: "3.1.23-4-msys-1.0.18"
override "util-macros", version: "1.19.0"
override "xproto", version: "7.0.28"
-override "zlib", version: "1.2.8"
+override "zlib", version: "1.2.11"
+override "openssl", version: "1.0.2o"
diff --git a/pkg.rb b/pkg.rb
deleted file mode 100644
index bb26f257d0..0000000000
--- a/pkg.rb
+++ /dev/null
@@ -1 +0,0 @@
-package "chef-12.9.38-1.powerpc"
diff --git a/rubygems-pkg/rubygems-update-2.4.6.gem b/rubygems-pkg/rubygems-update-2.4.6.gem
deleted file mode 100644
index 97ebec693a..0000000000
--- a/rubygems-pkg/rubygems-update-2.4.6.gem
+++ /dev/null
Binary files differ
diff --git a/spec/data/client.d_00/02-strings.rb b/spec/data/client.d_00/02-strings.rb
new file mode 100644
index 0000000000..7d9a49268c
--- /dev/null
+++ b/spec/data/client.d_00/02-strings.rb
@@ -0,0 +1,2 @@
+# 02-strings.rb
+something '/foo/bar'
diff --git a/spec/data/cookbooks/openldap/templates/default/openldap_nested_variable_stuff.erb b/spec/data/cookbooks/openldap/templates/default/openldap_nested_variable_stuff.erb
new file mode 100644
index 0000000000..5ebee33806
--- /dev/null
+++ b/spec/data/cookbooks/openldap/templates/default/openldap_nested_variable_stuff.erb
@@ -0,0 +1 @@
+super secret is <%= @secret.first["key"] -%>
diff --git a/spec/data/lwrp/providers/buck_passer.rb b/spec/data/lwrp/providers/buck_passer.rb
index 2bbca07bf7..0bbd413867 100644
--- a/spec/data/lwrp/providers/buck_passer.rb
+++ b/spec/data/lwrp/providers/buck_passer.rb
@@ -10,7 +10,7 @@ def without_deprecation_warnings(&block)
end
end
-action :pass_buck do
+def action_pass_buck
lwrp_foo :prepared_thumbs do
action :prepare_thumbs
# We know there will be a deprecation error here; head it off
diff --git a/spec/data/lwrp/providers/buck_passer_2.rb b/spec/data/lwrp/providers/buck_passer_2.rb
index c3bab7266f..980506c671 100644
--- a/spec/data/lwrp/providers/buck_passer_2.rb
+++ b/spec/data/lwrp/providers/buck_passer_2.rb
@@ -8,7 +8,7 @@ def without_deprecation_warnings(&block)
end
end
-action :pass_buck do
+def action_pass_buck
lwrp_bar :prepared_eyes do
action :prepare_eyes
# We know there will be a deprecation error here; head it off
diff --git a/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb b/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb
index 77c1111ff5..d6996da55e 100644
--- a/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb
+++ b/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb
@@ -13,7 +13,7 @@ def without_deprecation_warnings(&block)
end
end
-action :twiddle_thumbs do
+def action_twiddle_thumbs
@enclosed_resource = lwrp_foo :foo do
monkey generate_new_name(new_resource.monkey){ 'the monkey' }
# We know there will be a deprecation error here; head it off
diff --git a/spec/data/lwrp/providers/inline_compiler.rb b/spec/data/lwrp/providers/inline_compiler.rb
index 2535276b24..91a80b32af 100644
--- a/spec/data/lwrp/providers/inline_compiler.rb
+++ b/spec/data/lwrp/providers/inline_compiler.rb
@@ -1,6 +1,4 @@
-use_inline_resources
-
action :test do
ruby_block "interior-ruby-block-1" do
diff --git a/spec/data/lwrp/resources/buck_passer.rb b/spec/data/lwrp/resources/buck_passer.rb
new file mode 100644
index 0000000000..7335c0aae2
--- /dev/null
+++ b/spec/data/lwrp/resources/buck_passer.rb
@@ -0,0 +1,5 @@
+
+provides :buck_passer
+
+default_action :pass_buck
+actions :pass_buck
diff --git a/spec/data/lwrp/resources/buck_passer_2.rb b/spec/data/lwrp/resources/buck_passer_2.rb
new file mode 100644
index 0000000000..c7a1a279f3
--- /dev/null
+++ b/spec/data/lwrp/resources/buck_passer_2.rb
@@ -0,0 +1,3 @@
+
+default_action :pass_buck
+actions :pass_buck
diff --git a/spec/data/lwrp/resources/embedded_resource_accesses_providers_scope.rb b/spec/data/lwrp/resources/embedded_resource_accesses_providers_scope.rb
new file mode 100644
index 0000000000..3a8ae2c19f
--- /dev/null
+++ b/spec/data/lwrp/resources/embedded_resource_accesses_providers_scope.rb
@@ -0,0 +1,3 @@
+
+default_action :twiddle_thumbs
+actions :twiddle_thumbs
diff --git a/spec/data/lwrp/resources/inline_compiler.rb b/spec/data/lwrp/resources/inline_compiler.rb
new file mode 100644
index 0000000000..fe446ddf84
--- /dev/null
+++ b/spec/data/lwrp/resources/inline_compiler.rb
@@ -0,0 +1,3 @@
+
+default_action :test
+actions :test, :no_updates
diff --git a/spec/data/lwrp/resources/monkey_name_printer.rb b/spec/data/lwrp/resources/monkey_name_printer.rb
new file mode 100644
index 0000000000..d70e2f34e3
--- /dev/null
+++ b/spec/data/lwrp/resources/monkey_name_printer.rb
@@ -0,0 +1,5 @@
+
+property :monkey
+
+default_action :twiddle_thumbs
+actions :twiddle_thumbs
diff --git a/spec/data/lwrp/resources/paint_drying_watcher.rb b/spec/data/lwrp/resources/paint_drying_watcher.rb
new file mode 100644
index 0000000000..519b7f83fd
--- /dev/null
+++ b/spec/data/lwrp/resources/paint_drying_watcher.rb
@@ -0,0 +1,3 @@
+
+default_action :prepare_eyes
+actions :prepare_eyes, :watch_paint_dry
diff --git a/spec/data/lwrp/resources/thumb_twiddler.rb b/spec/data/lwrp/resources/thumb_twiddler.rb
new file mode 100644
index 0000000000..2b5d2d803e
--- /dev/null
+++ b/spec/data/lwrp/resources/thumb_twiddler.rb
@@ -0,0 +1,3 @@
+
+default_action :prepare_thumbs
+actions :prepare_thumbs, :twiddle_thumbs
diff --git a/spec/data/mixin/invalid_data.rb b/spec/data/mixin/invalid_data.rb
new file mode 100644
index 0000000000..e6f6c3a783
--- /dev/null
+++ b/spec/data/mixin/invalid_data.rb
@@ -0,0 +1,3 @@
+# For spec/functional/mixin/from_file_spec.rb
+a :foo
+c :bar
diff --git a/spec/data/mixin/real_data.rb b/spec/data/mixin/real_data.rb
new file mode 100644
index 0000000000..e15b86fc68
--- /dev/null
+++ b/spec/data/mixin/real_data.rb
@@ -0,0 +1,2 @@
+# For spec/functional/mixin/from_file_spec.rb
+a :foo
diff --git a/spec/data/prefer_metadata_json/metadata.json b/spec/data/prefer_metadata_json/metadata.json
new file mode 100644
index 0000000000..eff8836a3b
--- /dev/null
+++ b/spec/data/prefer_metadata_json/metadata.json
@@ -0,0 +1,51 @@
+{
+ "name": "prefer_metadata_json",
+ "description": "",
+ "long_description": "",
+ "maintainer": null,
+ "maintainer_email": null,
+ "license": "All rights reserved",
+ "platforms": {
+
+ },
+ "dependencies": {
+
+ },
+ "recommendations": {
+
+ },
+ "suggestions": {
+
+ },
+ "conflicting": {
+
+ },
+ "providing": {
+
+ },
+ "replacing": {
+
+ },
+ "attributes": {
+
+ },
+ "groupings": {
+
+ },
+ "recipes": {
+
+ },
+ "version": "1.2.3",
+ "source_url": "",
+ "issues_url": "",
+ "privacy": false,
+ "chef_versions": [
+
+ ],
+ "ohai_versions": [
+
+ ],
+ "gems": [
+
+ ]
+}
diff --git a/spec/data/prefer_metadata_json/metadata.rb b/spec/data/prefer_metadata_json/metadata.rb
new file mode 100644
index 0000000000..a46aa29a5c
--- /dev/null
+++ b/spec/data/prefer_metadata_json/metadata.rb
@@ -0,0 +1,6 @@
+# these deliberately do not match metadata.json
+name "test"
+version "0.0.1"
+
+# this raises hard if anything even tries to parse it
+raise "TEH SADNESS"
diff --git a/spec/data/prefer_metadata_json/recipes/default.rb b/spec/data/prefer_metadata_json/recipes/default.rb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/spec/data/prefer_metadata_json/recipes/default.rb
diff --git a/spec/data/root_alias_cookbooks/dup_attr/attributes.rb b/spec/data/root_alias_cookbooks/dup_attr/attributes.rb
new file mode 100644
index 0000000000..3a3bab96e1
--- /dev/null
+++ b/spec/data/root_alias_cookbooks/dup_attr/attributes.rb
@@ -0,0 +1 @@
+default["aliased"]["attr"] = "value"
diff --git a/spec/data/root_alias_cookbooks/dup_attr/attributes/default.rb b/spec/data/root_alias_cookbooks/dup_attr/attributes/default.rb
new file mode 100644
index 0000000000..a6f6c78bb0
--- /dev/null
+++ b/spec/data/root_alias_cookbooks/dup_attr/attributes/default.rb
@@ -0,0 +1 @@
+default["aliased"]["attr"] = "other"
diff --git a/spec/data/root_alias_cookbooks/dup_attr/metadata.rb b/spec/data/root_alias_cookbooks/dup_attr/metadata.rb
new file mode 100644
index 0000000000..703a73ab19
--- /dev/null
+++ b/spec/data/root_alias_cookbooks/dup_attr/metadata.rb
@@ -0,0 +1,2 @@
+name "dup_attr"
+version "1.0.0"
diff --git a/spec/data/root_alias_cookbooks/dup_attr/recipe.rb b/spec/data/root_alias_cookbooks/dup_attr/recipe.rb
new file mode 100644
index 0000000000..d82e58fbcd
--- /dev/null
+++ b/spec/data/root_alias_cookbooks/dup_attr/recipe.rb
@@ -0,0 +1,3 @@
+ruby_block "root alias" do
+ block { }
+end
diff --git a/spec/data/root_alias_cookbooks/dup_recipe/attributes.rb b/spec/data/root_alias_cookbooks/dup_recipe/attributes.rb
new file mode 100644
index 0000000000..3a3bab96e1
--- /dev/null
+++ b/spec/data/root_alias_cookbooks/dup_recipe/attributes.rb
@@ -0,0 +1 @@
+default["aliased"]["attr"] = "value"
diff --git a/spec/data/root_alias_cookbooks/dup_recipe/metadata.rb b/spec/data/root_alias_cookbooks/dup_recipe/metadata.rb
new file mode 100644
index 0000000000..62273a64d5
--- /dev/null
+++ b/spec/data/root_alias_cookbooks/dup_recipe/metadata.rb
@@ -0,0 +1,2 @@
+name "dup_recipe"
+version "1.0.0"
diff --git a/spec/data/root_alias_cookbooks/dup_recipe/recipe.rb b/spec/data/root_alias_cookbooks/dup_recipe/recipe.rb
new file mode 100644
index 0000000000..d82e58fbcd
--- /dev/null
+++ b/spec/data/root_alias_cookbooks/dup_recipe/recipe.rb
@@ -0,0 +1,3 @@
+ruby_block "root alias" do
+ block { }
+end
diff --git a/spec/data/root_alias_cookbooks/dup_recipe/recipes/default.rb b/spec/data/root_alias_cookbooks/dup_recipe/recipes/default.rb
new file mode 100644
index 0000000000..3eb7c22809
--- /dev/null
+++ b/spec/data/root_alias_cookbooks/dup_recipe/recipes/default.rb
@@ -0,0 +1,3 @@
+ruby_block "other" do
+ block { }
+end
diff --git a/spec/data/root_alias_cookbooks/simple/attributes.rb b/spec/data/root_alias_cookbooks/simple/attributes.rb
new file mode 100644
index 0000000000..3a3bab96e1
--- /dev/null
+++ b/spec/data/root_alias_cookbooks/simple/attributes.rb
@@ -0,0 +1 @@
+default["aliased"]["attr"] = "value"
diff --git a/spec/data/root_alias_cookbooks/simple/metadata.rb b/spec/data/root_alias_cookbooks/simple/metadata.rb
new file mode 100644
index 0000000000..9147558459
--- /dev/null
+++ b/spec/data/root_alias_cookbooks/simple/metadata.rb
@@ -0,0 +1,2 @@
+name "simple"
+version "1.0.0"
diff --git a/spec/data/root_alias_cookbooks/simple/recipe.rb b/spec/data/root_alias_cookbooks/simple/recipe.rb
new file mode 100644
index 0000000000..d82e58fbcd
--- /dev/null
+++ b/spec/data/root_alias_cookbooks/simple/recipe.rb
@@ -0,0 +1,3 @@
+ruby_block "root alias" do
+ block { }
+end
diff --git a/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb b/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb
index ef0967a4d2..e45e7d9f68 100644
--- a/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb
+++ b/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb
@@ -1,4 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "circular-dep1::default"
-
-
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "circular-dep1::default"
diff --git a/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb b/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb
index f2ef012aa1..37f396b1f9 100644
--- a/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb
+++ b/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb
@@ -1,3 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "circular-dep2::default"
-
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "circular-dep2::default"
diff --git a/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb b/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb
index e818d36a9e..3059494198 100644
--- a/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb
+++ b/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb
@@ -1,2 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "dependency1::aa_first"
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "dependency1::aa_first"
diff --git a/spec/data/run_context/cookbooks/dependency1/attributes/default.rb b/spec/data/run_context/cookbooks/dependency1/attributes/default.rb
index 6875274e3f..a65a3345bc 100644
--- a/spec/data/run_context/cookbooks/dependency1/attributes/default.rb
+++ b/spec/data/run_context/cookbooks/dependency1/attributes/default.rb
@@ -1,2 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "dependency1::default"
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "dependency1::default"
diff --git a/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb b/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb
index 1a513b03d4..94ffb30133 100644
--- a/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb
+++ b/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb
@@ -1,3 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "dependency1::zz_last"
-
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "dependency1::zz_last"
diff --git a/spec/data/run_context/cookbooks/dependency2/attributes/default.rb b/spec/data/run_context/cookbooks/dependency2/attributes/default.rb
index 526751f422..8917bf9730 100644
--- a/spec/data/run_context/cookbooks/dependency2/attributes/default.rb
+++ b/spec/data/run_context/cookbooks/dependency2/attributes/default.rb
@@ -1,3 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "dependency2::default"
-
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "dependency2::default"
diff --git a/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb b/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb
index 3ad2b925aa..07294665b2 100644
--- a/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb
+++ b/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb
@@ -1,3 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "no-default-attr::server"
-
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "no-default-attr::server"
diff --git a/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb b/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb
index cca56bc61f..77309462b1 100644
--- a/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb
+++ b/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb
@@ -1,3 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "test-with-circular-deps::default"
-
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "test-with-circular-deps::default"
diff --git a/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb b/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb
index 4d71cc3cfe..c4cc8151a4 100644
--- a/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb
+++ b/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb
@@ -1,3 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "test-with-deps::default"
-
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "test-with-deps::default"
diff --git a/spec/data/sample_msu1.xml b/spec/data/sample_msu1.xml
new file mode 100644
index 0000000000..cc68dbf060
--- /dev/null
+++ b/spec/data/sample_msu1.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<unattend xmlns="urn:schemas-microsoft-com:unattend">
+ <servicing>
+ <package action="install">
+ <assemblyIdentity name="Package_for_KB2859903" version="10.2.1.0" language="neutral" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35"/>
+ <source location="%configsetroot%\IE10-Windows6.1-KB2859903-x86.CAB" />
+ </package>
+ </servicing>
+</unattend>
+
diff --git a/spec/data/sample_msu2.xml b/spec/data/sample_msu2.xml
new file mode 100644
index 0000000000..6f95e04f93
--- /dev/null
+++ b/spec/data/sample_msu2.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<unattend xmlns="urn:schemas-microsoft-com:unattend">
+ <servicing>
+ <package action="install">
+ <assemblyIdentity name="Package_for_KB2859903" version="10.2.1.0" language="neutral" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35"/>
+ <source location="%configsetroot%\IE10-Windows6.1-KB2859903-x86.CAB" />
+ </package>
+ <package action="install">
+ <assemblyIdentity name="Package_for_abc" version="10.2.1.0" language="neutral" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35"/>
+ <source location="%configsetroot%\abc.CAB" />
+ </package>
+ </servicing>
+</unattend>
+
diff --git a/spec/data/sample_msu3.xml b/spec/data/sample_msu3.xml
new file mode 100644
index 0000000000..0ef09da206
--- /dev/null
+++ b/spec/data/sample_msu3.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<unattend xmlns="urn:schemas-microsoft-com:unattend">
+ <servicing>
+ <package action="install">
+ <assemblyIdentity name="Package_for_KB2859903" version="10.2.1.0" language="neutral" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35"/>
+ <source location="%configsetroot%\IE10-Windows6.1-KB2859903-x86.CAB" />
+ </package>
+ </servicing>
+ <servicing>
+ <package action="install">
+ <assemblyIdentity name="Package_for_abc" version="10.2.1.0" language="neutral" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35"/>
+ <source location="%configsetroot%\abc.CAB" />
+ </package>
+ </servicing>
+</unattend>
+
diff --git a/spec/data/shef-config.rb b/spec/data/shef-config.rb
index 3c3ae9045a..efd601d6d8 100644
--- a/spec/data/shef-config.rb
+++ b/spec/data/shef-config.rb
@@ -1,10 +1,11 @@
-Ohai::Config[:disabled_plugins] << 'darwin::system_profiler' << 'darwin::kernel' << 'darwin::ssh_host_key' << 'network_listeners'
-Ohai::Config[:disabled_plugins] << "virtualization" << "darwin::virtualization"
-Ohai::Config[:disabled_plugins] << 'darwin::uptime' << 'darwin::filesystem' << 'dmi' << 'lanuages' << 'perl' << 'python' << 'java'
-Ohai::Config[:disabled_plugins] << "linux::block_device" << "linux::kernel" << "linux::ssh_host_key" << "linux::virtualization"
-Ohai::Config[:disabled_plugins] << "linux::cpu" << "linux::memory" << "ec2" << "rackspace" << "eucalyptus" << "ip_scopes"
-Ohai::Config[:disabled_plugins] << "solaris2::cpu" << "solaris2::dmi" << "solaris2::filesystem" << "solaris2::kernel"
-Ohai::Config[:disabled_plugins] << "solaris2::virtualization" << "solaris2::zpools"
-Ohai::Config[:disabled_plugins] << 'c' << 'php' << 'mono' << 'groovy' << 'lua' << 'erlang'
-Ohai::Config[:disabled_plugins] << "kernel" << "linux::filesystem" << "ruby"
-
+ohai[:disabled_plugins] << "darwin::system_profiler" << "darwin::kernel" << "darwin::ssh_host_key" << "network_listeners"
+ohai[:disabled_plugins] << "virtualization" << "darwin::virtualization"
+ohai[:disabled_plugins] << "darwin::uptime" << "darwin::filesystem" << "dmi" << "lanuages" << "perl" << "python" << "java"
+ohai[:disabled_plugins] << "linux::block_device" << "linux::kernel" << "linux::ssh_host_key" << "linux::virtualization"
+ohai[:disabled_plugins] << "linux::cpu" << "linux::memory" << "ec2" << "rackspace" << "eucalyptus" << "ip_scopes"
+ohai[:disabled_plugins] << "solaris2::cpu" << "solaris2::dmi" << "solaris2::filesystem" << "solaris2::kernel"
+ohai[:disabled_plugins] << "solaris2::virtualization" << "solaris2::zpools"
+ohai[:disabled_plugins] << "c" << "php" << "mono" << "groovy" << "lua" << "erlang"
+ohai[:disabled_plugins] << "kernel" << "linux::filesystem" << "ruby"
+chef_repo_path File.dirname(__FILE__)
+cookbook_path "#{chef_repo_path}/cookbooks"
diff --git a/spec/data/templates/chef-seattle20160930-4388-1crv7ef.txt b/spec/data/templates/chef-seattle20160930-4388-1crv7ef.txt
new file mode 100644
index 0000000000..f476ccd704
--- /dev/null
+++ b/spec/data/templates/chef-seattle20160930-4388-1crv7ef.txt
@@ -0,0 +1 @@
+Do do do do, do do do do, do do do do, do do do do \ No newline at end of file
diff --git a/spec/data/templates/chef-seattle20160930-4388-jjfoae.txt b/spec/data/templates/chef-seattle20160930-4388-jjfoae.txt
new file mode 100644
index 0000000000..f476ccd704
--- /dev/null
+++ b/spec/data/templates/chef-seattle20160930-4388-jjfoae.txt
@@ -0,0 +1 @@
+Do do do do, do do do do, do do do do, do do do do \ No newline at end of file
diff --git a/spec/data/templates/chef-seattle20160930-4388-umeq2c.txt b/spec/data/templates/chef-seattle20160930-4388-umeq2c.txt
new file mode 100644
index 0000000000..f476ccd704
--- /dev/null
+++ b/spec/data/templates/chef-seattle20160930-4388-umeq2c.txt
@@ -0,0 +1 @@
+Do do do do, do do do do, do do do do, do do do do \ No newline at end of file
diff --git a/spec/data/trusted_certs/example_no_cn.crt b/spec/data/trusted_certs/example_no_cn.crt
new file mode 100644
index 0000000000..6b42b40d99
--- /dev/null
+++ b/spec/data/trusted_certs/example_no_cn.crt
@@ -0,0 +1,36 @@
+-----BEGIN CERTIFICATE-----
+MIIGPzCCBCegAwIBAgIJAKwtLqBeqNzfMA0GCSqGSIb3DQEBBQUAMHIxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHU2VhdHRsZTEQMA4GA1UEChMH
+WW91Q29ycDETMBEGA1UECxMKT3BlcmF0aW9uczEdMBsGCSqGSIb3DQEJARYObWVA
+ZXhhbXBsZS5jb20wHhcNMTYxMDMxMTkxMzQ2WhcNMjYxMDI5MTkxMzQ2WjByMQsw
+CQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxEDAOBgNV
+BAoTB1lvdUNvcnAxEzARBgNVBAsTCk9wZXJhdGlvbnMxHTAbBgkqhkiG9w0BCQEW
+Dm1lQGV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
+s1OiWnMV3shxVccqzenDBww5rSou9Ab/VqujKisJ54dXyHukYMxh9MJwlRDsy0FB
+uKRAyNfhM43hSMYhtF7NS//D1lI/LDvIQkBaH8R834bvK102Avmsx7zKPOo/CUkd
+g7uuL2eRzRszEuAREH1E7/PpTj11CjirG9i7FlbKj7vDA1Nqvtb0kHdiQuH2Cojy
+Uf1uVFyE5UliFXtePDrxpOAfJUbcSdOLsK8olKHGCb0cfN/tCfbyEY8rHGsAUK2A
+afuHRTR7pRQwfqJ5EK3DBbbFz+GSi+9zWFOudfqTsczS/HtpMbF8HBwqBAa+mpU/
+UjmhpTYQ+rgVtWtEcttboeK6jvFBFLyQ6VRcrDi/8lmAnm1Q+RZk5g3GwZMhIMNU
+5XQZf6jsUsIFBuOaRyLn9dXkgyO7gOy8n8Yw+YdIFt29kaqZ6pu9kpS0jquxzSKj
+MVS4OrThLwgazfQu/BlOvJpQfcNPI/VP9c41yHKpeoIh6oxNDc/212/wwgwPgD83
+8YXddupaSuE++h9Z10CCZgwux8deTlMjzETIMiIo8R3KV0pJgZ11akeJ8USr+QQ2
++fO/GdpNUa5nNTgF3t4zTF3DPToqj6KDgxLhUdXopF1hLYgwr8FKOtn9KXP+I0hz
+hWzZoX9gwFLEPrUy265Gpw8TVTmNuSiiZtgJDSDKTBcCAwEAAaOB1zCB1DAdBgNV
+HQ4EFgQUr5Y6dxhyVxfhwFsEKLDIXxQ2zBswgaQGA1UdIwSBnDCBmYAUr5Y6dxhy
+VxfhwFsEKLDIXxQ2zBuhdqR0MHIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQ
+MA4GA1UEBxMHU2VhdHRsZTEQMA4GA1UEChMHWW91Q29ycDETMBEGA1UECxMKT3Bl
+cmF0aW9uczEdMBsGCSqGSIb3DQEJARYObWVAZXhhbXBsZS5jb22CCQCsLS6gXqjc
+3zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQBYXgqXAnocH6i8o47c
+BZPMGO9y4LCB4YNIrZPKRNFvRl2aolA5KiBxr6WJp1iczxVA4lCmXU1LGfvRPHec
+nHtVax3+Q1JCZhBSv/txQTjgn72qoJyCsPmjyWifbE1jFdRj0g74/Eu/0ku3L0vV
+jTlqzJXQIzRkQm+Y5OrZo92tXaOgO+C0qdd6gaEaIIya6bzrBpW95NtVymhXi2Qf
+7G7Z/yw8XhoQiDJaPHF6XavC3dYvi51cehnPR4E6Jok23kbJEe3Qw5Yh747JjSsS
+Sz07CKqTFcFjHI2f1sFdDjw34lj5mtOf3pEpRGGmvzkF8zm/sVQQ2rCKnqEe7zPy
+Bg9guzVpORG+g76hGFZcYnz78LLNrIYcuYoLcbbZh404wjmifVKO5OC1dRgmJTuc
+VnJe92568Y9cUAjrLztxp5gwXgYUllsXweJ2UGiHxSBqUfCCGG5vK5sYs52HR6wJ
+wRSvwk/VHifYPxJ54RRB51ebYjmD1j41tRseHdFq21qpXSvr9DFLUJBvdN9zA/6t
+xCBlXAdYxD0n0/bruUYNoXBeMhLp+WKSAQvTlVIyqoNQCo1OBBzBVNg9otl3jw5d
+1QOhodRqmS5UQAJptuXtk8WN8OZqMCCeogIfdpa5tJG+/fxFML9EvqedS4c05Wf/
+oYdVLVWSjyoA2l4Xb4LdexAgCg==
+-----END CERTIFICATE-----
diff --git a/spec/functional/assets/chefinittest b/spec/functional/assets/chefinittest
index 79e064cd5f..b67abbfa43 100755
--- a/spec/functional/assets/chefinittest
+++ b/spec/functional/assets/chefinittest
@@ -1,15 +1,17 @@
#!/bin/ksh
+TMPDIR="${TMPDIR:-/tmp}"
+
function create_chef_txt {
- touch /tmp/chefinittest.txt
+ touch $TMPDIR/chefinittest.txt
}
function delete_chef_txt {
- rm /tmp/chefinittest.txt
+ rm $TMPDIR/chefinittest.txt
}
function rename_chef_txt {
- mv /tmp/chefinittest.txt /tmp/$1
+ mv $TMPDIR/chefinittest.txt $TMPDIR/$1
}
case "$1" in
@@ -20,7 +22,7 @@ stop )
delete_chef_txt
;;
status )
- [ -f /tmp/chefinittest.txt ] || [ -f /tmp/chefinittest_reload.txt ] || [ -f /tmp/chefinittest_restart.txt ]
+ [ -f $TMPDIR/chefinittest.txt ] || [ -f $TMPDIR/chefinittest_reload.txt ] || [ -f $TMPDIR/chefinittest_restart.txt ]
;;
reload )
rename_chef_txt "chefinittest_reload.txt"
diff --git a/spec/functional/assets/chocolatey_feed/test-A.1.0.nupkg b/spec/functional/assets/chocolatey_feed/test-A.1.0.nupkg
index a2d3e7bb4e..0295507373 100644
--- a/spec/functional/assets/chocolatey_feed/test-A.1.0.nupkg
+++ b/spec/functional/assets/chocolatey_feed/test-A.1.0.nupkg
Binary files differ
diff --git a/spec/functional/assets/chocolatey_feed/test-A.1.5.nupkg b/spec/functional/assets/chocolatey_feed/test-A.1.5.nupkg
index 9ce9445689..19782211e3 100644
--- a/spec/functional/assets/chocolatey_feed/test-A.1.5.nupkg
+++ b/spec/functional/assets/chocolatey_feed/test-A.1.5.nupkg
Binary files differ
diff --git a/spec/functional/assets/chocolatey_feed/test-A.2.0.nupkg b/spec/functional/assets/chocolatey_feed/test-A.2.0.nupkg
index 250084d3ce..360c0843e2 100644
--- a/spec/functional/assets/chocolatey_feed/test-A.2.0.nupkg
+++ b/spec/functional/assets/chocolatey_feed/test-A.2.0.nupkg
Binary files differ
diff --git a/spec/functional/assets/chocolatey_feed/test-B.1.0.nupkg b/spec/functional/assets/chocolatey_feed/test-B.1.0.nupkg
index f275c78dc6..d82b0d09bc 100644
--- a/spec/functional/assets/chocolatey_feed/test-B.1.0.nupkg
+++ b/spec/functional/assets/chocolatey_feed/test-B.1.0.nupkg
Binary files differ
diff --git a/spec/functional/assets/testchefsubsys b/spec/functional/assets/testchefsubsys
index e9ff30d4aa..e5c2f8cfc4 100755
--- a/spec/functional/assets/testchefsubsys
+++ b/spec/functional/assets/testchefsubsys
@@ -5,7 +5,6 @@ sleep 120 &
pid="$!"
-trap 'echo I am going down, so killing off my processes..; kill $pid; exit' SIGHUP SIGINT
- SIGQUIT SIGTERM
+trap 'echo I am going down, so killing off my processes..; kill $pid; exit' SIGHUP SIGINT SIGQUIT SIGTERM
-wait \ No newline at end of file
+wait
diff --git a/spec/functional/assets/yumrepo/chef_rpm-1.10-1.aarch64.rpm b/spec/functional/assets/yumrepo/chef_rpm-1.10-1.aarch64.rpm
new file mode 100644
index 0000000000..808e5c64c9
--- /dev/null
+++ b/spec/functional/assets/yumrepo/chef_rpm-1.10-1.aarch64.rpm
Binary files differ
diff --git a/spec/functional/assets/yumrepo/chef_rpm-1.10-1.i686.rpm b/spec/functional/assets/yumrepo/chef_rpm-1.10-1.i686.rpm
new file mode 100644
index 0000000000..ed7b6ddc8e
--- /dev/null
+++ b/spec/functional/assets/yumrepo/chef_rpm-1.10-1.i686.rpm
Binary files differ
diff --git a/spec/functional/assets/yumrepo/chef_rpm-1.10-1.ppc64.rpm b/spec/functional/assets/yumrepo/chef_rpm-1.10-1.ppc64.rpm
new file mode 100644
index 0000000000..f3683b3c89
--- /dev/null
+++ b/spec/functional/assets/yumrepo/chef_rpm-1.10-1.ppc64.rpm
Binary files differ
diff --git a/spec/functional/assets/yumrepo/chef_rpm-1.10-1.ppc64le.rpm b/spec/functional/assets/yumrepo/chef_rpm-1.10-1.ppc64le.rpm
new file mode 100644
index 0000000000..4f8de433d9
--- /dev/null
+++ b/spec/functional/assets/yumrepo/chef_rpm-1.10-1.ppc64le.rpm
Binary files differ
diff --git a/spec/functional/assets/yumrepo/chef_rpm-1.10-1.s390x.rpm b/spec/functional/assets/yumrepo/chef_rpm-1.10-1.s390x.rpm
new file mode 100644
index 0000000000..98538293ff
--- /dev/null
+++ b/spec/functional/assets/yumrepo/chef_rpm-1.10-1.s390x.rpm
Binary files differ
diff --git a/spec/functional/assets/yumrepo/chef_rpm-1.10-1.src.rpm b/spec/functional/assets/yumrepo/chef_rpm-1.10-1.src.rpm
new file mode 100644
index 0000000000..03a92f1cfc
--- /dev/null
+++ b/spec/functional/assets/yumrepo/chef_rpm-1.10-1.src.rpm
Binary files differ
diff --git a/spec/functional/assets/yumrepo/chef_rpm-1.10-1.x86_64.rpm b/spec/functional/assets/yumrepo/chef_rpm-1.10-1.x86_64.rpm
new file mode 100644
index 0000000000..3533640780
--- /dev/null
+++ b/spec/functional/assets/yumrepo/chef_rpm-1.10-1.x86_64.rpm
Binary files differ
diff --git a/spec/functional/assets/yumrepo/chef_rpm-1.2-1.aarch64.rpm b/spec/functional/assets/yumrepo/chef_rpm-1.2-1.aarch64.rpm
new file mode 100644
index 0000000000..73d59bf3e2
--- /dev/null
+++ b/spec/functional/assets/yumrepo/chef_rpm-1.2-1.aarch64.rpm
Binary files differ
diff --git a/spec/functional/assets/yumrepo/chef_rpm-1.2-1.i686.rpm b/spec/functional/assets/yumrepo/chef_rpm-1.2-1.i686.rpm
new file mode 100644
index 0000000000..6637756abb
--- /dev/null
+++ b/spec/functional/assets/yumrepo/chef_rpm-1.2-1.i686.rpm
Binary files differ
diff --git a/spec/functional/assets/yumrepo/chef_rpm-1.2-1.ppc64.rpm b/spec/functional/assets/yumrepo/chef_rpm-1.2-1.ppc64.rpm
new file mode 100644
index 0000000000..677e43f83b
--- /dev/null
+++ b/spec/functional/assets/yumrepo/chef_rpm-1.2-1.ppc64.rpm
Binary files differ
diff --git a/spec/functional/assets/yumrepo/chef_rpm-1.2-1.ppc64le.rpm b/spec/functional/assets/yumrepo/chef_rpm-1.2-1.ppc64le.rpm
new file mode 100644
index 0000000000..df21a68780
--- /dev/null
+++ b/spec/functional/assets/yumrepo/chef_rpm-1.2-1.ppc64le.rpm
Binary files differ
diff --git a/spec/functional/assets/yumrepo/chef_rpm-1.2-1.s390x.rpm b/spec/functional/assets/yumrepo/chef_rpm-1.2-1.s390x.rpm
new file mode 100644
index 0000000000..02a771c6f5
--- /dev/null
+++ b/spec/functional/assets/yumrepo/chef_rpm-1.2-1.s390x.rpm
Binary files differ
diff --git a/spec/functional/assets/yumrepo/chef_rpm-1.2-1.src.rpm b/spec/functional/assets/yumrepo/chef_rpm-1.2-1.src.rpm
new file mode 100644
index 0000000000..5d4d13e18a
--- /dev/null
+++ b/spec/functional/assets/yumrepo/chef_rpm-1.2-1.src.rpm
Binary files differ
diff --git a/spec/functional/assets/yumrepo/chef_rpm-1.2-1.x86_64.rpm b/spec/functional/assets/yumrepo/chef_rpm-1.2-1.x86_64.rpm
new file mode 100644
index 0000000000..314c52f22e
--- /dev/null
+++ b/spec/functional/assets/yumrepo/chef_rpm-1.2-1.x86_64.rpm
Binary files differ
diff --git a/spec/functional/assets/yumrepo/repodata/4632d67cb92636e7575d911c24f0e04d3505a944e97c483abe0c3e73a7c62d33-filelists.sqlite.bz2 b/spec/functional/assets/yumrepo/repodata/4632d67cb92636e7575d911c24f0e04d3505a944e97c483abe0c3e73a7c62d33-filelists.sqlite.bz2
new file mode 100644
index 0000000000..3c5e406935
--- /dev/null
+++ b/spec/functional/assets/yumrepo/repodata/4632d67cb92636e7575d911c24f0e04d3505a944e97c483abe0c3e73a7c62d33-filelists.sqlite.bz2
Binary files differ
diff --git a/spec/functional/assets/yumrepo/repodata/74599b793e54d877323837d2d81a1c3c594c44e4335f9528234bb490f7b9b439-other.xml.gz b/spec/functional/assets/yumrepo/repodata/74599b793e54d877323837d2d81a1c3c594c44e4335f9528234bb490f7b9b439-other.xml.gz
new file mode 100644
index 0000000000..ddccd353ae
--- /dev/null
+++ b/spec/functional/assets/yumrepo/repodata/74599b793e54d877323837d2d81a1c3c594c44e4335f9528234bb490f7b9b439-other.xml.gz
Binary files differ
diff --git a/spec/functional/assets/yumrepo/repodata/a845d418f919d2115ab95a56b2c76f6825ad0d0bede49181a55c04f58995d057-primary.sqlite.bz2 b/spec/functional/assets/yumrepo/repodata/a845d418f919d2115ab95a56b2c76f6825ad0d0bede49181a55c04f58995d057-primary.sqlite.bz2
new file mode 100644
index 0000000000..50bbe1f37f
--- /dev/null
+++ b/spec/functional/assets/yumrepo/repodata/a845d418f919d2115ab95a56b2c76f6825ad0d0bede49181a55c04f58995d057-primary.sqlite.bz2
Binary files differ
diff --git a/spec/functional/assets/yumrepo/repodata/af9b7cf9ef23bd7b43068d74a460f3b5d06753d638e58e4a0c9edc35bfb9cdc4-other.sqlite.bz2 b/spec/functional/assets/yumrepo/repodata/af9b7cf9ef23bd7b43068d74a460f3b5d06753d638e58e4a0c9edc35bfb9cdc4-other.sqlite.bz2
new file mode 100644
index 0000000000..e341e1df69
--- /dev/null
+++ b/spec/functional/assets/yumrepo/repodata/af9b7cf9ef23bd7b43068d74a460f3b5d06753d638e58e4a0c9edc35bfb9cdc4-other.sqlite.bz2
Binary files differ
diff --git a/spec/functional/assets/yumrepo/repodata/bdb4f5f1492a3b9532f22c43110a81500dd744f23da0aec5c33b2a41317c737d-filelists.xml.gz b/spec/functional/assets/yumrepo/repodata/bdb4f5f1492a3b9532f22c43110a81500dd744f23da0aec5c33b2a41317c737d-filelists.xml.gz
new file mode 100644
index 0000000000..9636d5b868
--- /dev/null
+++ b/spec/functional/assets/yumrepo/repodata/bdb4f5f1492a3b9532f22c43110a81500dd744f23da0aec5c33b2a41317c737d-filelists.xml.gz
Binary files differ
diff --git a/spec/functional/assets/yumrepo/repodata/c10d1d34ce99e02f12ec96ef68360543ab1bb7c3cb81a4a2bf78df7d8597e9df-primary.xml.gz b/spec/functional/assets/yumrepo/repodata/c10d1d34ce99e02f12ec96ef68360543ab1bb7c3cb81a4a2bf78df7d8597e9df-primary.xml.gz
new file mode 100644
index 0000000000..afa4b4db9a
--- /dev/null
+++ b/spec/functional/assets/yumrepo/repodata/c10d1d34ce99e02f12ec96ef68360543ab1bb7c3cb81a4a2bf78df7d8597e9df-primary.xml.gz
Binary files differ
diff --git a/spec/functional/assets/yumrepo/repodata/filelists.xml.gz b/spec/functional/assets/yumrepo/repodata/filelists.xml.gz
new file mode 100644
index 0000000000..954b9abcd7
--- /dev/null
+++ b/spec/functional/assets/yumrepo/repodata/filelists.xml.gz
Binary files differ
diff --git a/spec/functional/assets/yumrepo/repodata/other.xml.gz b/spec/functional/assets/yumrepo/repodata/other.xml.gz
new file mode 100644
index 0000000000..db6ffa611d
--- /dev/null
+++ b/spec/functional/assets/yumrepo/repodata/other.xml.gz
Binary files differ
diff --git a/spec/functional/assets/yumrepo/repodata/primary.xml.gz b/spec/functional/assets/yumrepo/repodata/primary.xml.gz
new file mode 100644
index 0000000000..fe06057d60
--- /dev/null
+++ b/spec/functional/assets/yumrepo/repodata/primary.xml.gz
Binary files differ
diff --git a/spec/functional/assets/yumrepo/repodata/repomd.xml b/spec/functional/assets/yumrepo/repodata/repomd.xml
new file mode 100644
index 0000000000..31be5c80f5
--- /dev/null
+++ b/spec/functional/assets/yumrepo/repodata/repomd.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo">
+ <data type="other">
+ <location href="repodata/other.xml.gz"/>
+ <checksum type="sha">6d44b25ecb901d242a28a6a457d9c6a240e93a72</checksum>
+ <timestamp>1512520884</timestamp>
+ <open-checksum type="sha">f3e463916922801d1be801e28304c84b2ee58638</open-checksum>
+ </data>
+ <data type="filelists">
+ <location href="repodata/filelists.xml.gz"/>
+ <checksum type="sha">21dcce9b122a907aa94ec7ec6108006c0f26e7c8</checksum>
+ <timestamp>1512520884</timestamp>
+ <open-checksum type="sha">9241f39704584bd27bfc0cf7b5e8aaa21945deb9</open-checksum>
+ </data>
+ <data type="primary">
+ <location href="repodata/primary.xml.gz"/>
+ <checksum type="sha">6896c706000889416d769616f32e381db3a46ef2</checksum>
+ <timestamp>1512520884</timestamp>
+ <open-checksum type="sha">976e163091ffd175e2bad28d6b3564495ec4b1e9</open-checksum>
+ </data>
+</repomd>
diff --git a/spec/functional/event_loggers/windows_eventlog_spec.rb b/spec/functional/event_loggers/windows_eventlog_spec.rb
index 78aed7409f..8a9475680d 100644
--- a/spec/functional/event_loggers/windows_eventlog_spec.rb
+++ b/spec/functional/event_loggers/windows_eventlog_spec.rb
@@ -19,12 +19,12 @@
require "spec_helper"
require "securerandom"
require "chef/event_loggers/windows_eventlog"
-if Chef::Platform.windows? && (not Chef::Platform.windows_server_2003?)
+if Chef::Platform.windows?
require "win32/eventlog"
include Win32
end
-describe Chef::EventLoggers::WindowsEventLogger, :windows_only, :not_supported_on_win2k3 do
+describe Chef::EventLoggers::WindowsEventLogger, :windows_only do
def rand
random.rand(1 << 32).to_s
end
@@ -48,25 +48,28 @@ describe Chef::EventLoggers::WindowsEventLogger, :windows_only, :not_supported_o
it "writes run_start event with event_id 10000 and contains version" do
logger.run_start(version)
- expect(event_log.read(flags, offset).any? { |e| e.source == "Chef" && e.event_id == 10000 &&
- e.string_inserts[0].include?(version)}).to be_truthy
+ expect(event_log.read(flags, offset).any? do |e|
+ e.source == "Chef" && e.event_id == 10000 &&
+ e.string_inserts[0].include?(version) end).to be_truthy
end
it "writes run_started event with event_id 10001 and contains the run_id" do
logger.run_started(run_status)
- expect(event_log.read(flags, offset).any? { |e| e.source == "Chef" && e.event_id == 10001 &&
- e.string_inserts[0].include?(run_id)}).to be_truthy
+ expect(event_log.read(flags, offset).any? do |e|
+ e.source == "Chef" && e.event_id == 10001 &&
+ e.string_inserts[0].include?(run_id) end).to be_truthy
end
it "writes run_completed event with event_id 10002 and contains the run_id and elapsed time" do
logger.run_started(run_status)
logger.run_completed(node)
- expect(event_log.read(flags, offset).any? { |e| e.source == "Chef" && e.event_id == 10002 &&
- e.string_inserts[0].include?(run_id) &&
- e.string_inserts[1].include?(elapsed_time.to_s)
- }).to be_truthy
+ expect(event_log.read(flags, offset).any? do |e|
+ e.source == "Chef" && e.event_id == 10002 &&
+ e.string_inserts[0].include?(run_id) &&
+ e.string_inserts[1].include?(elapsed_time.to_s)
+ end).to be_truthy
end
it "writes run_failed event with event_id 10003 and contains the run_id, elapsed time, and exception info" do
diff --git a/spec/functional/file_content_management/deploy_strategies_spec.rb b/spec/functional/file_content_management/deploy_strategies_spec.rb
index 7c54af347c..9e2131388f 100644
--- a/spec/functional/file_content_management/deploy_strategies_spec.rb
+++ b/spec/functional/file_content_management/deploy_strategies_spec.rb
@@ -209,7 +209,7 @@ describe Chef::FileContentManagement::Deploy::MvUnix, :unix_only do
end
# On Unix we won't have loaded the file, avoid undefined constant errors:
-class Chef::FileContentManagement::Deploy::MvWindows ; end
+class Chef::FileContentManagement::Deploy::MvWindows; end
describe Chef::FileContentManagement::Deploy::MvWindows, :windows_only do
diff --git a/spec/functional/http/simple_spec.rb b/spec/functional/http/simple_spec.rb
index aeb7be7d86..fcabd3f308 100644
--- a/spec/functional/http/simple_spec.rb
+++ b/spec/functional/http/simple_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,7 +27,13 @@ describe Chef::HTTP::Simple do
let(:http_client_disable_gzip) { described_class.new(source, { :disable_gzip => true } ) }
before(:all) do
- start_tiny_server
+ start_tiny_server(RequestTimeout: 1)
+ end
+
+ before(:each) do
+ Chef::Config[:rest_timeout] = 2
+ Chef::Config[:http_retry_delay] = 0
+ Chef::Config[:http_retry_count] = 0
end
after(:all) do
@@ -46,10 +52,10 @@ describe Chef::HTTP::Simple do
end
shared_examples_for "validates content length and throws an exception" do
- it "successfully downloads a streaming request" do
+ it "a streaming request throws a content length exception" do
expect { http_client.streaming_request(source) }.to raise_error(Chef::Exceptions::ContentLengthMismatch)
end
- it "successfully does a non-streaming GET request" do
+ it "a non-streaming GET request throws a content length exception" do
expect { http_client.get(source) }.to raise_error(Chef::Exceptions::ContentLengthMismatch)
end
end
@@ -81,11 +87,11 @@ describe Chef::HTTP::Simple do
it_behaves_like "downloading all the things"
- context "when Chef::Log.level = :debug" do
+ context "when Chef::Log.level = :trace" do
before do
- Chef::Log.level = :debug
+ Chef::Log.level = :trace
@debug_log = ""
- allow(Chef::Log).to receive(:debug) { |str| @debug_log << str }
+ allow(Chef::Log).to receive(:trace) { |str| @debug_log << str }
end
let(:source) { "http://localhost:9000" }
diff --git a/spec/functional/knife/cookbook_delete_spec.rb b/spec/functional/knife/cookbook_delete_spec.rb
index a43e3a36c4..7438ef9841 100644
--- a/spec/functional/knife/cookbook_delete_spec.rb
+++ b/spec/functional/knife/cookbook_delete_spec.rb
@@ -20,86 +20,75 @@ require "spec_helper"
require "tiny_server"
describe Chef::Knife::CookbookDelete do
- before(:all) do
- @server = TinyServer::Manager.new
- @server.start
+ let(:server) { TinyServer::Manager.new }
+ let(:api) { TinyServer::API.instance }
+ let(:knife_stdout) { StringIO.new }
+ let(:knife_stderr) { StringIO.new }
+ let(:knife) do
+ knife = Chef::Knife::CookbookDelete.new
+ allow(knife.ui).to receive(:stdout).and_return(knife_stdout)
+ allow(knife.ui).to receive(:stderr).and_return(knife_stderr)
+ knife
end
before(:each) do
- @knife = Chef::Knife::CookbookDelete.new
- @api = TinyServer::API.instance
- @api.clear
+ server.start
+ api.clear
Chef::Config[:node_name] = nil
Chef::Config[:client_key] = nil
Chef::Config[:chef_server_url] = "http://localhost:9000"
end
- after(:all) do
- @server.stop
+ after(:each) do
+ server.stop
end
context "when the cookbook doesn't exist" do
- let(:log_output) { StringIO.new }
-
before do
- @knife.name_args = %w{no-such-cookbook}
- @api.get("/cookbooks/no-such-cookbook", 404, Chef::JSONCompat.to_json({ "error" => "dear Tim, no. -Sent from my iPad" }))
- end
-
- around do |ex|
- old_logger = Chef::Log.logger
- old_level = Chef::Log.level
- begin
- Chef::Log.logger = Logger.new(log_output)
- Chef::Log.level = :debug
- ex.run
- ensure
- Chef::Log.logger = old_logger
- Chef::Log.level = old_level
- end
+ knife.name_args = %w{no-such-cookbook}
+ api.get("/cookbooks/no-such-cookbook", 404, Chef::JSONCompat.to_json({ "error" => "dear Tim, no. -Sent from my iPad" }))
end
it "logs an error and exits" do
- allow(@knife.ui).to receive(:stderr).and_return(log_output)
- expect { @knife.run }.to raise_error(SystemExit)
- expect(log_output.string).to match(/Cannot find a cookbook named no-such-cookbook to delete/)
+ expect { knife.run }.to raise_error(SystemExit)
+ expect(knife_stderr.string).to match(/Cannot find a cookbook named no-such-cookbook to delete/)
end
end
context "when there is only one version of a cookbook" do
before do
- @knife.name_args = %w{obsolete-cookbook}
+ knife.name_args = %w{obsolete-cookbook}
@cookbook_list = { "obsolete-cookbook" => { "versions" => ["version" => "1.0.0"] } }
- @api.get("/cookbooks/obsolete-cookbook", 200, Chef::JSONCompat.to_json(@cookbook_list))
+ api.get("/cookbooks/obsolete-cookbook", 200, Chef::JSONCompat.to_json(@cookbook_list))
end
it "asks for confirmation, then deletes the cookbook" do
stdin, stdout = StringIO.new("y\n"), StringIO.new
- allow(@knife.ui).to receive(:stdin).and_return(stdin)
- allow(@knife.ui).to receive(:stdout).and_return(stdout)
+ allow(knife.ui).to receive(:stdin).and_return(stdin)
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
cb100_deleted = false
- @api.delete("/cookbooks/obsolete-cookbook/1.0.0", 200) { cb100_deleted = true; "[\"true\"]" }
+ api.delete("/cookbooks/obsolete-cookbook/1.0.0", 200) { cb100_deleted = true; "[\"true\"]" }
- @knife.run
+ knife.run
expect(stdout.string).to match(/#{Regexp.escape('Do you really want to delete obsolete-cookbook version 1.0.0? (Y/N)')}/)
expect(cb100_deleted).to be_truthy
end
it "asks for confirmation before purging" do
- @knife.config[:purge] = true
+ knife.config[:purge] = true
stdin, stdout = StringIO.new("y\ny\n"), StringIO.new
- allow(@knife.ui).to receive(:stdin).and_return(stdin)
- allow(@knife.ui).to receive(:stdout).and_return(stdout)
+ allow(knife.ui).to receive(:stdin).and_return(stdin)
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
cb100_deleted = false
- @api.delete("/cookbooks/obsolete-cookbook/1.0.0?purge=true", 200) { cb100_deleted = true; "[\"true\"]" }
+ api.delete("/cookbooks/obsolete-cookbook/1.0.0?purge=true", 200) { cb100_deleted = true; "[\"true\"]" }
- @knife.run
+ knife.run
expect(stdout.string).to match(/#{Regexp.escape('Are you sure you want to purge files')}/)
expect(stdout.string).to match(/#{Regexp.escape('Do you really want to delete obsolete-cookbook version 1.0.0? (Y/N)')}/)
@@ -111,21 +100,21 @@ describe Chef::Knife::CookbookDelete do
context "when there are several versions of a cookbook" do
before do
- @knife.name_args = %w{obsolete-cookbook}
+ knife.name_args = %w{obsolete-cookbook}
versions = ["1.0.0", "1.1.0", "1.2.0"]
with_version = lambda { |version| { "version" => version } }
@cookbook_list = { "obsolete-cookbook" => { "versions" => versions.map(&with_version) } }
- @api.get("/cookbooks/obsolete-cookbook", 200, Chef::JSONCompat.to_json(@cookbook_list))
+ api.get("/cookbooks/obsolete-cookbook", 200, Chef::JSONCompat.to_json(@cookbook_list))
end
it "deletes all versions of a cookbook when given the '-a' flag" do
- @knife.config[:all] = true
- @knife.config[:yes] = true
+ knife.config[:all] = true
+ knife.config[:yes] = true
cb100_deleted = cb110_deleted = cb120_deleted = nil
- @api.delete("/cookbooks/obsolete-cookbook/1.0.0", 200) { cb100_deleted = true; "[\"true\"]" }
- @api.delete("/cookbooks/obsolete-cookbook/1.1.0", 200) { cb110_deleted = true; "[\"true\"]" }
- @api.delete("/cookbooks/obsolete-cookbook/1.2.0", 200) { cb120_deleted = true; "[\"true\"]" }
- @knife.run
+ api.delete("/cookbooks/obsolete-cookbook/1.0.0", 200) { cb100_deleted = true; "[\"true\"]" }
+ api.delete("/cookbooks/obsolete-cookbook/1.1.0", 200) { cb110_deleted = true; "[\"true\"]" }
+ api.delete("/cookbooks/obsolete-cookbook/1.2.0", 200) { cb120_deleted = true; "[\"true\"]" }
+ knife.run
expect(cb100_deleted).to be_truthy
expect(cb110_deleted).to be_truthy
@@ -134,28 +123,28 @@ describe Chef::Knife::CookbookDelete do
it "asks which version to delete and deletes that when not given the -a flag" do
cb100_deleted = cb110_deleted = cb120_deleted = nil
- @api.delete("/cookbooks/obsolete-cookbook/1.0.0", 200) { cb100_deleted = true; "[\"true\"]" }
+ api.delete("/cookbooks/obsolete-cookbook/1.0.0", 200) { cb100_deleted = true; "[\"true\"]" }
stdin, stdout = StringIO.new, StringIO.new
- allow(@knife.ui).to receive(:stdin).and_return(stdin)
- allow(@knife.ui).to receive(:stdout).and_return(stdout)
+ allow(knife.ui).to receive(:stdin).and_return(stdin)
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
stdin << "1\n"
stdin.rewind
- @knife.run
+ knife.run
expect(cb100_deleted).to be_truthy
expect(stdout.string).to match(/Which version\(s\) do you want to delete\?/)
end
it "deletes all versions of the cookbook when not given the -a flag and the user chooses to delete all" do
cb100_deleted = cb110_deleted = cb120_deleted = nil
- @api.delete("/cookbooks/obsolete-cookbook/1.0.0", 200) { cb100_deleted = true; "[\"true\"]" }
- @api.delete("/cookbooks/obsolete-cookbook/1.1.0", 200) { cb110_deleted = true; "[\"true\"]" }
- @api.delete("/cookbooks/obsolete-cookbook/1.2.0", 200) { cb120_deleted = true; "[\"true\"]" }
+ api.delete("/cookbooks/obsolete-cookbook/1.0.0", 200) { cb100_deleted = true; "[\"true\"]" }
+ api.delete("/cookbooks/obsolete-cookbook/1.1.0", 200) { cb110_deleted = true; "[\"true\"]" }
+ api.delete("/cookbooks/obsolete-cookbook/1.2.0", 200) { cb120_deleted = true; "[\"true\"]" }
stdin, stdout = StringIO.new("4\n"), StringIO.new
- allow(@knife.ui).to receive(:stdin).and_return(stdin)
- allow(@knife.ui).to receive(:stdout).and_return(stdout)
+ allow(knife.ui).to receive(:stdin).and_return(stdin)
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
- @knife.run
+ knife.run
expect(cb100_deleted).to be_truthy
expect(cb110_deleted).to be_truthy
diff --git a/spec/functional/knife/exec_spec.rb b/spec/functional/knife/exec_spec.rb
index 838d15671c..ac8f617a90 100644
--- a/spec/functional/knife/exec_spec.rb
+++ b/spec/functional/knife/exec_spec.rb
@@ -20,11 +20,15 @@ require "spec_helper"
require "tiny_server"
describe Chef::Knife::Exec do
- before(:all) do
- @server = TinyServer::Manager.new#(:debug => true)
+ before(:each) do
+ @server = TinyServer::Manager.new #(:debug => true)
@server.start
end
+ after(:each) do
+ @server.stop
+ end
+
before(:each) do
@knife = Chef::Knife::Exec.new
@api = TinyServer::API.instance
@@ -37,10 +41,6 @@ describe Chef::Knife::Exec do
$output = StringIO.new
end
- after(:all) do
- @server.stop
- end
-
it "executes a script in the context of the chef-shell main context" do
@node = Chef::Node.new
@node.name("ohai-world")
diff --git a/spec/functional/knife/ssh_spec.rb b/spec/functional/knife/ssh_spec.rb
index a23220ed52..3defbe781f 100644
--- a/spec/functional/knife/ssh_spec.rb
+++ b/spec/functional/knife/ssh_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright 2010-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,13 +21,13 @@ require "tiny_server"
describe Chef::Knife::Ssh do
- before(:all) do
+ before(:each) do
Chef::Knife::Ssh.load_deps
@server = TinyServer::Manager.new
@server.start
end
- after(:all) do
+ after(:each) do
@server.stop
end
@@ -181,11 +181,11 @@ describe Chef::Knife::Ssh do
it "uses the ssh_attribute" do
@knife.run
- expect(@knife.get_ssh_attribute(Chef::Node.new)).to eq("ec2.public_hostname")
+ expect(@knife.get_ssh_attribute({ "target" => "ec2.public_hostname" })).to eq("ec2.public_hostname")
end
end
- context "when knife[:ssh_attribute] is not provided]" do
+ context "when knife[:ssh_attribute] is not provided" do
before do
setup_knife(["*:*", "uptime"])
Chef::Config[:knife][:ssh_attribute] = nil
@@ -193,28 +193,75 @@ describe Chef::Knife::Ssh do
it "uses the default" do
@knife.run
- expect(@knife.get_ssh_attribute(Chef::Node.new)).to eq("fqdn")
+ expect(@knife.get_ssh_attribute({ "fqdn" => "fqdn" })).to eq("fqdn")
end
end
- context "when -a ec2.public_ipv4 is provided" do
+ context "when -a ec2.public_public_hostname is provided" do
before do
- setup_knife(["-a ec2.public_hostname", "*:*", "uptime"])
+ setup_knife(["-a", "ec2.public_hostname", "*:*", "uptime"])
Chef::Config[:knife][:ssh_attribute] = nil
end
it "should use the value on the command line" do
@knife.run
- expect(@knife.config[:attribute]).to eq("ec2.public_hostname")
+ expect(@knife.config[:ssh_attribute]).to eq("ec2.public_hostname")
end
it "should override what is set in knife.rb" do
# This is the setting imported from knife.rb
Chef::Config[:knife][:ssh_attribute] = "fqdn"
# Then we run knife with the -a flag, which sets the above variable
- setup_knife(["-a ec2.public_hostname", "*:*", "uptime"])
+ setup_knife(["-a", "ec2.public_hostname", "*:*", "uptime"])
@knife.run
- expect(@knife.config[:attribute]).to eq("ec2.public_hostname")
+ expect(@knife.config[:ssh_attribute]).to eq("ec2.public_hostname")
+ end
+ end
+ end
+
+ describe "prefix" do
+ context "when knife[:prefix_attribute] is set" do
+ before do
+ setup_knife(["*:*", "uptime"])
+ Chef::Config[:knife][:prefix_attribute] = "name"
+ end
+
+ it "uses the prefix_attribute" do
+ @knife.run
+ expect(@knife.get_prefix_attribute({ "prefix" => "name" })).to eq("name")
+ end
+ end
+
+ context "when knife[:prefix_attribute] is not provided" do
+ before do
+ setup_knife(["*:*", "uptime"])
+ Chef::Config[:knife][:prefix_attribute] = nil
+ end
+
+ it "falls back to nil" do
+ @knife.run
+ expect(@knife.get_prefix_attribute({})).to eq(nil)
+ end
+ end
+
+ context "when --prefix-attribute ec2.public_public_hostname is provided" do
+ before do
+ setup_knife(["--prefix-attribute", "ec2.public_hostname", "*:*", "uptime"])
+ Chef::Config[:knife][:prefix_attribute] = nil
+ end
+
+ it "should use the value on the command line" do
+ @knife.run
+ expect(@knife.config[:prefix_attribute]).to eq("ec2.public_hostname")
+ end
+
+ it "should override what is set in knife.rb" do
+ # This is the setting imported from knife.rb
+ Chef::Config[:knife][:prefix_attribute] = "fqdn"
+ # Then we run knife with the -b flag, which sets the above variable
+ setup_knife(["--prefix-attribute", "ec2.public_hostname", "*:*", "uptime"])
+ @knife.run
+ expect(@knife.config[:prefix_attribute]).to eq("ec2.public_hostname")
end
end
end
@@ -246,6 +293,34 @@ describe Chef::Knife::Ssh do
end
end
+ context "when knife[:ssh_gateway_identity] is set" do
+ before do
+ setup_knife(["*:*", "uptime"])
+ Chef::Config[:knife][:ssh_gateway] = "user@ec2.public_hostname"
+ Chef::Config[:knife][:ssh_gateway_identity] = "~/.ssh/aws-gateway.rsa"
+ end
+
+ it "uses the ssh_gateway_identity file" do
+ expect(@knife.session).to receive(:via).with("ec2.public_hostname", "user", { :keys => File.expand_path("#{ENV['HOME']}/.ssh/aws-gateway.rsa").squeeze("/"), :keys_only => true })
+ @knife.run
+ expect(@knife.config[:ssh_gateway_identity]).to eq("~/.ssh/aws-gateway.rsa")
+ end
+ end
+
+ context "when -ssh-gateway-identity is provided and knife[:ssh_gateway] is set" do
+ before do
+ setup_knife(["--ssh-gateway-identity", "~/.ssh/aws-gateway.rsa", "*:*", "uptime"])
+ Chef::Config[:knife][:ssh_gateway] = "user@ec2.public_hostname"
+ Chef::Config[:knife][:ssh_gateway_identity] = nil
+ end
+
+ it "uses the ssh_gateway_identity file" do
+ expect(@knife.session).to receive(:via).with("ec2.public_hostname", "user", { :keys => File.expand_path("#{ENV['HOME']}/.ssh/aws-gateway.rsa").squeeze("/"), :keys_only => true })
+ @knife.run
+ expect(@knife.config[:ssh_gateway_identity]).to eq("~/.ssh/aws-gateway.rsa")
+ end
+ end
+
context "when the gateway requires a password" do
before do
setup_knife(["-G user@ec2.public_hostname", "*:*", "uptime"])
@@ -276,9 +351,9 @@ describe Chef::Knife::Ssh do
Chef::Config[:client_key] = nil
Chef::Config[:chef_server_url] = "http://localhost:9000"
- @api.get("/search/node?q=*:*&sort=X_CHEF_id_CHEF_X%20asc&start=0", 200) {
- %({"total":1, "start":0, "rows":[{"name":"i-xxxxxxxx", "json_class":"Chef::Node", "automatic":{"fqdn":"the.fqdn", "ec2":{"public_hostname":"the_public_hostname"}},"recipes":[]}]})
- }
+ @api.post("/search/node?q=*:*&start=0&rows=1000", 200) do
+ %({"total":1, "start":0, "rows":[{"data": {"fqdn":"the.fqdn", "target": "the_public_hostname"}}]})
+ end
end
end
diff --git a/spec/functional/mixin/from_file_spec.rb b/spec/functional/mixin/from_file_spec.rb
new file mode 100644
index 0000000000..a279f48790
--- /dev/null
+++ b/spec/functional/mixin/from_file_spec.rb
@@ -0,0 +1,82 @@
+#
+# Copyright:: Copyright 2014-2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Mixin::FromFile do
+ REAL_DATA = File.join(CHEF_SPEC_DATA, "mixin", "real_data.rb")
+ INVALID_DATA = File.join(CHEF_SPEC_DATA, "mixin", "invalid_data.rb")
+ NO_DATA = File.join(CHEF_SPEC_DATA, "mixin", "non_existant_data.rb")
+
+ class TestData
+ include Chef::Mixin::FromFile
+
+ def a(a = nil)
+ @a = a if a
+ @a
+ end
+ end
+
+ class ClassTestData
+ class <<self
+ include Chef::Mixin::FromFile
+
+ def a(a = nil)
+ @a = a if a
+ @a
+ end
+ end
+ end
+
+ describe "from_file" do
+ it "should load data" do
+ datum = TestData.new
+ datum.from_file(REAL_DATA)
+ expect(datum.a).to eq(:foo)
+ end
+
+ it "should load class data" do
+ datum = ClassTestData
+ datum.class_from_file(REAL_DATA)
+ expect(datum.a).to eq(:foo)
+ end
+
+ it "should set source_file" do
+ datum = TestData.new
+ datum.from_file(REAL_DATA)
+ expect(datum.source_file).to eq(REAL_DATA)
+ end
+
+ it "should set class source_file" do
+ datum = ClassTestData
+ datum.class_from_file(REAL_DATA)
+ expect(datum.source_file).to eq(REAL_DATA)
+ end
+
+ it "should fail on invalid data" do
+ datum = TestData.new
+ expect do
+ datum.from_file(INVALID_DATA)
+ end.to raise_error(NoMethodError)
+ end
+
+ it "should fail on nonexistant data" do
+ datum = TestData.new
+ expect { datum.from_file(NO_DATA) }.to raise_error(IOError)
+ end
+ end
+end
diff --git a/spec/functional/mixin/powershell_out_spec.rb b/spec/functional/mixin/powershell_out_spec.rb
index 293e9552ab..d6fba5b084 100644
--- a/spec/functional/mixin/powershell_out_spec.rb
+++ b/spec/functional/mixin/powershell_out_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,7 +23,7 @@ describe Chef::Mixin::PowershellOut, windows_only: true do
describe "#powershell_out" do
it "runs a powershell command and collects stdout" do
- expect(powershell_out("get-process").run_command.stdout).to match /Handles\s+NPM\(K\)\s+PM\(K\)\s+WS\(K\)\s+VM\(M\)\s+CPU\(s\)\s+Id\s+ProcessName/
+ expect(powershell_out("get-process").run_command.stdout).to match /Handles/
end
it "does not raise exceptions when the command is invalid" do
@@ -33,7 +33,7 @@ describe Chef::Mixin::PowershellOut, windows_only: true do
describe "#powershell_out!" do
it "runs a powershell command and collects stdout" do
- expect(powershell_out!("get-process").run_command.stdout).to match /Handles\s+NPM\(K\)\s+PM\(K\)\s+WS\(K\)\s+VM\(M\)\s+CPU\(s\)\s+Id\s+ProcessName/
+ expect(powershell_out!("get-process").run_command.stdout).to match /Handles/
end
it "raises exceptions when the command is invalid" do
diff --git a/spec/functional/mixin/user_context_spec.rb b/spec/functional/mixin/user_context_spec.rb
new file mode 100644
index 0000000000..802b1db9f1
--- /dev/null
+++ b/spec/functional/mixin/user_context_spec.rb
@@ -0,0 +1,117 @@
+#
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+require "chef/win32/api" if Chef::Platform.windows?
+require "chef/win32/api/error" if Chef::Platform.windows?
+require "chef/mixin/user_context"
+
+describe Chef::Mixin::UserContext, windows_only: true do
+ include Chef::Mixin::UserContext
+
+ let(:get_user_name_a) do
+ FFI.ffi_lib "advapi32.dll"
+ FFI.attach_function :GetUserNameA, [ :pointer, :pointer ], :bool
+ end
+
+ let(:process_username) do
+ name_size = FFI::Buffer.new(:long).write_long(0)
+ succeeded = get_user_name_a.call(nil, name_size)
+ last_error = FFI::LastError.error
+ if succeeded || last_error != Chef::ReservedNames::Win32::API::Error::ERROR_INSUFFICIENT_BUFFER
+ raise Chef::Exceptions::Win32APIError, "Expected ERROR_INSUFFICIENT_BUFFER from GetUserNameA but it returned the following error: #{last_error}"
+ end
+ user_name = FFI::MemoryPointer.new :char, (name_size.read_long)
+ succeeded = get_user_name_a.call(user_name, name_size)
+ last_error = FFI::LastError.error
+ if succeeded == 0 || last_error != 0
+ raise Chef::Exceptions::Win32APIError, "GetUserNameA failed with #{lasterror}"
+ end
+ user_name.read_string
+ end
+
+ let(:test_user) { "chefuserctx3" }
+ let(:test_domain) { windows_nonadmin_user_domain }
+ let(:test_password) { "j823jfxK3;2Xe1" }
+
+ let(:username_domain_qualification) { nil }
+ let(:username_with_conditional_domain) { username_domain_qualification.nil? ? username_to_impersonate : "#{username_domain_qualification}\\#{username_to_impersonate}" }
+
+ let(:windows_nonadmin_user) { test_user }
+ let(:windows_nonadmin_user_password) { test_password }
+
+ let(:username_while_impersonating) do
+ username = nil
+ with_user_context(username_with_conditional_domain, username_to_impersonate_password, domain_to_impersonate) do
+ username = process_username
+ end
+ username
+ end
+
+ before do
+ allow_any_instance_of(described_class).to receive(:node).and_return({ "platform_family" => "windows" })
+ end
+
+ shared_examples_for "method that executes the block while impersonating the alternate user" do
+ it "uses different credentials for other network connections" do
+ allow_any_instance_of(Chef::Util::Windows::LogonSession).to receive(:validate_session_open!).and_return(true)
+ expect(username_while_impersonating.downcase).not_to eq(username_to_impersonate.downcase)
+ end
+ end
+
+ describe "#with_user_context" do
+ context "when the user and domain are both nil" do
+ let(:username_to_impersonate) { nil }
+ let(:domain_to_impersonate) { nil }
+ let(:username_to_impersonate_password) { nil }
+
+ it "has the same token and username as the process" do
+ expect(username_while_impersonating.downcase).to eq(ENV["username"].downcase)
+ end
+ end
+
+ context "when a non-nil user is specified" do
+ include_context "a non-admin Windows user"
+ context "when a username different than the process user is specified" do
+ let(:username_to_impersonate) { test_user }
+ let(:username_to_impersonate_password) { test_password }
+ context "when an explicit domain is given with a valid password" do
+ let(:domain_to_impersonate) { test_domain }
+ it "uses different credentials for other network connections" do
+ expect(username_while_impersonating.downcase).not_to eq(username_to_impersonate.downcase)
+ end
+ end
+
+ context "when a valid password and a non-qualified user is given and no domain is specified" do
+ let(:domain_to_impersonate) { "." }
+ it_behaves_like "method that executes the block while impersonating the alternate user"
+ end
+
+ it "raises an error user if specified with the wrong password" do
+ expect { with_user_context(username_to_impersonate, username_to_impersonate_password + "1", nil) }.to raise_error(ArgumentError)
+ end
+ end
+ end
+
+ context "when invalid arguments are passed" do
+ it "raises an ArgumentError exception if the password is not specified but the user is specified" do
+ expect { with_user_context(test_user, nil, nil) }.to raise_error(ArgumentError)
+ end
+ end
+ end
+end
diff --git a/spec/functional/notifications_spec.rb b/spec/functional/notifications_spec.rb
index 1b1ef83294..87afbd8359 100644
--- a/spec/functional/notifications_spec.rb
+++ b/spec/functional/notifications_spec.rb
@@ -13,11 +13,11 @@ describe "Notifications" do
# We always pretend we are on OSx because that has a specific provider (HomebrewProvider) so it
# tests the translation from Provider => HomebrewProvider
- let(:node) {
+ let(:node) do
n = Chef::Node.new
n.override[:os] = "darwin"
n
- }
+ end
let(:cookbook_collection) { double("Chef::CookbookCollection").as_null_object }
let(:events) { double("Chef::EventDispatch::Dispatcher").as_null_object }
let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) }
@@ -26,7 +26,7 @@ describe "Notifications" do
before do
# By default, every provider will do nothing
- p = Chef::Provider.new(nil, run_context)
+ p = Chef::Provider.new(Chef::Resource.new("lies"), run_context)
allow_any_instance_of(Chef::Resource).to receive(:provider_for_action).and_return(p)
allow(p).to receive(:run_action)
end
diff --git a/spec/functional/provider/whyrun_safe_ruby_block_spec.rb b/spec/functional/provider/whyrun_safe_ruby_block_spec.rb
index 892e1080db..1bb36f2cf6 100644
--- a/spec/functional/provider/whyrun_safe_ruby_block_spec.rb
+++ b/spec/functional/provider/whyrun_safe_ruby_block_spec.rb
@@ -21,10 +21,10 @@ require "spec_helper"
describe Chef::Resource::WhyrunSafeRubyBlock do
let(:node) { Chef::Node.new }
- let(:run_context) {
+ let(:run_context) do
events = Chef::EventDispatch::Dispatcher.new
Chef::RunContext.new(node, {}, events)
- }
+ end
before do
$evil_global_evil_laugh = :wahwah
diff --git a/spec/functional/rebooter_spec.rb b/spec/functional/rebooter_spec.rb
index 4b77c40e91..36961593b0 100644
--- a/spec/functional/rebooter_spec.rb
+++ b/spec/functional/rebooter_spec.rb
@@ -35,30 +35,32 @@ describe Chef::Platform::Rebooter do
resource
end
+ let(:node) { Chef::Node.new }
+
let(:run_context) do
- node = Chef::Node.new
events = Chef::EventDispatch::Dispatcher.new
Chef::RunContext.new(node, {}, events)
end
let(:expected) do
{
- :windows => 'shutdown /r /t 300 /c "rebooter spec test"',
- :linux => 'shutdown -r +5 "rebooter spec test"',
+ :windows => "#{ENV['SYSTEMROOT']}/System32/shutdown.exe /r /t 300 /c \"rebooter spec test\"",
+ :linux => 'shutdown -r +5 "rebooter spec test" &',
+ :solaris => 'shutdown -i6 -g5 -y "rebooter spec test" &',
}
end
let(:rebooter) { Chef::Platform::Rebooter }
- describe '#reboot_if_needed!' do
+ describe "#reboot_if_needed!" do
- it 'should not call #shell_out! when reboot has not been requested' do
+ it "should not call #shell_out! when reboot has not been requested" do
expect(rebooter).to receive(:shell_out!).exactly(0).times
expect(rebooter).to receive(:reboot_if_needed!).once.and_call_original
rebooter.reboot_if_needed!(run_context.node)
end
- describe 'calling #shell_out! to reboot' do
+ describe "calling #shell_out! to reboot" do
before(:each) do
run_context.request_reboot(reboot_info)
@@ -69,35 +71,45 @@ describe Chef::Platform::Rebooter do
end
shared_context "test a reboot method" do
- def test_rebooter_method(method_sym, is_windows, expected_reboot_str)
+ def test_rebooter_method(method_sym, is_windows, is_solaris, expected_reboot_str)
allow(ChefConfig).to receive(:windows?).and_return(is_windows)
+ node.automatic["os"] = node.automatic["platform"] = node.automatic["platform_family"] = "solaris2" if is_solaris
expect(rebooter).to receive(:shell_out!).once.with(expected_reboot_str)
+ expect(rebooter).to receive(:raise).with(Chef::Exceptions::Reboot)
expect(rebooter).to receive(method_sym).once.and_call_original
rebooter.send(method_sym, run_context.node)
end
end
- describe 'when using #reboot_if_needed!' do
+ describe "when using #reboot_if_needed!" do
include_context "test a reboot method"
it "should produce the correct string on Windows" do
- test_rebooter_method(:reboot_if_needed!, true, expected[:windows])
+ test_rebooter_method(:reboot_if_needed!, true, false, expected[:windows])
+ end
+
+ it "should produce a SysV-like shutdown on solaris" do
+ test_rebooter_method(:reboot_if_needed!, false, true, expected[:solaris])
end
- it "should produce the correct (Linux-specific) string on non-Windows" do
- test_rebooter_method(:reboot_if_needed!, false, expected[:linux])
+ it "should produce a BSD-like shutdown by default" do
+ test_rebooter_method(:reboot_if_needed!, false, false, expected[:linux])
end
end
- describe 'when using #reboot!' do
+ describe "when using #reboot!" do
include_context "test a reboot method"
it "should produce the correct string on Windows" do
- test_rebooter_method(:reboot!, true, expected[:windows])
+ test_rebooter_method(:reboot!, true, false, expected[:windows])
+ end
+
+ it "should produce a SysV-like shutdown on solaris" do
+ test_rebooter_method(:reboot!, false, true, expected[:solaris])
end
- it "should produce the correct (Linux-specific) string on non-Windows" do
- test_rebooter_method(:reboot!, false, expected[:linux])
+ it "should produce a BSD-like shutdown by default" do
+ test_rebooter_method(:reboot!, false, false, expected[:linux])
end
end
end
diff --git a/spec/functional/resource/package_spec.rb b/spec/functional/resource/apt_package_spec.rb
index 6dc55f7f01..c032bafc92 100644
--- a/spec/functional/resource/package_spec.rb
+++ b/spec/functional/resource/apt_package_spec.rb
@@ -1,7 +1,7 @@
# encoding: UTF-8
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software, Inc.
+# Copyright:: Copyright 2013-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,7 +23,7 @@ require "webrick"
module AptServer
def enable_testing_apt_source
File.open("/etc/apt/sources.list.d/chef-integration-test.list", "w+") do |f|
- f.puts "deb http://localhost:9000/ sid main"
+ f.puts "deb [trusted=yes] http://localhost:9000/ sid main"
end
# Magic to update apt cache for only our repo
shell_out!("apt-get update " +
@@ -92,7 +92,7 @@ metadata = { :unix_only => true,
:arch => "x86_64" # test packages are 64bit
}
-describe Chef::Resource::Package, metadata do
+describe Chef::Resource::AptPackage, metadata do
include Chef::Mixin::ShellOut
context "with a remote package source" do
@@ -143,7 +143,7 @@ describe Chef::Resource::Package, metadata do
end
def base_resource
- r = Chef::Resource::Package.new("chef-integration-test", run_context)
+ r = Chef::Resource::AptPackage.new("chef-integration-test", run_context)
# The apt repository in the spec data is not gpg signed, so we need to
# force apt to accept the package:
r.options("--force-yes")
@@ -260,7 +260,7 @@ describe Chef::Resource::Package, metadata do
end
before do
- node.set[:preseed_value] = "FROM TEMPLATE"
+ node.normal[:preseed_value] = "FROM TEMPLATE"
end
it "preseeds the package, then installs it" do
diff --git a/spec/functional/resource/bash_spec.rb b/spec/functional/resource/bash_spec.rb
index 87211ec264..4a5fee64bc 100644
--- a/spec/functional/resource/bash_spec.rb
+++ b/spec/functional/resource/bash_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright 2014-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,61 +21,27 @@ require "functional/resource/base"
describe Chef::Resource::Bash, :unix_only do
let(:code) { "echo hello" }
- let(:resource) {
+ let(:resource) do
resource = Chef::Resource::Bash.new("foo_resource", run_context)
- resource.code(code)
+ resource.code(code) unless code.nil?
resource
- }
+ end
describe "when setting the command attribute" do
let (:command) { "wizard racket" }
- # in Chef-12 the `command` attribute is largely useless, but does set the identity attribute
- # so that notifications need to target the value of the command. it will not run the `command`
- # and if it is given without a code block then it does nothing and always succeeds.
- describe "in Chef-12", chef: "< 13" do
- it "gets the commmand attribute from the name" do
- expect(resource.command).to eql("foo_resource")
- end
-
- it "sets the resource identity to the command name" do
- resource.command command
- expect(resource.identity).to eql(command)
- end
-
- it "warns when the code is not present and a useless `command` is present" do
- expect(Chef::Log).to receive(:warn).with(/coding error/)
- expect(Chef::Log).to receive(:warn).with(/deprecated/)
- resource.code nil
- resource.command command
- expect { resource.run_action(:run) }.not_to raise_error
- end
-
- describe "when the code is not present" do
- let(:code) { nil }
- it "warns" do
- expect(Chef::Log).to receive(:warn)
- expect { resource.run_action(:run) }.not_to raise_error
- end
- end
+ it "should raise an exception when trying to set the command" do
+ expect { resource.command command }.to raise_error(Chef::Exceptions::Script)
end
- # in Chef-13 the `command` attribute needs to be for internal use only
- describe "in Chef-13", chef: ">= 13" do
- it "should raise an exception when trying to set the command" do
- expect { resource.command command }.to raise_error # FIXME: add a real error in Chef-13
- end
-
- it "should initialize the command to nil" do
- expect(resource.command).to be_nil
- end
+ it "should initialize the command to nil" do
+ expect(resource.command).to be_nil
+ end
- describe "when the code is not present" do
- let(:code) { nil }
- it "raises an exception" do
- expect { resource.run_action(:run) }.to raise_error # FIXME: add a real error in Chef-13
- expect { resource.run_action(:run) }.not_to raise_error
- end
+ describe "when the code is not present" do
+ let(:code) { nil }
+ it "raises an exception" do
+ expect { resource.run_action(:run) }.to raise_error(Chef::Exceptions::ValidationFailed)
end
end
end
diff --git a/spec/functional/resource/batch_spec.rb b/spec/functional/resource/batch_spec.rb
index e4fc6420c7..2b176ed06c 100644
--- a/spec/functional/resource/batch_spec.rb
+++ b/spec/functional/resource/batch_spec.rb
@@ -23,7 +23,11 @@ describe Chef::Resource::WindowsScript::Batch, :windows_only do
let(:output_command) { " > " }
- let (:architecture_command) { "@echo %PROCESSOR_ARCHITECTURE%" }
+ let(:architecture_command) { "@echo %PROCESSOR_ARCHITECTURE%" }
+
+ let(:resource) do
+ Chef::Resource::WindowsScript::Batch.new("Batch resource functional test", @run_context)
+ end
it_behaves_like "a Windows script running on Windows"
diff --git a/spec/functional/resource/bff_spec.rb b/spec/functional/resource/bff_spec.rb
index e7f7540e5a..0b45e097af 100644
--- a/spec/functional/resource/bff_spec.rb
+++ b/spec/functional/resource/bff_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Prabhu Das (<prabhu.das@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright 2013-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -62,7 +62,7 @@ describe Chef::Resource::BffPackage, :requires_root, :external => ohai[:platform
context "package install action with options" do
it "should install a package" do
- new_resource.options("-e/tmp/installp.log")
+ new_resource.options("-e#{Dir.tmpdir}/installp.log")
new_resource.run_action(:install)
bff_pkg_should_be_installed(new_resource)
end
@@ -108,7 +108,7 @@ describe Chef::Resource::BffPackage, :requires_root, :external => ohai[:platform
end
it "should remove an installed package" do
- new_resource.options("-e/tmp/installp.log")
+ new_resource.options("-e#{Dir.tmpdir}/installp.log")
new_resource.run_action(:remove)
bff_pkg_should_be_removed(new_resource)
end
diff --git a/spec/functional/resource/chocolatey_package_spec.rb b/spec/functional/resource/chocolatey_package_spec.rb
index fb51fd2d64..e8dae581b9 100644
--- a/spec/functional/resource/chocolatey_package_spec.rb
+++ b/spec/functional/resource/chocolatey_package_spec.rb
@@ -18,16 +18,9 @@
require "spec_helper"
require "chef/mixin/powershell_out"
-describe Chef::Resource::ChocolateyPackage, :windows_only do
+describe Chef::Resource::ChocolateyPackage, :windows_only, :choco_installed do
include Chef::Mixin::PowershellOut
- before(:all) do
- powershell_out!("iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))")
- unless ENV["PATH"] =~ /chocolatey\\bin/
- ENV["PATH"] = "C:\\ProgramData\\chocolatey\\bin;#{ENV["PATH"]}"
- end
- end
-
let(:package_name) { "test-A" }
let(:package_list) { proc { powershell_out!("choco list -lo -r #{Array(package_name).join(' ')}").stdout.chomp } }
let(:package_source) { File.join(CHEF_SPEC_ASSETS, "chocolatey_feed") }
@@ -82,11 +75,6 @@ describe Chef::Resource::ChocolateyPackage, :windows_only do
subject.package_name "blah"
expect { subject.run_action(:install) }.to raise_error Chef::Exceptions::Package
end
-
- it "raises if package version is not found" do
- subject.version "3.0"
- expect { subject.run_action(:install) }.to raise_error Chef::Exceptions::Package
- end
end
context "upgrading a package" do
diff --git a/spec/functional/resource/cron_spec.rb b/spec/functional/resource/cron_spec.rb
index 3380eccb0d..1bff8bf874 100644
--- a/spec/functional/resource/cron_spec.rb
+++ b/spec/functional/resource/cron_spec.rb
@@ -1,7 +1,7 @@
# encoding: UTF-8
#
# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright 2013-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -120,7 +120,7 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
return if %w{aix solaris}.include?(ohai[:platform])
# Test if the attribute exists on newly created cron
cron_should_exists(cron_name, "")
- expect(shell_out("crontab -l -u #{new_resource.user} | grep \"#{attribute.upcase}=#{value}\"").exitstatus).to eq(0)
+ expect(shell_out("crontab -l -u #{new_resource.user} | grep '#{attribute.upcase}=\"#{value}\"'").exitstatus).to eq(0)
end
after do
@@ -146,6 +146,13 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
new_resource.home "/home/opscode"
create_and_validate_with_attribute(new_resource, "home", "/home/opscode")
end
+
+ %i{ home mailto path shell }.each do |attr|
+ it "supports an empty string for #{attr} attribute" do
+ new_resource.send(attr, "")
+ create_and_validate_with_attribute(new_resource, attr.to_s, "")
+ end
+ end
end
describe "negative tests for create action" do
@@ -154,7 +161,7 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
end
def cron_create_should_raise_exception
- expect { new_resource.run_action(:create) }.to raise_error(Chef::Exceptions::Cron, /Error updating state of #{new_resource.name}, exit: 1/)
+ expect { new_resource.run_action(:create) }.to raise_error(Chef::Exceptions::Cron)
cron_should_not_exists(new_resource.name)
end
diff --git a/spec/functional/resource/deploy_revision_spec.rb b/spec/functional/resource/deploy_revision_spec.rb
deleted file mode 100644
index 72eaea3c12..0000000000
--- a/spec/functional/resource/deploy_revision_spec.rb
+++ /dev/null
@@ -1,881 +0,0 @@
-#
-# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-require "tmpdir"
-
-# Deploy relies heavily on symlinks, so it doesn't work on windows.
-describe Chef::Resource::DeployRevision, :unix_only => true, :requires_git => true do
-
- let(:file_cache_path) { Dir.mktmpdir }
- let(:deploy_directory) { Dir.mktmpdir }
-
- # By making restart or other operations write to this file, we can externally
- # track the order in which those operations happened.
- let(:observe_order_file) { Tempfile.new("deploy-resource-observe-operations") }
-
- before do
- Chef::Log.level = :info
- @old_file_cache_path = Chef::Config[:file_cache_path]
- Chef::Config[:file_cache_path] = file_cache_path
- end
-
- after do
- Chef::Config[:file_cache_path] = @old_file_cache_path
- FileUtils.remove_entry_secure deploy_directory if File.exist?(deploy_directory)
- FileUtils.remove_entry_secure file_cache_path
- observe_order_file.close
- FileUtils.remove_entry_secure observe_order_file.path
- end
-
- before(:all) do
- @ohai = Ohai::System.new
- @ohai.all_plugins(%w{platform os})
- end
-
- let(:node) do
- Chef::Node.new.tap do |n|
- n.name "rspec-test"
- n.consume_external_attrs(@ohai.data, {})
- end
- end
-
- let(:event_dispatch) { Chef::EventDispatch::Dispatcher.new }
- let(:run_context) { Chef::RunContext.new(node, {}, event_dispatch) }
-
- # These tests use git's bundle feature, which is a way to export an entire
- # git repo (or subset of commits) as a single file.
- #
- # Generally you can treat a git bundle as a regular git remote.
- #
- # See also: http://git-scm.com/2010/03/10/bundles.html
- let(:git_bundle_repo) { File.expand_path("git_bundles/sinatra-test-app.gitbundle", CHEF_SPEC_DATA) }
-
- let(:git_bundle_with_in_repo_callbacks) { File.expand_path("git_bundles/sinatra-test-app-with-callback-files.gitbundle", CHEF_SPEC_DATA) }
-
- let(:git_bundle_with_in_repo_symlinks) { File.expand_path("git_bundles/sinatra-test-app-with-symlinks.gitbundle", CHEF_SPEC_DATA) }
-
- # This is the fourth version
- let(:latest_rev) { "3eb5ca6c353c83d9179dd3b29347539829b401f3" }
-
- # This is the third version
- let(:previous_rev) { "6d19a6dbecc8e37f5b2277345885c0c783eb8fb1" }
-
- # This is the second version
- let(:second_rev) { "0827e1b0e5043608ac0a824da5c558e252154ad0" }
-
- # This is the sixth version, it is on the "with-deploy-scripts" branch
- let(:rev_with_in_repo_callbacks) { "2404d015882659754bdb93ad6e4b4d3d02691a82" }
-
- # This is the fifth version in the "with-symlinks" branch
- let(:rev_with_in_repo_symlinks) { "5a4748c52aaea8250b4346a9b8ede95ee3755e28" }
-
- # Read values from the +observe_order_file+ and split each line. This way you
- # can see in which order things really happened.
- def actual_operations_order
- IO.read(observe_order_file.path).split("\n").map(&:strip)
- end
-
- # 1. touch `restart.txt` in cwd so we know that the command is run with the
- # right cwd.
- # 2. Append +tag+ to the `observe_order_file` so we can check the order in
- # which operations happen later in the test.
- def shell_restart_command(tag)
- "touch restart.txt && echo '#{tag}' >> #{observe_order_file.path}"
- end
-
- let(:basic_deploy_resource) do
- Chef::Resource::DeployRevision.new(deploy_directory, run_context).tap do |r|
- r.name "deploy-revision-unit-test"
- r.repo git_bundle_repo
- r.symlink_before_migrate({})
- r.symlinks({})
- end
- end
-
- let(:deploy_to_latest_rev) do
- basic_deploy_resource.dup.tap do |r|
- r.revision(latest_rev)
- r.restart_command shell_restart_command(:deploy_to_latest_rev)
- end
- end
-
- let(:deploy_to_previous_rev) do
- basic_deploy_resource.dup.tap do |r|
- r.revision(previous_rev)
- r.restart_command shell_restart_command(:deploy_to_previous_rev)
- end
- end
-
- let(:deploy_to_latest_rev_again) do
- basic_deploy_resource.dup.tap do |r|
- r.revision(latest_rev)
- r.restart_command shell_restart_command(:deploy_to_latest_rev_again)
- end
- end
-
- let(:deploy_to_previous_rev_again) do
- basic_deploy_resource.dup.tap do |r|
- r.revision(previous_rev)
- r.restart_command shell_restart_command(:deploy_to_previous_rev_again)
- end
- end
-
- let(:deploy_to_second_rev) do
- basic_deploy_resource.dup.tap do |r|
- r.revision(second_rev)
- r.restart_command shell_restart_command(:deploy_to_second_rev)
- end
- end
-
- let(:deploy_to_second_rev_again) do
- basic_deploy_resource.dup.tap do |r|
- r.revision(second_rev)
- r.restart_command shell_restart_command(:deploy_to_second_rev_again)
- end
- end
-
- let(:deploy_to_second_rev_again_again) do
- basic_deploy_resource.dup.tap do |r|
- r.revision(second_rev)
- r.restart_command shell_restart_command(:deploy_to_second_rev_again_again)
- end
- end
-
- # Computes the full path for +path+ relative to the deploy directory
- def rel_path(path)
- File.expand_path(path, deploy_directory)
- end
-
- def actual_current_rev
- Dir.chdir(rel_path("current")) do
- `git rev-parse HEAD`.strip
- end
- end
-
- def self.the_app_is_deployed_at_revision(target_rev_spec)
- it "deploys the app to the target revision (#{target_rev_spec})" do
- target_rev = send(target_rev_spec)
-
- expect(File).to exist(rel_path("current"))
-
- expect(actual_current_rev).to eq(target_rev)
-
- # Is the app code actually there?
- expect(File).to exist(rel_path("current/app/app.rb"))
- end
- end
-
- context "when deploying a simple app" do
- describe "for the first time, with the required directory layout precreated" do
- before do
- FileUtils.mkdir_p(rel_path("releases"))
- FileUtils.mkdir_p(rel_path("shared"))
- deploy_to_latest_rev.run_action(:deploy)
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "restarts the application" do
- expect(File).to exist(rel_path("current/restart.txt"))
- expect(actual_operations_order).to eq(%w{deploy_to_latest_rev})
- end
-
- it "is marked as updated" do
- expect(deploy_to_latest_rev).to be_updated_by_last_action
- end
- end
-
- describe "back to a previously deployed revision, with the directory structure precreated" do
- before do
- FileUtils.mkdir_p(rel_path("releases"))
- FileUtils.mkdir_p(rel_path("shared"))
-
- deploy_to_latest_rev.run_action(:deploy)
- deploy_to_previous_rev.run_action(:deploy)
- deploy_to_latest_rev_again.run_action(:deploy)
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "restarts the application after rolling back" do
- expect(actual_operations_order).to eq(%w{deploy_to_latest_rev deploy_to_previous_rev deploy_to_latest_rev_again})
- end
-
- it "is marked updated" do
- expect(deploy_to_latest_rev_again).to be_updated_by_last_action
- end
-
- it "deploys the right code" do
- expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the fourth version of the app")
- end
- end
-
- describe "for the first time, with no existing directory layout" do
- before do
- deploy_to_latest_rev.run_action(:deploy)
- end
-
- it "creates the required directory tree" do
- expect(File).to be_directory(rel_path("releases"))
- expect(File).to be_directory(rel_path("shared"))
- expect(File).to be_directory(rel_path("releases/#{latest_rev}"))
-
- expect(File).to be_directory(rel_path("current/tmp"))
- expect(File).to be_directory(rel_path("current/config"))
- expect(File).to be_directory(rel_path("current/public"))
-
- expect(File).to be_symlink(rel_path("current"))
- expect(File.readlink(rel_path("current"))).to eq(rel_path("releases/#{latest_rev}"))
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "restarts the application" do
- expect(File).to exist(rel_path("current/restart.txt"))
- expect(actual_operations_order).to eq(%w{deploy_to_latest_rev})
- end
-
- it "is marked as updated" do
- expect(deploy_to_latest_rev).to be_updated_by_last_action
- end
- end
-
- describe "again to the current revision" do
- before do
- deploy_to_latest_rev.run_action(:deploy)
- deploy_to_latest_rev.run_action(:deploy)
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "does not restart the app" do
- expect(actual_operations_order).to eq(%w{deploy_to_latest_rev})
- end
-
- it "is not marked updated" do
- expect(deploy_to_latest_rev).not_to be_updated_by_last_action
- end
-
- end
-
- describe "again with force_deploy" do
- before do
- deploy_to_latest_rev.run_action(:force_deploy)
- deploy_to_latest_rev_again.run_action(:force_deploy)
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "restarts the app" do
- expect(actual_operations_order).to eq(%w{deploy_to_latest_rev deploy_to_latest_rev_again})
- end
-
- it "is marked updated" do
- expect(deploy_to_latest_rev).to be_updated_by_last_action
- end
-
- end
-
- describe "again to a new revision" do
- before do
- deploy_to_previous_rev.run_action(:deploy)
- deploy_to_latest_rev.run_action(:deploy)
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "restarts the application after the new deploy" do
- expect(actual_operations_order).to eq(%w{deploy_to_previous_rev deploy_to_latest_rev})
- end
-
- it "is marked updated" do
- expect(deploy_to_previous_rev).to be_updated_by_last_action
- end
-
- it "leaves the old copy of the app around for rollback" do
- expect(File).to exist(File.join(deploy_directory, "releases", previous_rev))
- end
-
- end
-
- describe "back to a previously deployed revision (implicit rollback)" do
- before do
- deploy_to_latest_rev.run_action(:deploy)
- deploy_to_previous_rev.run_action(:deploy)
- deploy_to_latest_rev_again.run_action(:deploy)
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "restarts the application after rolling back" do
- expect(actual_operations_order).to eq(%w{deploy_to_latest_rev deploy_to_previous_rev deploy_to_latest_rev_again})
- end
-
- it "is marked updated" do
- expect(deploy_to_latest_rev_again).to be_updated_by_last_action
- end
-
- it "deploys the right code" do
- expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the fourth version of the app")
- end
- end
-
- describe "back to a previously deployed revision where resource rev == latest revision (explicit rollback)" do
- before do
- deploy_to_previous_rev.run_action(:deploy)
- @previous_rev_all_releases = deploy_to_previous_rev.provider_for_action(:deploy).all_releases
- deploy_to_latest_rev.run_action(:deploy)
- @latest_rev_all_releases = deploy_to_latest_rev.provider_for_action(:deploy).all_releases
- deploy_to_latest_rev_again.run_action(:rollback)
- @previous_rev_again_all_releases = deploy_to_latest_rev_again.provider_for_action(:deploy).all_releases
- end
-
- the_app_is_deployed_at_revision(:previous_rev)
-
- it "restarts the application after rolling back" do
- expect(actual_operations_order).to eq(%w{deploy_to_previous_rev deploy_to_latest_rev deploy_to_latest_rev_again})
- end
-
- it "is marked updated" do
- expect(deploy_to_latest_rev_again).to be_updated_by_last_action
- end
-
- it "deploys the right code" do
- expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the third version of the app")
- end
-
- it "all_releases after first deploy should have one entry" do
- expect(@previous_rev_all_releases.length).to eq(1)
- end
-
- it "all_releases after second deploy should have two entries" do
- expect(@latest_rev_all_releases.length).to eq(2)
- end
-
- it "all_releases after rollback should have one entry" do
- expect(@previous_rev_again_all_releases.length).to eq(1)
- end
-
- it "all_releases after rollback should be the same as after the first deploy" do
- expect(@previous_rev_again_all_releases).to eq(@previous_rev_all_releases)
- end
-
- end
-
- describe "back to a previously deployed revision where resource rev == previous revision (explicit rollback)" do
- before do
- deploy_to_previous_rev.run_action(:deploy)
- @previous_rev_all_releases = deploy_to_previous_rev.provider_for_action(:deploy).all_releases
- deploy_to_latest_rev.run_action(:deploy)
- @latest_rev_all_releases = deploy_to_latest_rev.provider_for_action(:deploy).all_releases
- deploy_to_previous_rev_again.run_action(:rollback)
- # FIXME: only difference with previous test is using latest_rev_again insetad of previous_rev_again
- @previous_rev_again_all_releases = deploy_to_latest_rev_again.provider_for_action(:deploy).all_releases
- end
-
- the_app_is_deployed_at_revision(:previous_rev)
-
- it "restarts the application after rolling back" do
- expect(actual_operations_order).to eq(%w{deploy_to_previous_rev deploy_to_latest_rev deploy_to_previous_rev_again})
- end
-
- it "is marked updated" do
- expect(deploy_to_previous_rev_again).to be_updated_by_last_action
- end
-
- it "deploys the right code" do
- expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the third version of the app")
- end
-
- it "all_releases after first deploy should have one entry" do
- expect(@previous_rev_all_releases.length).to eq(1)
- end
-
- it "all_releases after second deploy should have two entries" do
- expect(@latest_rev_all_releases.length).to eq(2)
- end
-
- it "all_releases after rollback should have one entry" do
- expect(@previous_rev_again_all_releases.length).to eq(1)
- end
-
- it "all_releases after rollback should be the same as after the first deploy" do
- expect(@previous_rev_again_all_releases).to eq(@previous_rev_all_releases)
- end
- end
-
- describe "back to a previously deployed revision where resource rev == latest revision (explicit rollback)" do
- before do
- deploy_to_second_rev.run_action(:deploy)
- @first_deploy_all_releases = deploy_to_second_rev.provider_for_action(:deploy).all_releases
- deploy_to_previous_rev.run_action(:deploy)
- @second_deploy_all_releases = deploy_to_previous_rev.provider_for_action(:deploy).all_releases
- deploy_to_previous_rev_again.run_action(:rollback)
- @third_deploy_all_releases = deploy_to_previous_rev_again.provider_for_action(:deploy).all_releases
- deploy_to_latest_rev.run_action(:deploy)
- @fourth_deploy_all_releases = deploy_to_latest_rev.provider_for_action(:deploy).all_releases
- deploy_to_latest_rev_again.run_action(:rollback)
- @fifth_deploy_all_releases = deploy_to_latest_rev_again.provider_for_action(:deploy).all_releases
- end
-
- the_app_is_deployed_at_revision(:second_rev)
-
- it "restarts the application after rolling back" do
- expect(actual_operations_order).to eq(%w{deploy_to_second_rev deploy_to_previous_rev deploy_to_previous_rev_again deploy_to_latest_rev deploy_to_latest_rev_again})
- end
-
- it "is marked updated" do
- expect(deploy_to_latest_rev_again).to be_updated_by_last_action
- end
-
- it "deploys the right code" do
- expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the second version of the app")
- end
-
- it "all_releases after rollback should have one entry" do
- expect(@fifth_deploy_all_releases.length).to eq(1)
- end
-
- it "all_releases after rollback should be the same as after the first deploy" do
- expect(@fifth_deploy_all_releases).to eq(@first_deploy_all_releases)
- end
- end
-
- describe "back to a previously deployed revision where resource rev == latest revision (explicit rollback)" do
- before do
- deploy_to_second_rev.run_action(:deploy)
- @first_deploy_all_releases = deploy_to_second_rev.provider_for_action(:deploy).all_releases
- deploy_to_previous_rev.run_action(:deploy)
- @second_deploy_all_releases = deploy_to_previous_rev.provider_for_action(:deploy).all_releases
- deploy_to_second_rev_again.run_action(:rollback)
- @third_deploy_all_releases = deploy_to_second_rev_again.provider_for_action(:deploy).all_releases
- deploy_to_latest_rev.run_action(:deploy)
- @fourth_deploy_all_releases = deploy_to_latest_rev.provider_for_action(:deploy).all_releases
- deploy_to_second_rev_again_again.run_action(:rollback)
- @fifth_deploy_all_releases = deploy_to_second_rev_again_again.provider_for_action(:deploy).all_releases
- end
-
- the_app_is_deployed_at_revision(:second_rev)
-
- it "restarts the application after rolling back" do
- expect(actual_operations_order).to eq(%w{deploy_to_second_rev deploy_to_previous_rev deploy_to_second_rev_again deploy_to_latest_rev deploy_to_second_rev_again_again})
- end
-
- it "is marked updated" do
- expect(deploy_to_second_rev_again_again).to be_updated_by_last_action
- end
-
- it "deploys the right code" do
- expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the second version of the app")
- end
-
- it "all_releases after rollback should have one entry" do
- expect(@fifth_deploy_all_releases.length).to eq(1)
- end
-
- it "all_releases after rollback should be the same as after the first deploy" do
- expect(@fifth_deploy_all_releases).to eq(@first_deploy_all_releases)
- end
-
- end
-
- # CHEF-3435
- describe "to a deploy_to path that does not yet exist" do
-
- let(:top_level_tmpdir) { Dir.mktmpdir }
-
- # override top level deploy_directory let block with one that is two
- # directories deeper
- let(:deploy_directory) { File.expand_path("nested/deeper", top_level_tmpdir) }
-
- after do
- FileUtils.remove_entry_secure top_level_tmpdir
- end
-
- before do
- expect(File).not_to exist(deploy_directory)
- deploy_to_latest_rev.run_action(:deploy)
- end
-
- it "creates the required directory tree" do
- expect(File).to be_directory(rel_path("releases"))
- expect(File).to be_directory(rel_path("shared"))
- expect(File).to be_directory(rel_path("releases/#{latest_rev}"))
-
- expect(File).to be_directory(rel_path("current/tmp"))
- expect(File).to be_directory(rel_path("current/config"))
- expect(File).to be_directory(rel_path("current/public"))
-
- expect(File).to be_symlink(rel_path("current"))
- expect(File.readlink(rel_path("current"))).to eq(rel_path("releases/#{latest_rev}"))
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- end
- end
-
- context "when deploying an app with inline recipe callbacks" do
-
- # Use closures to capture and mutate this variable. This allows us to track
- # ordering of operations.
- callback_order = []
-
- let(:deploy_to_latest_with_inline_recipes) do
- deploy_to_latest_rev.dup.tap do |r|
- r.symlink_before_migrate "config/config.ru" => "config.ru"
- r.before_migrate do
- callback_order << :before_migrate
-
- file "#{release_path}/before_migrate.txt" do
- # The content here isn't relevant, but it gets printed when running
- # the tests. Could be handy for debugging.
- content callback_order.inspect
- end
- end
- r.before_symlink do
- callback_order << :before_symlink
-
- current_release_path = release_path
- ruby_block "ensure before symlink" do
- block do
- if ::File.exist?(::File.join(current_release_path, "/tmp"))
- raise "Ordering issue with provider, expected symlinks to not have been created"
- end
- end
- end
-
- file "#{release_path}/before_symlink.txt" do
- content callback_order.inspect
- end
- end
- r.before_restart do
- callback_order << :before_restart
-
- current_release_path = release_path
- ruby_block "ensure after symlink" do
- block do
- unless ::File.exist?(::File.join(current_release_path, "/tmp"))
- raise "Ordering issue with provider, expected symlinks to have been created"
- end
- end
- end
-
- file "#{release_path}/tmp/before_restart.txt" do
- content callback_order.inspect
- end
- end
- r.after_restart do
- callback_order << :after_restart
- file "#{release_path}/tmp/after_restart.txt" do
- content callback_order.inspect
- end
- end
- end
- end
-
- before do
- callback_order.clear # callback_order variable is global for this context group
- deploy_to_latest_with_inline_recipes.run_action(:deploy)
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "is marked updated" do
- expect(deploy_to_latest_with_inline_recipes).to be_updated_by_last_action
- end
-
- it "calls the callbacks in order" do
- expect(callback_order).to eq([:before_migrate, :before_symlink, :before_restart, :after_restart])
- end
-
- it "runs chef resources in the callbacks" do
- expect(File).to exist(rel_path("current/before_migrate.txt"))
- expect(File).to exist(rel_path("current/before_symlink.txt"))
- expect(File).to exist(rel_path("current/tmp/before_restart.txt"))
- expect(File).to exist(rel_path("current/tmp/after_restart.txt"))
- end
- end
-
- context "when deploying an app with in-repo callback scripts" do
- let(:deploy_with_in_repo_callbacks) do
- basic_deploy_resource.dup.tap do |r|
- r.repo git_bundle_with_in_repo_callbacks
- r.revision rev_with_in_repo_callbacks
- end
- end
-
- before do
- deploy_with_in_repo_callbacks.run_action(:deploy)
- end
-
- the_app_is_deployed_at_revision(:rev_with_in_repo_callbacks)
-
- it "runs chef resources in the callbacks" do
- expect(File).to exist(rel_path("current/before_migrate.txt"))
- expect(File).to exist(rel_path("current/before_symlink.txt"))
- expect(File).to exist(rel_path("current/tmp/before_restart.txt"))
- expect(File).to exist(rel_path("current/tmp/after_restart.txt"))
- end
-
- end
-
- context "when deploying an app with migrations" do
- let(:deploy_with_migration) do
- basic_deploy_resource.dup.tap do |r|
-
- # Need this so we can call methods from this test inside the inline
- # recipe callbacks
- spec_context = self
-
- r.revision latest_rev
-
- # enable migrations
- r.migrate true
- # abuse `shell_restart_command` so we can observe order of when the
- # miration command gets run
- r.migration_command shell_restart_command("migration")
- r.before_migrate do
-
- # inline recipe callbacks don't cwd, so you have to get the release
- # directory as a local and "capture" it in the closure.
- current_release = release_path
- execute spec_context.shell_restart_command("before_migrate") do
- cwd current_release
- end
- end
- r.before_symlink do
- current_release = release_path
- execute spec_context.shell_restart_command("before_symlink") do
- cwd current_release
- end
- end
-
- r.before_restart do
- current_release = release_path
- execute spec_context.shell_restart_command("before_restart") do
- cwd current_release
- end
- end
-
- r.after_restart do
- current_release = release_path
- execute spec_context.shell_restart_command("after_restart") do
- cwd current_release
- end
- end
-
- end
- end
-
- before do
- deploy_with_migration.run_action(:deploy)
- end
-
- it "runs migrations in between the before_migrate and before_symlink steps" do
- expect(actual_operations_order).to eq(%w{before_migrate migration before_symlink before_restart after_restart})
- end
- end
-
- context "when deploying an app with in-repo symlinks" do
- let(:deploy_with_in_repo_symlinks) do
- basic_deploy_resource.dup.tap do |r|
- r.repo git_bundle_with_in_repo_symlinks
- r.revision rev_with_in_repo_symlinks
- end
- end
-
- it "should not raise an exception calling File.utime on symlinks" do
- expect { deploy_with_in_repo_symlinks.run_action(:deploy) }.not_to raise_error
- end
- end
-
- context "when a previously deployed application has been nuked" do
-
- shared_examples_for "a redeployed application" do
-
- it "should redeploy the application" do
- expect(File).to be_directory(rel_path("releases"))
- expect(File).to be_directory(rel_path("shared"))
- expect(File).to be_directory(rel_path("releases/#{latest_rev}"))
-
- expect(File).to be_directory(rel_path("current/tmp"))
- expect(File).to be_directory(rel_path("current/config"))
- expect(File).to be_directory(rel_path("current/public"))
-
- expect(File).to be_symlink(rel_path("current"))
- expect(File.readlink(rel_path("current"))).to eq(rel_path("releases/#{latest_rev}"))
- end
- end
-
- # background: If a deployment is hosed and the user decides to rm -rf the
- # deployment dir, deploy resource should detect that and nullify its cache.
-
- context "by removing the entire deploy directory" do
-
- before do
- deploy_to_latest_rev.dup.run_action(:deploy)
- FileUtils.rm_rf(deploy_directory)
- deploy_to_latest_rev.dup.run_action(:deploy)
- end
-
- include_examples "a redeployed application"
-
- end
-
- context "by removing the current/ directory" do
-
- before do
- deploy_to_latest_rev.dup.run_action(:deploy)
- FileUtils.rm(rel_path("current"))
- deploy_to_latest_rev.dup.run_action(:deploy)
- end
-
- include_examples "a redeployed application"
-
- end
- end
-
- context "when a deployment fails" do
-
- shared_examples_for "a recovered deployment" do
-
- it "should redeploy the application" do
- expect(File).to be_directory(rel_path("releases"))
- expect(File).to be_directory(rel_path("shared"))
- expect(File).to be_directory(rel_path("releases/#{latest_rev}"))
-
- expect(File).to be_directory(rel_path("current/tmp"))
- expect(File).to be_directory(rel_path("current/config"))
- expect(File).to be_directory(rel_path("current/public"))
-
- expect(File).to be_symlink(rel_path("current"))
- expect(File.readlink(rel_path("current"))).to eq(rel_path("releases/#{latest_rev}"))
-
- # if callbacks ran, we know the app was deployed and not merely rolled
- # back to a (busted) prior deployment.
- expect(callback_order).to eq([:before_migrate,
- :before_symlink,
- :before_restart,
- :after_restart ])
- end
- end
-
- let!(:callback_order) { [] }
-
- let(:deploy_to_latest_with_callback_tracking) do
- resource = deploy_to_latest_rev.dup
- tracker = callback_order
- resource.before_migrate { tracker << :before_migrate }
- resource.before_symlink { tracker << :before_symlink }
- resource.before_restart { tracker << :before_restart }
- resource.after_restart { tracker << :after_restart }
- resource
- end
-
- [:before_migrate, :before_symlink, :before_restart, :after_restart].each do |callback|
-
- context "in the `#{callback}' callback" do
- before do
- expect { deploy_that_fails.run_action(:deploy) }.to raise_error(Exception, %r{I am a failed deploy})
- deploy_to_latest_with_callback_tracking.run_action(:deploy)
- end
-
- let(:deploy_that_fails) do
- resource = deploy_to_latest_rev.dup
- errant_callback = lambda { |x| raise Exception, "I am a failed deploy" }
- resource.send(callback, &errant_callback)
- resource
- end
-
- include_examples "a recovered deployment"
-
- end
-
- end
-
- context "in the service restart step" do
-
- let(:deploy_that_fails) do
- resource = deploy_to_latest_rev.dup
- resource.restart_command("RUBYOPT=\"\" ruby -e 'exit 1'")
- resource
- end
-
- before do
- expect { deploy_that_fails.run_action(:deploy) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
- deploy_to_latest_with_callback_tracking.run_action(:deploy)
- end
-
- include_examples "a recovered deployment"
- end
-
- context "when cloning the app code" do
-
- class BadTimeScmProvider
- def initialize(new_resource, run_context)
- end
-
- def load_current_resource
- end
-
- def revision_slug
- "5"
- end
-
- def run_action(action)
- raise RuntimeError, "network error"
- end
- end
-
- let(:deploy_that_fails) do
- resource = deploy_to_latest_rev.dup
- resource.scm_provider(BadTimeScmProvider)
- resource
- end
-
- before do
- expect { deploy_that_fails.run_action(:deploy) }.to raise_error(RuntimeError, /network error/)
- deploy_to_latest_with_callback_tracking.run_action(:deploy)
- end
-
- include_examples "a recovered deployment"
- end
-
- context "and then is deployed to a different revision" do
-
- let(:deploy_that_fails) do
- resource = deploy_to_previous_rev.dup
- resource.after_restart { |x| raise Exception, "I am a failed deploy" }
- resource
- end
-
- before do
- expect { deploy_that_fails.run_action(:deploy) }.to raise_error(Exception, %r{I am a failed deploy})
- deploy_to_latest_rev.run_action(:deploy)
- end
-
- it "removes the unsuccessful deploy after a later successful deploy" do
- expect(::File).not_to exist(File.join(deploy_directory, "releases", previous_rev))
- end
-
- end
-
- end
-end
diff --git a/spec/functional/resource/dnf_package_spec.rb b/spec/functional/resource/dnf_package_spec.rb
new file mode 100644
index 0000000000..01611ef996
--- /dev/null
+++ b/spec/functional/resource/dnf_package_spec.rb
@@ -0,0 +1,686 @@
+#
+# Copyright:: Copyright 2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "functional/resource/base"
+require "chef/mixin/shell_out"
+
+# run this test only for following platforms.
+exclude_test = !(%w{rhel fedora}.include?(ohai[:platform_family]) && File.exist?("/usr/bin/dnf"))
+describe Chef::Resource::RpmPackage, :requires_root, :external => exclude_test do
+ include Chef::Mixin::ShellOut
+
+ def flush_cache
+ # needed on at least fc23/fc24 sometimes to deal with the dnf cache getting out of sync with the rpm db
+ FileUtils.rm_f "/var/cache/dnf/@System.solv"
+ Chef::Resource::DnfPackage.new("shouldnt-matter", run_context).run_action(:flush_cache)
+ end
+
+ def preinstall(*rpms)
+ rpms.each do |rpm|
+ shell_out!("rpm -ivh #{CHEF_SPEC_ASSETS}/yumrepo/#{rpm}")
+ end
+ flush_cache
+ end
+
+ before(:each) do
+ File.open("/etc/yum.repos.d/chef-dnf-localtesting.repo", "w+") do |f|
+ f.write <<-EOF
+[chef-dnf-localtesting]
+name=Chef DNF spec testing repo
+baseurl=file://#{CHEF_SPEC_ASSETS}/yumrepo
+enable=1
+gpgcheck=0
+ EOF
+ end
+ shell_out!("rpm -qa | grep chef_rpm | xargs -r rpm -e")
+ end
+
+ after(:all) do
+ shell_out!("rpm -qa | grep chef_rpm | xargs -r rpm -e")
+ FileUtils.rm_f "/etc/yum.repos.d/chef-dnf-localtesting.repo"
+ end
+
+ let(:package_name) { "chef_rpm" }
+ let(:dnf_package) { Chef::Resource::DnfPackage.new(package_name, run_context) }
+
+ describe ":install" do
+ context "vanilla use case" do
+ let(:package_name) { "chef_rpm" }
+
+ it "installs if the package is not installed" do
+ flush_cache
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "does not install if the package is installed" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "does not install twice" do
+ flush_cache
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "does not install if the prior version package is installed" do
+ preinstall("chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+
+ it "does not install if the i686 package is installed" do
+ skip "FIXME: do nothing, or install the x86_64 version?"
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.i686")
+ end
+
+ it "does not install if the prior version i686 package is installed" do
+ skip "FIXME: do nothing, or install the x86_64 version?"
+ preinstall("chef_rpm-1.2-1.i686.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.i686")
+ end
+ end
+
+ context "with versions or globs in the name" do
+ it "works with a version" do
+ flush_cache
+ dnf_package.package_name("chef_rpm-1.10")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "works with an older version" do
+ flush_cache
+ dnf_package.package_name("chef_rpm-1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+
+ it "works with an evr" do
+ flush_cache
+ dnf_package.package_name("chef_rpm-0:1.2-1")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+
+ it "works with a version glob" do
+ flush_cache
+ dnf_package.package_name("chef_rpm-1*")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "works with a name glob + version glob" do
+ flush_cache
+ dnf_package.package_name("chef_rp*-1*")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+ end
+
+ # version only matches the actual dnf version, does not work with epoch or release or combined evr
+ context "with version property" do
+ it "matches the full version" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.10")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "matches with a glob" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1*")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "matches the vr" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.10-1")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "matches the evr" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("0:1.10-1")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "matches with a vr glob" do
+ pending "doesn't work on command line either"
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.10-1*")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "matches with an evr glob" do
+ pending "doesn't work on command line either"
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("0:1.10-1*")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+ end
+
+ context "downgrades" do
+ it "just work with DNF" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.version("1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+
+ it "throws a deprecation warning with allow_downgrade" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ expect(Chef).to receive(:deprecated).with(:dnf_package_allow_downgrade, /^the allow_downgrade property on the dnf_package provider is not used/)
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.version("1.2")
+ dnf_package.run_action(:install)
+ dnf_package.allow_downgrade true
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+ end
+
+ context "with arches" do
+ it "installs with 64-bit arch in the name" do
+ flush_cache
+ dnf_package.package_name("chef_rpm.x86_64")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "installs with 32-bit arch in the name" do
+ flush_cache
+ dnf_package.package_name("chef_rpm.i686")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.i686")
+ end
+
+ it "installs with 64-bit arch in the property" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.arch("x86_64")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "installs with 32-bit arch in the property" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.arch("i686")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.i686")
+ end
+ end
+
+ context "with constraints" do
+ it "with nothing installed, it installs the latest version" do
+ flush_cache
+ dnf_package.package_name("chef_rpm >= 1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "when it is met, it does nothing" do
+ preinstall("chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.package_name("chef_rpm >= 1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+
+ it "when it is met, it does nothing" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.package_name("chef_rpm >= 1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "with nothing intalled, it installs the latest version" do
+ flush_cache
+ dnf_package.package_name("chef_rpm > 1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "when it is not met by an installed rpm, it upgrades" do
+ preinstall("chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.package_name("chef_rpm > 1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "when it is met by an installed rpm, it does nothing" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.package_name("chef_rpm > 1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "when there is no solution to the contraint" do
+ flush_cache
+ dnf_package.package_name("chef_rpm > 2.0")
+ expect { dnf_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+
+ it "when there is no solution to the contraint but an rpm is preinstalled" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.package_name("chef_rpm > 2.0")
+ expect { dnf_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+ end
+
+ context "with source arguments" do
+ it "raises an exception when the package does not exist" do
+ flush_cache
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/this-file-better-not-exist.rpm")
+ expect { dnf_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+
+ it "does not raise a hard exception in why-run mode when the package does not exist" do
+ Chef::Config[:why_run] = true
+ flush_cache
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/this-file-better-not-exist.rpm")
+ dnf_package.run_action(:install)
+ expect { dnf_package.run_action(:install) }.not_to raise_error
+ end
+
+ it "installs the package when using the source argument" do
+ flush_cache
+ dnf_package.name "something"
+ dnf_package.package_name "somethingelse"
+ dnf_package.source("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+
+ it "installs the package when the name is a path to a file" do
+ flush_cache
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+
+ it "does not downgrade the package with :install" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "does not upgrade the package with :install" do
+ preinstall("chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+
+ it "is idempotent when the package is already installed" do
+ preinstall("chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+ end
+
+ context "with no available version" do
+ it "works when a package is installed" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-dnf-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+
+ it "works with a local source" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-dnf-localtesting.repo"
+ flush_cache
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+ end
+
+ context "multipackage with arches" do
+ it "installs two rpms" do
+ flush_cache
+ dnf_package.package_name([ "chef_rpm.x86_64", "chef_rpm.i686" ] )
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to match(/chef_rpm-1.10-1.x86_64/)
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to match(/chef_rpm-1.10-1.i686/)
+ end
+
+ it "does nothing if both are installed" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm", "chef_rpm-1.10-1.i686.rpm")
+ flush_cache
+ dnf_package.package_name([ "chef_rpm.x86_64", "chef_rpm.i686" ] )
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "installs the second rpm if the first is installed" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.package_name([ "chef_rpm.x86_64", "chef_rpm.i686" ] )
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to match(/chef_rpm-1.10-1.x86_64/)
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to match(/chef_rpm-1.10-1.i686/)
+ end
+
+ it "installs the first rpm if the second is installed" do
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ dnf_package.package_name([ "chef_rpm.x86_64", "chef_rpm.i686" ] )
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to match(/chef_rpm-1.10-1.x86_64/)
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to match(/chef_rpm-1.10-1.i686/)
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "installs two rpms with multi-arch" do
+ flush_cache
+ dnf_package.package_name(%w{chef_rpm chef_rpm} )
+ dnf_package.arch(%w{x86_64 i686})
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to match(/chef_rpm-1.10-1.x86_64/)
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to match(/chef_rpm-1.10-1.i686/)
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "installs the second rpm if the first is installed (muti-arch)" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.package_name(%w{chef_rpm chef_rpm} )
+ dnf_package.arch(%w{x86_64 i686})
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to match(/chef_rpm-1.10-1.x86_64/)
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to match(/chef_rpm-1.10-1.i686/)
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "installs the first rpm if the second is installed (muti-arch)" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.package_name(%w{chef_rpm chef_rpm} )
+ dnf_package.arch(%w{x86_64 i686})
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to match(/chef_rpm-1.10-1.x86_64/)
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to match(/chef_rpm-1.10-1.i686/)
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "does nothing if both are installed (muti-arch)" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm", "chef_rpm-1.10-1.i686.rpm")
+ dnf_package.package_name(%w{chef_rpm chef_rpm} )
+ dnf_package.arch(%w{x86_64 i686})
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+ end
+
+ describe ":upgrade" do
+ context "downgrades" do
+ it "just work with DNF" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.version("1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+
+ it "throws a deprecation warning with allow_downgrade" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ expect(Chef).to receive(:deprecated).with(:dnf_package_allow_downgrade, /^the allow_downgrade property on the dnf_package provider is not used/)
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.version("1.2")
+ dnf_package.run_action(:install)
+ dnf_package.allow_downgrade true
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+ end
+
+ context "with source arguments" do
+ it "installs the package when using the source argument" do
+ flush_cache
+ dnf_package.name "something"
+ dnf_package.package_name "somethingelse"
+ dnf_package.source("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+
+ it "installs the package when the name is a path to a file" do
+ flush_cache
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+
+ it "downgrades the package" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+
+ it "upgrades the package" do
+ preinstall("chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+
+ it "is idempotent when the package is already installed" do
+ preinstall("chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+ end
+
+ context "with no available version" do
+ it "works when a package is installed" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-dnf-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+
+ it "works with a local source" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-dnf-localtesting.repo"
+ flush_cache
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.x86_64")
+ end
+ end
+ end
+
+ describe ":remove" do
+ context "vanilla use case" do
+ let(:package_name) { "chef_rpm" }
+ it "does nothing if the package is not installed" do
+ flush_cache
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("package chef_rpm is not installed")
+ end
+
+ it "removes the package if the package is installed" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("package chef_rpm is not installed")
+ end
+
+ it "does not remove the package twice" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("package chef_rpm is not installed")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("package chef_rpm is not installed")
+ end
+
+ it "removes the package if the prior version package is installed" do
+ preinstall("chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("package chef_rpm is not installed")
+ end
+
+ it "removes the package if the i686 package is installed" do
+ skip "FIXME: should this be fixed or is the current behavior correct?"
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("package chef_rpm is not installed")
+ end
+
+ it "removes the package if the prior version i686 package is installed" do
+ skip "FIXME: should this be fixed or is the current behavior correct?"
+ preinstall("chef_rpm-1.2-1.i686.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("package chef_rpm is not installed")
+ end
+ end
+
+ context "with 64-bit arch" do
+ let(:package_name) { "chef_rpm.x86_64" }
+ it "does nothing if the package is not installed" do
+ flush_cache
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("package chef_rpm is not installed")
+ end
+
+ it "removes the package if the package is installed" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("package chef_rpm is not installed")
+ end
+
+ it "removes the package if the prior version package is installed" do
+ preinstall("chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("package chef_rpm is not installed")
+ end
+
+ it "does nothing if the i686 package is installed" do
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.i686")
+ end
+
+ it "does nothing if the prior version i686 package is installed" do
+ preinstall("chef_rpm-1.2-1.i686.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.2-1.i686")
+ end
+ end
+
+ context "with 32-bit arch" do
+ let(:package_name) { "chef_rpm.i686" }
+ it "removes only the 32-bit arch if both are installed" do
+ preinstall("chef_rpm-1.10-1.x86_64.rpm", "chef_rpm-1.10-1.i686.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("chef_rpm-1.10-1.x86_64")
+ end
+ end
+
+ context "with no available version" do
+ it "works when a package is installed" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-dnf-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.x86_64.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to eql("package chef_rpm is not installed")
+ end
+ end
+ end
+end
diff --git a/spec/functional/resource/dpkg_package_spec.rb b/spec/functional/resource/dpkg_package_spec.rb
index d65256231b..1988fd0c7d 100644
--- a/spec/functional/resource/dpkg_package_spec.rb
+++ b/spec/functional/resource/dpkg_package_spec.rb
@@ -27,11 +27,11 @@ describe Chef::Resource::DpkgPackage, :requires_root, :debian_family_only, arch:
let(:test1_1) { File.join(apt_data, "chef-integration-test_1.1-1_amd64.deb") }
let(:test2_0) { File.join(apt_data, "chef-integration-test2_1.0-1_amd64.deb") }
- let(:run_context) {
+ let(:run_context) do
node = TEST_NODE.dup
events = Chef::EventDispatch::Dispatcher.new
Chef::RunContext.new(node, {}, events)
- }
+ end
let(:dpkg_package) { Chef::Resource::DpkgPackage.new(test1_0, run_context) }
diff --git a/spec/functional/resource/dsc_resource_spec.rb b/spec/functional/resource/dsc_resource_spec.rb
index 8e4940d475..bb3cf2157d 100644
--- a/spec/functional/resource/dsc_resource_spec.rb
+++ b/spec/functional/resource/dsc_resource_spec.rb
@@ -21,17 +21,17 @@ require "spec_helper"
describe Chef::Resource::DscResource, :windows_powershell_dsc_only do
let(:event_dispatch) { Chef::EventDispatch::Dispatcher.new }
- let(:node) {
+ let(:node) do
Chef::Node.new.tap do |n|
n.consume_external_attrs(OHAI_SYSTEM.data, {})
end
- }
+ end
let(:run_context) { Chef::RunContext.new(node, {}, event_dispatch) }
- let(:new_resource) {
+ let(:new_resource) do
Chef::Resource::DscResource.new("dsc_resource_test", run_context)
- }
+ end
context "when Powershell does not support Invoke-DscResource"
context "when Powershell supports Invoke-DscResource" do
@@ -77,7 +77,7 @@ describe Chef::Resource::DscResource, :windows_powershell_dsc_only do
new_resource.run_action(:run)
expect(new_resource).to be_updated
reresource =
- Chef::Resource::DscResource.new("dsc_resource_retest", run_context)
+ Chef::Resource::DscResource.new("dsc_resource_retest", run_context)
reresource.resource :File
reresource.property :Contents, test_text
reresource.property :DestinationPath, tmp_file_name
diff --git a/spec/functional/resource/dsc_script_spec.rb b/spec/functional/resource/dsc_script_spec.rb
index 42c23a695f..ce92c468f0 100644
--- a/spec/functional/resource/dsc_script_spec.rb
+++ b/spec/functional/resource/dsc_script_spec.rb
@@ -65,28 +65,27 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
end
def delete_user(target_user)
- begin
- shell_out!("net user #{target_user} /delete")
- rescue Mixlib::ShellOut::ShellCommandFailed
- end
+ shell_out!("net user #{target_user} /delete")
+ rescue Mixlib::ShellOut::ShellCommandFailed
end
let(:dsc_env_variable) { "chefenvtest" }
let(:dsc_env_value1) { "value1" }
let(:env_value2) { "value2" }
- let(:dsc_test_run_context) {
+ let(:dsc_test_run_context) do
node = Chef::Node.new
+ node.automatic["os"] = "windows"
node.automatic["platform"] = "windows"
node.automatic["platform_version"] = "6.1"
node.automatic["kernel"][:machine] = :x86_64 # Only 64-bit architecture is supported
node.automatic[:languages][:powershell][:version] = "4.0"
empty_events = Chef::EventDispatch::Dispatcher.new
Chef::RunContext.new(node, {}, empty_events)
- }
+ end
let(:dsc_test_resource_name) { "DSCTest" }
- let(:dsc_test_resource_base) {
+ let(:dsc_test_resource_base) do
Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context)
- }
+ end
let(:test_registry_key) { 'HKEY_LOCAL_MACHINE\Software\Chef\Spec\Functional\Resource\dsc_script_spec' }
let(:test_registry_value) { "Registration" }
let(:test_registry_data1) { "LL927" }
@@ -94,7 +93,8 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
let(:reg_key_name_param_name) { "testregkeyname" }
let(:reg_key_value_param_name) { "testregvaluename" }
let(:registry_embedded_parameters) { "$#{reg_key_name_param_name} = '#{test_registry_key}';$#{reg_key_value_param_name} = '#{test_registry_value}'" }
- let(:dsc_reg_code) { <<-EOH
+ let(:dsc_reg_code) do
+ <<-EOH
#{registry_embedded_parameters}
Registry "ChefRegKey"
{
@@ -104,14 +104,15 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
Ensure = 'Present'
}
EOH
- }
+ end
let(:dsc_code) { dsc_reg_code }
- let(:dsc_reg_script) { <<-EOH
+ let(:dsc_reg_script) do
+ <<-EOH
param($testregkeyname, $testregvaluename)
#{dsc_reg_code}
EOH
- }
+ end
let(:dsc_user_prefix) { "dsc" }
let(:dsc_user_suffix) { "chefx" }
@@ -128,7 +129,8 @@ EOH
let(:dsc_user_param_code) { "\"$(#{dsc_user_prefix_param_code})_usr_$(#{dsc_user_suffix_param_code})\"" }
let(:config_flags) { nil }
- let(:config_params) { <<-EOH
+ let(:config_params) do
+ <<-EOH
[CmdletBinding()]
param
@@ -137,14 +139,15 @@ EOH
$#{dsc_user_suffix_param_name}
)
EOH
- }
+ end
let(:config_param_section) { "" }
let(:dsc_user_code) { "'#{dsc_user}'" }
let(:dsc_user_prefix_code) { dsc_user_prefix }
let(:dsc_user_suffix_code) { dsc_user_suffix }
let(:dsc_script_environment_attribute) { nil }
- let(:dsc_user_resources_code) { <<-EOH
+ let(:dsc_user_resources_code) do
+ <<-EOH
#{config_param_section}
node localhost
{
@@ -164,9 +167,9 @@ User dsctestusercreate
}
}
EOH
- }
+ end
- let(:dsc_user_config_data) {
+ let(:dsc_user_config_data) do
<<-EOH
@{
AllNodes = @(
@@ -178,13 +181,14 @@ EOH
}
EOH
- }
+ end
let(:dsc_environment_env_var_name) { "dsc_test_cwd" }
let(:dsc_environment_no_fail_not_etc_directory) { "#{ENV['systemroot']}\\system32" }
let(:dsc_environment_fail_etc_directory) { "#{ENV['systemroot']}\\system32\\drivers\\etc" }
let(:exception_message_signature) { "LL927-LL928" }
- let(:dsc_environment_config) {<<-EOH
+ let(:dsc_environment_config) do
+ <<-EOH
if (($pwd.path -eq '#{dsc_environment_fail_etc_directory}') -and (test-path('#{dsc_environment_fail_etc_directory}')))
{
throw 'Signature #{exception_message_signature}: Purposefully failing because cwd == #{dsc_environment_fail_etc_directory}'
@@ -196,21 +200,21 @@ environment "whatsmydir"
Ensure = 'Present'
}
EOH
- }
+ end
- let(:dsc_config_name) {
+ let(:dsc_config_name) do
dsc_test_resource_base.name
- }
- let(:dsc_resource_from_code) {
+ end
+ let(:dsc_resource_from_code) do
dsc_test_resource_base.code(dsc_code)
dsc_test_resource_base
- }
+ end
let(:config_name_value) { dsc_test_resource_base.name }
- let(:dsc_resource_from_path) {
+ let(:dsc_resource_from_path) do
dsc_test_resource_base.command(create_config_script_from_code(dsc_code, config_name_value))
dsc_test_resource_base
- }
+ end
before(:each) do
test_key_resource = Chef::Resource::RegistryKey.new(test_registry_key, dsc_test_run_context)
@@ -469,6 +473,7 @@ EOF
end
it "allows the use of ps_credential" do
+ skip("Skipped until we can adjust the test cert to meet the WMF 5 cert requirements.")
expect(user_exists?(dsc_user)).to eq(false)
powershell_script_resource.run_action(:run)
expect(File).to exist(configuration_data_path)
diff --git a/spec/functional/resource/env_spec.rb b/spec/functional/resource/env_spec.rb
deleted file mode 100755
index 83328a6738..0000000000
--- a/spec/functional/resource/env_spec.rb
+++ /dev/null
@@ -1,192 +0,0 @@
-#
-# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-
-describe Chef::Resource::Env, :windows_only do
- context "when running on Windows" do
- let(:chef_env_test_lower_case) { "chefenvtest" }
- let(:chef_env_test_mixed_case) { "chefENVtest" }
- let(:env_dne_key) { "env_dne_key" }
- let(:env_value1) { "value1" }
- let(:env_value2) { "value2" }
-
- let(:env_value_expandable) { "%SystemRoot%" }
- let(:test_run_context) {
- node = Chef::Node.new
- node.default["os"] = "windows"
- node.default["platform"] = "windows"
- node.default["platform_version"] = "6.1"
- empty_events = Chef::EventDispatch::Dispatcher.new
- Chef::RunContext.new(node, {}, empty_events)
- }
- let(:test_resource) {
- Chef::Resource::Env.new("unknown", test_run_context)
- }
-
- before(:each) do
- resource_lower = Chef::Resource::Env.new(chef_env_test_lower_case, test_run_context)
- resource_lower.run_action(:delete)
- resource_mixed = Chef::Resource::Env.new(chef_env_test_mixed_case, test_run_context)
- resource_mixed.run_action(:delete)
- end
-
- context "when the create action is invoked" do
- it "should create an environment variable for action create" do
- expect(ENV[chef_env_test_lower_case]).to eq(nil)
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- end
-
- it "should modify an existing variable's value to a new value" do
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- test_resource.value(env_value2)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
- end
-
- it "should modify an existing variable's value to a new value if the variable name case differs from the existing variable" do
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- test_resource.key_name(chef_env_test_mixed_case)
- test_resource.value(env_value2)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
- end
-
- it "should not expand environment variables if the variable is not PATH" do
- expect(ENV[chef_env_test_lower_case]).to eq(nil)
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value_expandable)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value_expandable)
- end
- end
-
- context "when the modify action is invoked" do
- it "should raise an exception for modify if the variable doesn't exist" do
- expect(ENV[chef_env_test_lower_case]).to eq(nil)
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- expect { test_resource.run_action(:modify) }.to raise_error(Chef::Exceptions::Env)
- end
-
- it "should modify an existing variable's value to a new value" do
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- test_resource.value(env_value2)
- test_resource.run_action(:modify)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
- end
-
- # This examlpe covers Chef Issue #1754
- it "should modify an existing variable's value to a new value if the variable name case differs from the existing variable" do
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- test_resource.key_name(chef_env_test_mixed_case)
- test_resource.value(env_value2)
- test_resource.run_action(:modify)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
- end
-
- it "should not expand environment variables if the variable is not PATH" do
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- test_resource.value(env_value_expandable)
- test_resource.run_action(:modify)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value_expandable)
- end
-
- context "when using PATH" do
- let(:random_name) { Time.now.to_i }
- let(:env_val) { "#{env_value_expandable}_#{random_name}" }
- let!(:path_before) { test_resource.provider_for_action(test_resource.action).env_value("PATH") || "" }
- let!(:env_path_before) { ENV["PATH"] }
-
- it "should expand PATH" do
- expect(path_before).not_to include(env_val)
- test_resource.key_name("PATH")
- test_resource.value("#{path_before};#{env_val}")
- test_resource.run_action(:create)
- expect(ENV["PATH"]).not_to include(env_val)
- expect(ENV["PATH"]).to include("#{random_name}")
- end
-
- after(:each) do
- # cleanup so we don't flood the path
- test_resource.key_name("PATH")
- test_resource.value(path_before)
- test_resource.run_action(:create)
- ENV["PATH"] = env_path_before
- end
- end
-
- end
-
- context "when the delete action is invoked" do
- it "should delete an environment variable" do
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- test_resource.run_action(:delete)
- expect(ENV[chef_env_test_lower_case]).to eq(nil)
- end
-
- it "should not raise an exception when a non-existent environment variable is deleted" do
- expect(ENV[chef_env_test_lower_case]).to eq(nil)
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- expect { test_resource.run_action(:delete) }.not_to raise_error
- expect(ENV[chef_env_test_lower_case]).to eq(nil)
- end
-
- it "should delete an existing variable's value to a new value if the specified variable name case differs from the existing variable" do
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- test_resource.key_name(chef_env_test_mixed_case)
- test_resource.run_action(:delete)
- expect(ENV[chef_env_test_lower_case]).to eq(nil)
- expect(ENV[chef_env_test_mixed_case]).to eq(nil)
- end
-
- it "should delete a value from the current process even if it is not in the registry" do
- expect(ENV[env_dne_key]).to eq(nil)
- ENV[env_dne_key] = env_value1
- test_resource.key_name(env_dne_key)
- test_resource.run_action(:delete)
- expect(ENV[env_dne_key]).to eq(nil)
- end
- end
- end
-end
diff --git a/spec/functional/resource/execute_spec.rb b/spec/functional/resource/execute_spec.rb
index c5978da519..c0956c5594 100644
--- a/spec/functional/resource/execute_spec.rb
+++ b/spec/functional/resource/execute_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright 2014-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,11 +21,11 @@ require "functional/resource/base"
require "timeout"
describe Chef::Resource::Execute do
- let(:resource) {
+ let(:resource) do
resource = Chef::Resource::Execute.new("foo_resource", run_context)
resource.command("echo hello")
resource
- }
+ end
describe "when guard is ruby block" do
it "guard can still run" do
@@ -41,17 +41,17 @@ describe Chef::Resource::Execute do
end
let(:guard) { "ruby -e 'exit 0'" }
- let!(:guard_resource) {
+ let!(:guard_resource) do
interpreter = Chef::GuardInterpreter::ResourceGuardInterpreter.new(resource, guard, nil)
interpreter.send(:get_interpreter_resource, resource)
- }
+ end
it "executes the guard and not the regular resource" do
expect_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:get_interpreter_resource).and_return(guard_resource)
# why_run mode doesn't disable the updated_by_last_action logic, so we really have to look at the provider action
# to see if why_run correctly disabled the resource. It should shell_out! for the guard but not the resource.
- expect_any_instance_of(Chef::Provider::Execute).to receive(:shell_out!).once
+ expect_any_instance_of(Chef::Provider::Execute).to receive(:shell_out_with_systems_locale!).once
resource.only_if guard
resource.run_action(:run)
@@ -137,6 +137,18 @@ describe Chef::Resource::Execute do
end
end
+ describe "when a guard is specified" do
+ describe "when using the default guard interpreter" do
+ let(:guard_interpreter_resource) { nil }
+ it_behaves_like "a resource with a guard specifying an alternate user identity"
+ end
+
+ describe "when using the execute resource as the guard interpreter" do
+ let(:guard_interpreter_resource) { :execute }
+ it_behaves_like "a resource with a guard specifying an alternate user identity"
+ end
+ end
+
# Ensure that CommandTimeout is raised, and is caused by resource.timeout really expiring.
# https://github.com/chef/chef/issues/2985
#
@@ -151,4 +163,9 @@ describe Chef::Resource::Execute do
expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::CommandTimeout)
end
end
+
+ describe "when running with an alternate user identity" do
+ let(:resource_command_property) { :command }
+ it_behaves_like "an execute resource that supports alternate user identity"
+ end
end
diff --git a/spec/functional/resource/group_spec.rb b/spec/functional/resource/group_spec.rb
index a5de63b7c6..ea9aa5c2b7 100644
--- a/spec/functional/resource/group_spec.rb
+++ b/spec/functional/resource/group_spec.rb
@@ -81,25 +81,39 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
if user && domain != "."
computer_name = ENV["computername"]
- !domain.casecmp(computer_name.downcase).zero?
+ !domain.casecmp(computer_name.downcase) == 0
end
end
+ def node
+ node = Chef::Node.new
+ node.consume_external_attrs(ohai.data, {})
+ node
+ end
+
def user(username)
- usr = Chef::Resource::User.new("#{username}", run_context)
+ usr = Chef::Resource.resource_for_node(:user, node).new(username, run_context)
if ohai[:platform_family] == "windows"
usr.password("ComplexPass11!")
end
usr
end
- def create_user(username)
- user(username).run_action(:create) if ! windows_domain_user?(username)
+ def create_user(username, uid = nil)
+ if ! windows_domain_user?(username)
+ user_to_create = user(username)
+ user_to_create.uid(uid) if uid
+ user_to_create.run_action(:create)
+ end
# TODO: User should exist
end
def remove_user(username)
- user(username).run_action(:remove) if ! windows_domain_user?(username)
+ if ! windows_domain_user?(username)
+ u = user(username)
+ u.manage_home false # jekins hosts throw mail spool file not owned by user if we use manage_home true
+ u.run_action(:remove)
+ end
# TODO: User shouldn't exist
end
@@ -159,8 +173,11 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
describe "when the users exist" do
before do
+ high_uid = 30000
(spec_members).each do |member|
- create_user(member)
+ remove_user(member)
+ create_user(member, high_uid)
+ high_uid += 1
end
end
@@ -282,12 +299,13 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
let(:group_name) { "group#{SecureRandom.random_number(9999)}" }
let(:included_members) { nil }
let(:excluded_members) { nil }
- let(:group_resource) {
+ let(:group_resource) do
group = Chef::Resource::Group.new(group_name, run_context)
group.members(included_members)
group.excluded_members(excluded_members)
+ group.gid(30000) unless ohai[:platform_family] == "mac_os_x"
group
- }
+ end
it "append should be false by default" do
expect(group_resource.append).to eq(false)
@@ -305,10 +323,11 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
end
describe "when group name is length 256", :windows_only do
- let!(:group_name) { "theoldmanwalkingdownthestreetalwayshadagood\
+ let!(:group_name) do
+ "theoldmanwalkingdownthestreetalwayshadagood\
smileonhisfacetheoldmanwalkingdownthestreetalwayshadagoodsmileonhisface\
theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking\
-downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestree" }
+downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestree" end
it "should create a group" do
group_resource.run_action(:create)
@@ -316,18 +335,6 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestree" }
end
end
- describe "when group name length is more than 256", :windows_only do
- let!(:group_name) { "theoldmanwalkingdownthestreetalwayshadagood\
-smileonhisfacetheoldmanwalkingdownthestreetalwayshadagoodsmileonhisface\
-theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking\
-downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" }
-
- it "should not create a group" do
- expect { group_resource.run_action(:create) }.to raise_error(ArgumentError)
- group_should_not_exist(group_name)
- end
- end
-
# not_supported_on_solaris because of the use of excluded_members
describe "should raise an error when same member is included in the members and excluded_members", :not_supported_on_solaris do
it "should raise an error" do
@@ -339,6 +346,25 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" }
end
end
+ # Note:This testcase is written separately from the `group create action` defined above because
+ # for group name > 256, Windows 2016 returns "The parameter is incorrect"
+ context "group create action: when group name length is more than 256", :windows_only do
+ let!(:group_name) do
+ "theoldmanwalkingdownthestreetalwayshadagood\
+smileonhisfacetheoldmanwalkingdownthestreetalwayshadagoodsmileonhisface\
+theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking\
+downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" end
+
+ it "should not create a group" do
+ expect { group_resource.run_action(:create) }.to raise_error(ArgumentError)
+ if windows_gte_10?
+ expect { Chef::Util::Windows::NetGroup.new(group_name).local_get_members }.to raise_error(ArgumentError, /The parameter is incorrect./)
+ else
+ group_should_not_exist(group_name)
+ end
+ end
+ end
+
describe "group remove action" do
describe "when there is a group" do
before do
@@ -405,6 +431,7 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" }
end
it "does not raise an error on manage" do
+ allow(Etc).to receive(:getpwnam).and_return(double("User"))
expect { group_resource.run_action(:manage) }.not_to raise_error
end
end
diff --git a/spec/functional/resource/ifconfig_spec.rb b/spec/functional/resource/ifconfig_spec.rb
index a30dcff641..f32ed069b5 100644
--- a/spec/functional/resource/ifconfig_spec.rb
+++ b/spec/functional/resource/ifconfig_spec.rb
@@ -16,11 +16,12 @@
# limitations under the License.
#
+require "spec_helper"
require "functional/resource/base"
require "chef/mixin/shell_out"
# run this test only for following platforms.
-include_flag = !(%w{ubuntu centos aix}.include?(ohai[:platform]))
+include_flag = !(%w{amazon debian aix}.include?(ohai[:platform_family]) || (ohai[:platform_family] == "rhel" && ohai[:platform_version].to_i < 7))
describe Chef::Resource::Ifconfig, :requires_root, :skip_travis, :external => include_flag do
# This test does not work in travis because there is no eth0
@@ -51,11 +52,17 @@ describe Chef::Resource::Ifconfig, :requires_root, :skip_travis, :external => in
end
end
+ def fetch_first_interface_name
+ shell_out("ip link list |grep UP|grep -vi loop|head -1|cut -d':' -f 2").stdout.strip
+ end
+
# **Caution: any updates to core interfaces can be risky.
def en0_interface_for_test
case ohai[:platform]
when "aix"
"en0"
+ when "ubuntu"
+ fetch_first_interface_name
else
"eth0"
end
diff --git a/spec/functional/resource/link_spec.rb b/spec/functional/resource/link_spec.rb
index 6dd6ad8422..2b8b509730 100644
--- a/spec/functional/resource/link_spec.rb
+++ b/spec/functional/resource/link_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright 2011-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,12 +20,14 @@ require "spec_helper"
if windows?
require "chef/win32/file" #probably need this in spec_helper
+ require "chef/win32/security"
end
describe Chef::Resource::Link do
let(:file_base) { "file_spec" }
let(:expect_updated?) { true }
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
# We create the files in a different directory than tmp to exercise
# different file deployment strategies more completely.
@@ -62,6 +64,18 @@ describe Chef::Resource::Link do
end
end
+ def node
+ node = Chef::Node.new
+ node.consume_external_attrs(ohai.data, {})
+ node
+ end
+
+ def user(user)
+ usr = Chef::Resource.resource_for_node(:user, node).new(user, run_context)
+ usr.password("ComplexPass11!") if windows?
+ usr
+ end
+
def cleanup_link(path)
if windows? && File.directory?(path)
# If the link target is a directory rm_rf doesn't work all the
@@ -108,12 +122,49 @@ describe Chef::Resource::Link do
end
end
+ let(:test_user) { windows? ? nil : ENV["USER"] }
+
+ def expected_owner
+ if windows?
+ get_sid(test_user)
+ else
+ test_user
+ end
+ end
+
+ def get_sid(value)
+ if value.kind_of?(String)
+ Chef::ReservedNames::Win32::Security::SID.from_account(value)
+ elsif value.kind_of?(Chef::ReservedNames::Win32::Security::SID)
+ value
+ else
+ raise "Must specify username or SID: #{value}"
+ end
+ end
+
+ def chown(file, user)
+ if windows?
+ Chef::ReservedNames::Win32::Security::SecurableObject.new(file).owner = get_sid(user)
+ else
+ File.lchown(Etc.getpwnam(user).uid, Etc.getpwnam(user).gid, file)
+ end
+ end
+
+ def owner(file)
+ if windows?
+ Chef::ReservedNames::Win32::Security::SecurableObject.new(file).security_descriptor.owner
+ else
+ Etc.getpwuid(File.lstat(file).uid).name
+ end
+ end
+
def create_resource
node = Chef::Node.new
events = Chef::EventDispatch::Dispatcher.new
cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks"))
cookbook_collection = Chef::CookbookCollection.new(Chef::CookbookLoader.new(cookbook_repo))
run_context = Chef::RunContext.new(node, cookbook_collection, events)
+ allow(run_context).to receive(:logger).and_return(logger)
resource = Chef::Resource::Link.new(target_file, run_context)
resource.to(to)
resource
@@ -123,7 +174,7 @@ describe Chef::Resource::Link do
create_resource
end
- describe "when supported on platform", :not_supported_on_win2k3 do
+ describe "when supported on platform" do
shared_examples_for "delete errors out" do
it "delete errors out" do
expect { resource.run_action(:delete) }.to raise_error(Chef::Exceptions::Link)
@@ -135,7 +186,7 @@ describe Chef::Resource::Link do
describe "the :delete action" do
before(:each) do
@info = []
- allow(Chef::Log).to receive(:info) { |msg| @info << msg }
+ allow(logger).to receive(:info) { |msg| @info << msg }
resource.run_action(:delete)
end
@@ -156,7 +207,7 @@ describe Chef::Resource::Link do
describe "the :delete action" do
before(:each) do
@info = []
- allow(Chef::Log).to receive(:info) { |msg| @info << msg }
+ allow(logger).to receive(:info) { |msg| @info << msg }
resource.run_action(:delete)
end
@@ -177,13 +228,14 @@ describe Chef::Resource::Link do
describe "the :create action" do
before(:each) do
@info = []
- allow(Chef::Log).to receive(:info) { |msg| @info << msg }
+ allow(logger).to receive(:info) { |msg| @info << msg }
resource.run_action(:create)
end
it "links to the target file" do
expect(symlink?(target_file)).to be_truthy
expect(readlink(target_file)).to eq(canonicalize(to))
+ expect(owner(target_file)).to eq(expected_owner) unless test_user.nil?
end
it "marks the resource updated" do
expect(resource).to be_updated
@@ -198,13 +250,14 @@ describe Chef::Resource::Link do
describe "the :create action" do
before(:each) do
@info = []
- allow(Chef::Log).to receive(:info) { |msg| @info << msg }
+ allow(logger).to receive(:info) { |msg| @info << msg }
resource.run_action(:create)
end
it "leaves the file linked" do
expect(symlink?(target_file)).to be_truthy
expect(readlink(target_file)).to eq(canonicalize(to))
+ expect(owner(target_file)).to eq(expected_owner) unless test_user.nil?
end
it "does not mark the resource updated" do
expect(resource).not_to be_updated
@@ -219,7 +272,7 @@ describe Chef::Resource::Link do
describe "the :create action" do
before(:each) do
@info = []
- allow(Chef::Log).to receive(:info) { |msg| @info << msg }
+ allow(logger).to receive(:info) { |msg| @info << msg }
resource.run_action(:create)
end
it "preserves the hard link" do
@@ -244,7 +297,7 @@ describe Chef::Resource::Link do
describe "the :create action" do
before(:each) do
@info = []
- allow(Chef::Log).to receive(:info) { |msg| @info << msg }
+ allow(logger).to receive(:info) { |msg| @info << msg }
resource.run_action(:create)
end
it "links to the target file" do
@@ -291,13 +344,23 @@ describe Chef::Resource::Link do
expect(File.exists?(to)).to be_truthy
end
end
- context "pointing somewhere else" do
+ context "pointing somewhere else", :requires_root_or_running_windows do
+ let(:test_user) { "test-link-user" }
+ before do
+ user(test_user).run_action(:create)
+ end
+ after do
+ user(test_user).run_action(:remove)
+ end
before(:each) do
+ resource.owner(test_user)
@other_target = File.join(test_file_dir, make_tmpname("other_spec"))
File.open(@other_target, "w") { |file| file.write("eek") }
symlink(@other_target, target_file)
+ chown(target_file, test_user)
expect(symlink?(target_file)).to be_truthy
expect(readlink(target_file)).to eq(canonicalize(@other_target))
+ expect(owner(target_file)).to eq(expected_owner)
end
after(:each) do
File.delete(@other_target)
@@ -388,6 +451,14 @@ describe Chef::Resource::Link do
symlink(other_dir, target_file)
end
include_context "create symbolic link succeeds"
+ include_context "delete succeeds"
+ end
+ context "and the link already exists and points at the target" do
+ before do
+ symlink(to, target_file)
+ end
+ include_context "create symbolic link is noop"
+ include_context "delete succeeds"
end
end
context "when the link destination is a symbolic link" do
@@ -406,6 +477,19 @@ describe Chef::Resource::Link do
include_context "create symbolic link succeeds"
include_context "delete is noop"
end
+ context "and the destination itself has another symbolic link" do
+ context "to a link that exist" do
+ before do
+ symlink(to, target_file)
+ end
+ include_context "create symbolic link is noop"
+ include_context "delete succeeds"
+ end
+ context "to a link that does not exist" do
+ include_context "create symbolic link succeeds"
+ include_context "delete is noop"
+ end
+ end
end
context "to a file that does not exist" do
before(:each) do
@@ -418,6 +502,19 @@ describe Chef::Resource::Link do
include_context "create symbolic link succeeds"
include_context "delete is noop"
end
+ context "and the destination itself has another symbolic link" do
+ context "to a link that exist" do
+ before do
+ symlink(to, target_file)
+ end
+ include_context "create symbolic link is noop"
+ include_context "delete succeeds"
+ end
+ context "to a link that does not exist" do
+ include_context "create symbolic link succeeds"
+ include_context "delete is noop"
+ end
+ end
end
end
context "when the link destination does not exist" do
@@ -559,7 +656,7 @@ describe Chef::Resource::Link do
end
context "and the link does not yet exist" do
it "links to the target file" do
- skip("OS X/FreeBSD/AIX symlink? and readlink working on hard links to symlinks") if os_x? || freebsd? || aix?
+ skip("OS X/FreeBSD/AIX/Solaris symlink? and readlink working on hard links to symlinks") if os_x? || freebsd? || aix? || solaris?
resource.run_action(:create)
expect(File.exists?(target_file)).to be_truthy
# OS X gets angry about this sort of link. Bug in OS X, IMO.
@@ -578,14 +675,9 @@ describe Chef::Resource::Link do
end
context "and the link does not yet exist" do
it "links to the target file" do
- skip("OS X/FreeBSD/AIX fails to create hardlinks to broken symlinks") if os_x? || freebsd? || aix?
+ skip("OS X/FreeBSD/AIX/Solaris fails to create hardlinks to broken symlinks") if os_x? || freebsd? || aix? || solaris?
resource.run_action(:create)
- # Windows and Unix have different definitions of exists? here, and that's OK.
- if windows?
- expect(File.exists?(target_file)).to be_truthy
- else
- expect(File.exists?(target_file)).to be_falsey
- end
+ expect(File.exists?(target_file) || File.symlink?(target_file)).to be_truthy
expect(symlink?(target_file)).to be_truthy
expect(readlink(target_file)).to eq(canonicalize(@other_target))
end
@@ -604,10 +696,4 @@ describe Chef::Resource::Link do
end
end
end
-
- describe "when not supported on platform", :win2k3_only do
- it "raises error" do
- expect { resource }.to raise_error(Chef::Exceptions::Win32APIFunctionNotImplemented)
- end
- end
end
diff --git a/spec/functional/resource/mount_spec.rb b/spec/functional/resource/mount_spec.rb
index c756b0d3d4..c98d6cec25 100644
--- a/spec/functional/resource/mount_spec.rb
+++ b/spec/functional/resource/mount_spec.rb
@@ -22,7 +22,7 @@ require "chef/mixin/shell_out"
require "tmpdir"
# run this test only for following platforms.
-include_flag = !(%w{ubuntu centos aix solaris2}.include?(ohai[:platform]))
+include_flag = !(%w{debian rhel amazon aix solaris2}.include?(ohai[:platform_family]))
describe Chef::Resource::Mount, :requires_root, :skip_travis, :external => include_flag do
# Disabled in travis because it refuses to let us mount a ramdisk. /dev/ramX does not
@@ -35,15 +35,19 @@ describe Chef::Resource::Mount, :requires_root, :skip_travis, :external => inclu
def setup_device_for_mount
# use ramdisk for creating a test device for mount.
# This can cleaner if we have chef resource/provider for ramdisk.
- case ohai[:platform]
+ case ohai[:platform_family]
when "aix"
# On AIX, we can't create a ramdisk inside a WPAR, so we use
# a "namefs" mount against / to test
# https://www-304.ibm.com/support/knowledgecenter/ssw_aix_71/com.ibm.aix.performance/namefs_file_sys.htm
device = "/"
fstype = "namefs"
- when "ubuntu", "centos"
+ when "debian", "rhel", "amazon"
device = "/dev/ram1"
+ unless File.exist?(device)
+ shell_out("mknod -m 660 #{device} b 1 0")
+ shell_out("chown root:disk #{device}")
+ end
shell_out("ls -1 /dev/ram*").stdout.each_line do |d|
if shell_out("mount | grep #{d}").exitstatus == "1"
# this device is not mounted, so use it.
diff --git a/spec/functional/resource/msu_package_spec.rb b/spec/functional/resource/msu_package_spec.rb
new file mode 100644
index 0000000000..23342be6ae
--- /dev/null
+++ b/spec/functional/resource/msu_package_spec.rb
@@ -0,0 +1,84 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/provider/package/cab"
+
+describe Chef::Resource::MsuPackage, :win2012r2_only do
+
+ let(:package_name) { "Package_for_KB2959977" }
+ let(:package_source) { "https://download.microsoft.com/download/3/B/3/3B320C07-B7B1-41E5-81F4-79EBC02DF7D3/Windows8.1-KB2959977-x64.msu" }
+
+ let(:new_resource) { Chef::Resource::CabPackage.new("windows_test_pkg") }
+ let(:cab_provider) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ Chef::Provider::Package::Cab.new(new_resource, run_context)
+ end
+
+ subject do
+ new_resource = Chef::Resource::MsuPackage.new("test msu package", run_context)
+ new_resource.package_name package_name
+ new_resource.source package_source
+ new_resource
+ end
+
+ context "installing package" do
+ after { remove_package }
+
+ it "installs the package successfully" do
+ subject.run_action(:install)
+ found_packages = cab_provider.installed_packages.select { |p| p["package_identity"] =~ /^#{package_name}~/ }
+ expect(found_packages.length).to be == 1
+ end
+ end
+
+ context "removing a package" do
+ it "removes an installed package" do
+ subject.run_action(:install)
+ remove_package
+ found_packages = cab_provider.installed_packages.select { |p| p["package_identity"] =~ /^#{package_name}~/ }
+ expect(found_packages.length).to be == 0
+ end
+ end
+
+ context "when an invalid msu package is given" do
+ def package_name
+ "Package_for_KB2859903"
+ end
+
+ def package_source
+ "https://download.microsoft.com/download/5/2/B/52BE95BF-22D8-4415-B644-0FDF398F6D96/IE10-Windows6.1-KB2859903-x86.msu"
+ end
+
+ it "raises error while installing" do
+ expect { subject.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ it "raises error while removing" do
+ expect { subject.run_action(:remove) }.to raise_error(Chef::Exceptions::Package)
+ end
+ end
+
+ def remove_package
+ pkg_to_remove = Chef::Resource::MsuPackage.new(package_name, run_context)
+ pkg_to_remove.source = package_source
+ pkg_to_remove.run_action(:remove)
+ end
+end
diff --git a/spec/functional/resource/ohai_spec.rb b/spec/functional/resource/ohai_spec.rb
index 9ce989d8df..06bccfc398 100644
--- a/spec/functional/resource/ohai_spec.rb
+++ b/spec/functional/resource/ohai_spec.rb
@@ -19,18 +19,18 @@
require "spec_helper"
describe Chef::Resource::Ohai do
- let(:ohai) {
+ let(:ohai) do
OHAI_SYSTEM
- }
+ end
let(:node) { Chef::Node.new }
- let(:run_context) {
+ let(:run_context) do
node.default[:platform] = ohai[:platform]
node.default[:platform_version] = ohai[:platform_version]
events = Chef::EventDispatch::Dispatcher.new
Chef::RunContext.new(node, {}, events)
- }
+ end
shared_examples_for "reloaded :uptime" do
it "should reload :uptime" do
@@ -51,11 +51,11 @@ describe Chef::Resource::Ohai do
end
describe "when reloading only uptime" do
- let(:ohai_resource) {
+ let(:ohai_resource) do
r = Chef::Resource::Ohai.new("reload all", run_context)
r.plugin("uptime")
r
- }
+ end
it_behaves_like "reloaded :uptime"
end
diff --git a/spec/functional/resource/powershell_script_spec.rb b/spec/functional/resource/powershell_script_spec.rb
index af345b0ea4..bbd304fd06 100644
--- a/spec/functional/resource/powershell_script_spec.rb
+++ b/spec/functional/resource/powershell_script_spec.rb
@@ -36,8 +36,6 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
let(:cmdlet_exit_code_success_content) { "get-item ." }
let(:windows_process_exit_code_success_content) { "#{ENV['SystemRoot']}\\system32\\attrib.exe $env:systemroot" }
let(:windows_process_exit_code_not_found_content) { "findstr /notavalidswitch" }
- # Note that process exit codes on 32-bit Win2k3 cannot
- # exceed maximum value of signed integer
let(:arbitrary_nonzero_process_exit_code) { 4193 }
let(:arbitrary_nonzero_process_exit_code_content) { "exit #{arbitrary_nonzero_process_exit_code}" }
let(:invalid_powershell_interpreter_flag) { "/thisflagisinvalid" }
diff --git a/spec/functional/resource/reboot_spec.rb b/spec/functional/resource/reboot_spec.rb
index 3cf7f58e55..c264b122a7 100644
--- a/spec/functional/resource/reboot_spec.rb
+++ b/spec/functional/resource/reboot_spec.rb
@@ -80,9 +80,9 @@ describe Chef::Resource::Reboot do
it "should have modified the run context correctly" do
# this doesn't actually test the flow of Chef::Client#do_run, unfortunately.
- expect {
+ expect do
resource.run_action(:reboot_now)
- }.to throw_symbol(:end_client_run_early)
+ end.to throw_symbol(:end_client_run_early)
test_reboot_action(resource)
end
diff --git a/spec/functional/resource/registry_spec.rb b/spec/functional/resource/registry_spec.rb
index e64b6697c5..7318086034 100644
--- a/spec/functional/resource/registry_spec.rb
+++ b/spec/functional/resource/registry_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Prajakta Purohit (<prajakta@chef.io>)
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright 2011-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -112,9 +112,9 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
@node.name("windowsbox")
@rest_client = double("Chef::ServerAPI (mock)")
- allow(@rest_client).to receive(:create_url).and_return("reports/nodes/windowsbox/runs/#{@run_id}");
- allow(@rest_client).to receive(:raw_http_request).and_return({ "result" => "ok" });
- allow(@rest_client).to receive(:post_rest).and_return({ "uri" => "https://example.com/reports/nodes/windowsbox/runs/#{@run_id}" });
+ allow(@rest_client).to receive(:create_url).and_return("reports/nodes/windowsbox/runs/#{@run_id}")
+ allow(@rest_client).to receive(:raw_http_request).and_return({ "result" => "ok" })
+ allow(@rest_client).to receive(:post_rest).and_return({ "uri" => "https://example.com/reports/nodes/windowsbox/runs/#{@run_id}" })
@resource_reporter = Chef::ResourceReporter.new(@rest_client)
@events.register(@resource_reporter)
@@ -124,7 +124,7 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
@new_resource.cookbook_name = "monkey"
@cookbook_version = double("Cookbook::Version", :version => "1.2.3")
- allow(@new_resource).to receive(:cookbook_version).and_return(@cookbook_version)
+ @new_resource.cookbook_version(@cookbook_version)
end
after (:all) do
@@ -153,6 +153,16 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
expect(@registry.data_exists?(reg_child, { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
end
+ it "does not create the key if it already exists with same value and type but datatype of data differs" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ :name => "number", :type => :dword, :data => "12345" }])
+ @new_resource.run_action(:create)
+
+ expect(@new_resource).not_to be_updated_by_last_action
+ expect(@registry.key_exists?(reg_child)).to eq(true)
+ expect(@registry.data_exists?(reg_child, { :name => "number", :type => :dword, :data => 12344 })).to eq(true)
+ end
+
it "creates a value if it does not exist" do
@new_resource.key(reg_child)
@new_resource.values([{ :name => "Mango", :type => :string, :data => "Yellow" }])
@@ -187,13 +197,37 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
expect(@registry.value_exists?(reg_child + '\OpscodeTest', { :name => "Chef", :type => :multi_string, :data => %w{OpscodeOrange Rules} })).to eq(true)
end
- it "gives error if action create and parent does not exist and recursive is set to false" do
+ it "raises an error if action create and parent does not exist and recursive is set to false" do
@new_resource.key(reg_child + '\Missing1\Missing2')
@new_resource.values([{ :name => "OC", :type => :string, :data => "MissingData" }])
@new_resource.recursive(false)
expect { @new_resource.run_action(:create) }.to raise_error(Chef::Exceptions::Win32RegNoRecursive)
end
+ it "raises an error if action create and type key missing in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ :name => "OC", :data => "my_data" }])
+ expect { @new_resource.run_action(:create) }.to raise_error(Chef::Exceptions::RegKeyValuesTypeMissing)
+ end
+
+ it "raises an error if action create and data key missing in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ :name => "OC", :type => :string }])
+ expect { @new_resource.run_action(:create) }.to raise_error(Chef::Exceptions::RegKeyValuesDataMissing)
+ end
+
+ it "raises an error if action create and only name key present in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ :name => "OC" }])
+ expect { @new_resource.run_action(:create) }.to raise_error(Chef::Exceptions::RegKeyValuesTypeMissing)
+ end
+
+ it "does not raise an error if action create and all keys are present in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ :name => "OC", :type => :string, :data => "my_data" }])
+ expect { @new_resource.run_action(:create) }.to_not raise_error
+ end
+
it "creates missing keys if action create and parent does not exist and recursive is set to true" do
@new_resource.key(reg_child + '\Missing1\Missing2')
@new_resource.values([{ :name => "OC", :type => :string, :data => "MissingData" }])
@@ -210,7 +244,7 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
@new_resource.recursive(true)
@new_resource.run_action(:create)
- @new_resource.values.each do |value|
+ @new_resource.each_value do |value|
expect(@registry.value_exists?(reg_child, value)).to eq(true)
end
end
@@ -260,7 +294,7 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
Chef::Config[:why_run] = true
end
- it "does not throw an exception if the keys do not exist but recursive is set to false" do
+ it "does not raise an exception if the keys do not exist but recursive is set to false" do
@new_resource.key(reg_child + '\Slitheen\Raxicoricofallapatorius')
@new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "is good for health" }])
@new_resource.recursive(false)
@@ -268,6 +302,7 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
expect(@registry.key_exists?(reg_child + '\Slitheen')).to eq(false)
expect(@registry.key_exists?(reg_child + '\Slitheen\Raxicoricofallapatorius')).to eq(false)
end
+
it "does not create key if the action is create" do
@new_resource.key(reg_child + '\Slitheen')
@new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "is good for health" }])
@@ -275,6 +310,34 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
@new_resource.run_action(:create)
expect(@registry.key_exists?(reg_child + '\Slitheen')).to eq(false)
end
+
+ it "does not raise an exception if the action create and type key missing in values hash" do
+ @new_resource.key(reg_child + '\Slitheen')
+ @new_resource.values([{ :name => "BriskWalk", :data => "my_data" }])
+ @new_resource.run_action(:create) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Slitheen')).to eq(false)
+ end
+
+ it "does not raise an exception if the action create and data key missing in values hash" do
+ @new_resource.key(reg_child + '\Slitheen')
+ @new_resource.values([{ :name => "BriskWalk", :type => :string }])
+ @new_resource.run_action(:create) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Slitheen')).to eq(false)
+ end
+
+ it "does not raise an exception if the action create and only name key present in values hash" do
+ @new_resource.key(reg_child + '\Slitheen')
+ @new_resource.values([{ :name => "BriskWalk" }])
+ @new_resource.run_action(:create) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Slitheen')).to eq(false)
+ end
+
+ it "does not raise an exception if the action create and all keys are present in values hash" do
+ @new_resource.key(reg_child + '\Slitheen')
+ @new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "my_data" }])
+ @new_resource.run_action(:create) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Slitheen')).to eq(false)
+ end
end
end
@@ -320,13 +383,37 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
expect(@registry.value_exists?(reg_child + '\Pyrovile', { :name => "Chef", :type => :multi_string, :data => %w{OpscodeOrange Rules} })).to eq(true)
end
- it "gives error if action create and parent does not exist and recursive is set to false" do
+ it "raises an error if action create and parent does not exist and recursive is set to false" do
@new_resource.key(reg_child + '\Sontaran\Sontar')
@new_resource.values([{ :name => "OC", :type => :string, :data => "MissingData" }])
@new_resource.recursive(false)
expect { @new_resource.run_action(:create_if_missing) }.to raise_error(Chef::Exceptions::Win32RegNoRecursive)
end
+ it "raises an error if action create_if_missing and type key missing in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ :name => "OC", :data => "my_data" }])
+ expect { @new_resource.run_action(:create_if_missing) }.to raise_error(Chef::Exceptions::RegKeyValuesTypeMissing)
+ end
+
+ it "raises an error if action create_if_missing and data key missing in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ :name => "OC", :type => :string }])
+ expect { @new_resource.run_action(:create_if_missing) }.to raise_error(Chef::Exceptions::RegKeyValuesDataMissing)
+ end
+
+ it "raises an error if action create_if_missing and only name key present in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ :name => "OC" }])
+ expect { @new_resource.run_action(:create_if_missing) }.to raise_error(Chef::Exceptions::RegKeyValuesTypeMissing)
+ end
+
+ it "does not raise an error if action create_if_missing and all keys are present in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ :name => "OC", :type => :string, :data => "my_data" }])
+ expect { @new_resource.run_action(:create_if_missing) }.to_not raise_error
+ end
+
it "creates missing keys if action create and parent does not exist and recursive is set to true" do
@new_resource.key(reg_child + '\Sontaran\Sontar')
@new_resource.values([{ :name => "OC", :type => :string, :data => "MissingData" }])
@@ -343,7 +430,7 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
@new_resource.recursive(true)
@new_resource.run_action(:create_if_missing)
- @new_resource.values.each do |value|
+ @new_resource.each_value do |value|
expect(@registry.value_exists?(reg_child + '\Adipose', value)).to eq(true)
end
end
@@ -371,7 +458,7 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
Chef::Config[:why_run] = true
end
- it "does not throw an exception if the keys do not exist but recursive is set to false" do
+ it "does not raise an exception if the keys do not exist but recursive is set to false" do
@new_resource.key(reg_child + '\Zygons\Zygor')
@new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "is good for health" }])
@new_resource.recursive(false)
@@ -379,6 +466,7 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
expect(@registry.key_exists?(reg_child + '\Zygons')).to eq(false)
expect(@registry.key_exists?(reg_child + '\Zygons\Zygor')).to eq(false)
end
+
it "does nothing if the action is create_if_missing" do
@new_resource.key(reg_child + '\Zygons')
@new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "is good for health" }])
@@ -386,6 +474,34 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
@new_resource.run_action(:create_if_missing)
expect(@registry.key_exists?(reg_child + '\Zygons')).to eq(false)
end
+
+ it "does not raise an exception if the action create_if_missing and type key missing in values hash" do
+ @new_resource.key(reg_child + '\Zygons')
+ @new_resource.values([{ :name => "BriskWalk", :data => "my_data" }])
+ @new_resource.run_action(:create_if_missing) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Zygons')).to eq(false)
+ end
+
+ it "does not raise an exception if the action create_if_missing and data key missing in values hash" do
+ @new_resource.key(reg_child + '\Zygons')
+ @new_resource.values([{ :name => "BriskWalk", :type => :string }])
+ @new_resource.run_action(:create_if_missing) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Zygons')).to eq(false)
+ end
+
+ it "does not raise an exception if the action create_if_missing and only name key present in values hash" do
+ @new_resource.key(reg_child + '\Zygons')
+ @new_resource.values([{ :name => "BriskWalk" }])
+ @new_resource.run_action(:create_if_missing) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Zygons')).to eq(false)
+ end
+
+ it "does not raise an exception if the action create_if_missing and all keys are present in values hash" do
+ @new_resource.key(reg_child + '\Zygons')
+ @new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "my_data" }])
+ @new_resource.run_action(:create_if_missing) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Zygons')).to eq(false)
+ end
end
end
diff --git a/spec/functional/resource/remote_file_spec.rb b/spec/functional/resource/remote_file_spec.rb
index b394bd0240..8e484b2968 100644
--- a/spec/functional/resource/remote_file_spec.rb
+++ b/spec/functional/resource/remote_file_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright 2011-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,6 +28,9 @@ describe Chef::Resource::RemoteFile do
before(:each) do
@old_file_cache = Chef::Config[:file_cache_path]
Chef::Config[:file_cache_path] = file_cache_path
+ Chef::Config[:rest_timeout] = 2
+ Chef::Config[:http_retry_delay] = 0
+ Chef::Config[:http_retry_count] = 0
end
after(:each) do
@@ -56,7 +59,7 @@ describe Chef::Resource::RemoteFile do
context "when fetching files over HTTP" do
before(:all) do
- start_tiny_server
+ start_tiny_server(RequestTimeout: 1)
end
after(:all) do
@@ -103,10 +106,11 @@ describe Chef::Resource::RemoteFile do
key_text = File.read(File.expand_path("ssl/chef-rspec.key", CHEF_SPEC_DATA))
key = OpenSSL::PKey::RSA.new(key_text)
- server_opts = { :SSLEnable => true,
- :SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
- :SSLCertificate => cert,
- :SSLPrivateKey => key }
+ server_opts = { SSLEnable: true,
+ SSLVerifyClient: OpenSSL::SSL::VERIFY_NONE,
+ SSLCertificate: cert,
+ SSLPrivateKey: key,
+ RequestTimeout: 1 }
start_tiny_server(server_opts)
end
@@ -123,9 +127,180 @@ describe Chef::Resource::RemoteFile do
end
+ context "when running on Windows", :windows_only do
+ describe "when fetching files over SMB" do
+ include Chef::Mixin::ShellOut
+ let(:smb_share_root_directory) { directory = File.join(Dir.tmpdir, make_tmpname("windows_script_test")); Dir.mkdir(directory); directory }
+ let(:smb_file_local_file_name) { "smb_file.txt" }
+ let(:smb_file_local_path) { File.join( smb_share_root_directory, smb_file_local_file_name ) }
+ let(:smb_share_name) { "chef_smb_test" }
+ let(:smb_remote_path) { File.join("//#{ENV['COMPUTERNAME']}", smb_share_name, smb_file_local_file_name).gsub(/\//, "\\") }
+ let(:smb_file_content) { "hellofun" }
+ let(:local_destination_path) { File.join(Dir.tmpdir, make_tmpname("chef_remote_file")) }
+ let(:windows_current_user) { ENV["USERNAME"] }
+ let(:windows_current_user_domain) { ENV["USERDOMAIN"] || ENV["COMPUTERNAME"] }
+ let(:windows_current_user_qualified) { "#{windows_current_user_domain}\\#{windows_current_user}" }
+
+ let(:remote_domain) { nil }
+ let(:remote_user) { nil }
+ let(:remote_password) { nil }
+
+ let(:resource) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ resource = Chef::Resource::RemoteFile.new(path, run_context)
+ end
+
+ before do
+ shell_out("net.exe share #{smb_share_name} /delete")
+ File.write(smb_file_local_path, smb_file_content )
+ shell_out!("net.exe share #{smb_share_name}=\"#{smb_share_root_directory.gsub(/\//, '\\')}\" /grant:\"authenticated users\",read")
+ end
+
+ after do
+ shell_out("net.exe share #{smb_share_name} /delete")
+ File.delete(smb_file_local_path) if File.exist?(smb_file_local_path)
+ File.delete(local_destination_path) if File.exist?(local_destination_path)
+ Dir.rmdir(smb_share_root_directory)
+ end
+
+ context "when configuring the Windows identity used to access the remote file" do
+ before do
+ resource.path(local_destination_path)
+ resource.source(smb_remote_path)
+ resource.remote_domain(remote_domain)
+ resource.remote_user(remote_user)
+ resource.remote_password(remote_password)
+ resource.node.default["platform_family"] = "windows"
+ allow_any_instance_of(Chef::Provider::RemoteFile::NetworkFile).to receive(:node).and_return({ "platform_family" => "windows" })
+ end
+
+ shared_examples_for "a remote_file resource accessing a remote file to which the specified user has access" do
+ it "has the same content as the original file" do
+ expect { resource.run_action(:create) }.not_to raise_error
+ expect(::File.read(local_destination_path).chomp).to eq smb_file_content
+ end
+ end
+
+ shared_examples_for "a remote_file resource accessing a remote file to which the specified user does not have access" do
+ it "causes an error to be raised" do
+ expect { resource.run_action(:create) }.to raise_error(Errno::EACCES)
+ end
+ end
+
+ shared_examples_for "a remote_file resource accessing a remote file with invalid user" do
+ it "causes an error to be raised" do
+ allow(Chef::Util::Windows::LogonSession).to receive(:validate_session_open!).and_return(true)
+ expect { resource.run_action(:create) }.to raise_error(Chef::Exceptions::Win32APIError)
+ end
+ end
+
+ context "when the file is accessible to non-admin users only as the current identity" do
+ before do
+ shell_out!("icacls #{smb_file_local_path} /grant:r \"authenticated users:(W)\" /grant \"#{windows_current_user_qualified}:(R)\" /inheritance:r")
+ end
+
+ context "when the resource is accessed using the current user's identity" do
+ let(:remote_user) { nil }
+ let(:remote_domain) { nil }
+ let(:remote_password) { nil }
+
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user has access"
+
+ describe "uses the ::Chef::Provider::RemoteFile::NetworkFile::TRANSFER_CHUNK_SIZE constant to chunk the file" do
+ let(:invalid_chunk_size) { -1 }
+ before do
+ stub_const("::Chef::Provider::RemoteFile::NetworkFile::TRANSFER_CHUNK_SIZE", invalid_chunk_size)
+ end
+
+ it "raises an ArgumentError when the chunk size is negative" do
+ expect(::Chef::Provider::RemoteFile::NetworkFile::TRANSFER_CHUNK_SIZE).to eq(invalid_chunk_size)
+ expect { resource.run_action(:create) }.to raise_error(ArgumentError)
+ end
+ end
+
+ context "when the file must be transferred in more than one chunk" do
+ before do
+ stub_const("::Chef::Provider::RemoteFile::NetworkFile::TRANSFER_CHUNK_SIZE", 3)
+ end
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user has access"
+ end
+ end
+
+ context "when the resource is accessed using an alternate user's identity with no access to the file" do
+ let (:windows_nonadmin_user) { "chefremfile1" }
+ let (:windows_nonadmin_user_password) { "j82ajfxK3;2Xe1" }
+ include_context "a non-admin Windows user"
+
+ before do
+ shell_out!("icacls #{smb_file_local_path} /grant:r \"authenticated users:(W)\" /deny \"#{windows_current_user_qualified}:(R)\" /inheritance:r")
+ end
+
+ let(:remote_user) { windows_nonadmin_user }
+ let(:remote_domain) { windows_nonadmin_user_domain }
+ let(:remote_password) { windows_nonadmin_user_password }
+
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user does not have access"
+ end
+ end
+
+ context "when the the file is only accessible as a specific alternate identity" do
+ let (:windows_nonadmin_user) { "chefremfile2" }
+ let (:windows_nonadmin_user_password) { "j82ajfxK3;2Xe2" }
+ include_context "a non-admin Windows user"
+
+ before do
+ shell_out!("icacls #{smb_file_local_path} /grant:r \"authenticated users:(W)\" /grant \"#{windows_current_user_qualified}:(R)\" /inheritance:r")
+ end
+
+ context "when the resource is accessed using the specific non-qualified alternate user identity with access" do
+ let(:remote_user) { windows_nonadmin_user }
+ let(:remote_domain) { "." }
+ let(:remote_password) { windows_nonadmin_user_password }
+
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user has access"
+ end
+
+ context "when the resource is accessed using the specific alternate user identity with access and the domain is specified" do
+ let(:remote_user) { windows_nonadmin_user }
+ let(:remote_domain) { windows_nonadmin_user_domain }
+ let(:remote_password) { windows_nonadmin_user_password }
+
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user has access"
+ end
+
+ context "when the resource is accessed using the current user's identity" do
+ before do
+ shell_out!("icacls #{smb_file_local_path} /grant:r \"authenticated users:(W)\" /grant \"#{windows_nonadmin_user_qualified}:(R)\" /deny #{windows_current_user_qualified}:(R) /inheritance:r")
+ end
+
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user does not have access"
+ end
+
+ context "when the resource is accessed using an alternate user's identity with no access to the file" do
+ let (:windows_nonadmin_user) { "chefremfile3" }
+ let (:windows_nonadmin_user_password) { "j82ajfxK3;2Xe3" }
+ include_context "a non-admin Windows user"
+
+ let(:remote_user) { windows_nonadmin_user_qualified }
+ let(:remote_domain) { nil }
+ let(:remote_password) { windows_nonadmin_user_password }
+
+ before do
+ allow_any_instance_of(Chef::Util::Windows::LogonSession).to receive(:validate_session_open!).and_return(true)
+ end
+
+ it_behaves_like "a remote_file resource accessing a remote file with invalid user"
+ end
+ end
+ end
+ end
+ end
+
context "when dealing with content length checking" do
before(:all) do
- start_tiny_server
+ start_tiny_server(RequestTimeout: 1)
end
after(:all) do
@@ -232,7 +407,16 @@ describe Chef::Resource::RemoteFile do
end
it "should not create the file" do
- expect { resource.run_action(:create) }.to raise_error
+ # This can legitimately raise either Errno::EADDRNOTAVAIL or Errno::ECONNREFUSED
+ # in different Ruby versions.
+ old_value = RSpec::Expectations.configuration.on_potential_false_positives
+ RSpec::Expectations.configuration.on_potential_false_positives = :nothing
+ begin
+ expect { resource.run_action(:create) }.to raise_error
+ ensure
+ RSpec::Expectations.configuration.on_potential_false_positives = old_value
+ end
+
expect(File).not_to exist(path)
end
end
diff --git a/spec/functional/resource/rpm_spec.rb b/spec/functional/resource/rpm_spec.rb
index ce9332e4ed..17d0bf9e3c 100644
--- a/spec/functional/resource/rpm_spec.rb
+++ b/spec/functional/resource/rpm_spec.rb
@@ -21,7 +21,7 @@ require "functional/resource/base"
require "chef/mixin/shell_out"
# run this test only for following platforms.
-exclude_test = !%w{aix centos redhat suse}.include?(ohai[:platform])
+exclude_test = !%w{aix rhel fedora suse}.include?(ohai[:platform_family])
describe Chef::Resource::RpmPackage, :requires_root, :external => exclude_test do
include Chef::Mixin::ShellOut
@@ -32,37 +32,34 @@ describe Chef::Resource::RpmPackage, :requires_root, :external => exclude_test d
end
def rpm_pkg_should_be_installed(resource)
- case ohai[:platform]
# Due to dependency issues , different rpm pkgs are used in different platforms.
# dummy rpm package works in aix, without any dependency issues.
- when "aix"
+ if ohai[:platform] == "aix"
expect(shell_out("rpm -qa | grep dummy").exitstatus).to eq(0)
# mytest rpm package works in centos, redhat and in suse without any dependency issues.
- when "centos", "redhat", "suse"
+ else
expect(shell_out("rpm -qa | grep mytest").exitstatus).to eq(0)
::File.exists?("/opt/mytest/mytest.sh") # The mytest rpm package contains the mytest.sh file
end
end
def rpm_pkg_should_not_be_installed(resource)
- case ohai[:platform]
- when "aix"
+ if ohai[:platform] == "aix"
expect(shell_out("rpm -qa | grep dummy").exitstatus).to eq(1)
- when "centos", "redhat", "suse"
+ else
expect(shell_out("rpm -qa | grep mytest").exitstatus).to eq(1)
!::File.exists?("/opt/mytest/mytest.sh")
end
end
before(:all) do
- case ohai[:platform]
# Due to dependency issues , different rpm pkgs are used in different platforms.
- when "aix"
+ if ohai[:platform] == "aix"
@pkg_name = "dummy"
@pkg_version = "1-0"
@pkg_path = "#{Dir.tmpdir}/dummy-1-0.aix6.1.noarch.rpm"
FileUtils.cp(File.join(CHEF_SPEC_ASSETS, "dummy-1-0.aix6.1.noarch.rpm") , @pkg_path)
- when "centos", "redhat", "suse"
+ else
@pkg_name = "mytest"
@pkg_version = "1.0-1"
@pkg_path = "#{Dir.tmpdir}/mytest-1.0-1.noarch.rpm"
diff --git a/spec/functional/resource/template_spec.rb b/spec/functional/resource/template_spec.rb
index f270043f2c..b9a39255f4 100644
--- a/spec/functional/resource/template_spec.rb
+++ b/spec/functional/resource/template_spec.rb
@@ -32,6 +32,7 @@ describe Chef::Resource::Template do
let(:node) do
node = Chef::Node.new
node.normal[:slappiness] = "a warm gun"
+ node.normal[:nested][:secret] = "value"
node
end
@@ -110,7 +111,7 @@ describe Chef::Resource::Template do
context "using single helper syntax referencing @node" do
before do
- node.set[:helper_test_attr] = "value from helper method"
+ node.normal[:helper_test_attr] = "value from helper method"
resource.helper(:helper_method) { "#{@node[:helper_test_attr]}" }
end
@@ -131,7 +132,7 @@ describe Chef::Resource::Template do
context "using an inline block referencing @node" do
before do
- node.set[:helper_test_attr] = "value from helper method"
+ node.normal[:helper_test_attr] = "value from helper method"
resource.helpers do
def helper_method
@@ -168,7 +169,7 @@ describe Chef::Resource::Template do
end
before do
- node.set[:helper_test_attr] = "value from helper method"
+ node.normal[:helper_test_attr] = "value from helper method"
resource.helpers(ExampleModuleReferencingATNode)
end
@@ -209,4 +210,36 @@ describe Chef::Resource::Template do
end
end
+ describe "when template variables contain lazy{} calls" do
+ it "resolves the DelayedEvaluator" do
+ resource.source("openldap_variable_stuff.conf.erb")
+ resource.variables(:secret => Chef::DelayedEvaluator.new { "nutella" })
+ resource.run_action(:create)
+ expect(IO.read(path)).to eq("super secret is nutella")
+ end
+
+ it "does not mutate the resource variables" do
+ resource.source("openldap_variable_stuff.conf.erb")
+ resource.variables(:secret => Chef::DelayedEvaluator.new { "nutella" })
+ resource.run_action(:create)
+ expect(resource.variables[:secret]).to be_a Chef::DelayedEvaluator
+ end
+
+ it "resolves the DelayedEvaluator when deeply nested" do
+ resource.source("openldap_nested_variable_stuff.erb")
+ resource.variables(:secret => [{ "key" => Chef::DelayedEvaluator.new { "nutella" } }])
+ resource.run_action(:create)
+ expect(IO.read(path)).to eq("super secret is nutella")
+ end
+ end
+
+ describe "when passing a node attribute mash as a template variable" do
+ it "uses the node attributes like a hash" do
+ resource.source("openldap_variable_stuff.conf.erb")
+ resource.variables(node[:nested])
+ resource.run_action(:create)
+ expect(IO.read(path)).to eq("super secret is value")
+ end
+ end
+
end
diff --git a/spec/functional/resource/user/dscl_spec.rb b/spec/functional/resource/user/dscl_spec.rb
index 5d904a980b..ed96e31bac 100644
--- a/spec/functional/resource/user/dscl_spec.rb
+++ b/spec/functional/resource/user/dscl_spec.rb
@@ -28,11 +28,9 @@ describe "Chef::Resource::User with Chef::Provider::User::Dscl provider", metada
include Chef::Mixin::ShellOut
def clean_user
- begin
- shell_out!("/usr/bin/dscl . -delete '/Users/#{username}'")
- rescue Mixlib::ShellOut::ShellCommandFailed
+ shell_out!("/usr/bin/dscl . -delete '/Users/#{username}'")
+ rescue Mixlib::ShellOut::ShellCommandFailed
# Raised when the user is already cleaned
- end
end
def user_should_exist
@@ -76,7 +74,7 @@ describe "Chef::Resource::User with Chef::Provider::User::Dscl provider", metada
let(:iterations) { nil }
let(:user_resource) do
- r = Chef::Resource::User.new("TEST USER RESOURCE", run_context)
+ r = Chef::Resource::User::DsclUser.new("TEST USER RESOURCE", run_context)
r.username(username)
r.uid(uid)
r.gid(gid)
@@ -123,7 +121,7 @@ describe "Chef::Resource::User with Chef::Provider::User::Dscl provider", metada
end
describe "when password is being set via shadow hash" do
- let(:password) {
+ let(:password) do
if node[:platform_version].start_with?("10.7.")
# On Mac 10.7 we only need to set the password
"c9b3bd1a0cde797eef0eff16c580dab996ba3a21961cccc\
@@ -139,7 +137,7 @@ b1d4880833aa7a190afc13e2bf0936b8\
c5adbbac718b7eb99463a7b679571e0f\
1c9fef2ef08d0b9e9c2bcf644eed2ffc"
end
- }
+ end
let(:iterations) { 25000 }
let(:salt) { "9e2e7d5ee473b496fd24cf0bbfcaedfcb291ee21740e570d1e917e874f8788ca" }
diff --git a/spec/functional/resource/user/useradd_spec.rb b/spec/functional/resource/user/useradd_spec.rb
index 43c26ac006..175809b5c0 100644
--- a/spec/functional/resource/user/useradd_spec.rb
+++ b/spec/functional/resource/user/useradd_spec.rb
@@ -21,19 +21,26 @@ require "spec_helper"
require "functional/resource/base"
require "chef/mixin/shell_out"
-def user_provider_for_platform
- case ohai[:platform]
+def resource_for_platform(username, run_context)
+ Chef::Resource.resource_for_node(:user, node).new(username, run_context)
+end
+
+# ideally we could somehow pass an array of [ ...::Aix, ...::Linux ] to the
+# filter, but we have to pick the right one for the O/S.
+def user_provider_filter
+ case ohai[:os]
when "aix"
Chef::Provider::User::Aix
- else
- Chef::Provider::User::Useradd
+ when "linux"
+ Chef::Provider::User::Linux
end
end
-metadata = { :unix_only => true,
- :requires_root => true,
- :not_supported_on_mac_osx => true,
- :provider => { :user => user_provider_for_platform },
+metadata = {
+ :unix_only => true,
+ :requires_root => true,
+ :not_supported_on_mac_osx => true,
+ :provider => { :user => user_provider_filter },
}
describe Chef::Provider::User::Useradd, metadata do
@@ -81,12 +88,13 @@ describe Chef::Provider::User::Useradd, metadata do
end
def try_cleanup
- ["/home/cheftestfoo", "/home/cheftestbar"].each do |f|
+ ["/home/cheftestfoo", "/home/cheftestbar", "/home/cf-test"].each do |f|
FileUtils.rm_rf(f) if File.exists? f
end
["cf-test"].each do |u|
- r = Chef::Resource::User.new("DELETE USER", run_context)
+ r = resource_for_platform("DELETE USER", run_context)
+ r.manage_home true
r.username("cf-test")
r.run_action(:remove)
end
@@ -111,7 +119,7 @@ describe Chef::Provider::User::Useradd, metadata do
break if status.exitstatus != 8
sleep 1
- max_retries = max_retries - 1
+ max_retries -= 1
rescue UserNotFound
break
end
@@ -134,10 +142,7 @@ describe Chef::Provider::User::Useradd, metadata do
Chef::RunContext.new(node, {}, events)
end
- let(:username) do
- "cf-test"
- end
-
+ let(:username) { "cf-test" }
let(:uid) { nil }
let(:home) { nil }
let(:manage_home) { false }
@@ -146,7 +151,7 @@ describe Chef::Provider::User::Useradd, metadata do
let(:comment) { nil }
let(:user_resource) do
- r = Chef::Resource::User.new("TEST USER RESOURCE", run_context)
+ r = resource_for_platform("TEST USER RESOURCE", run_context)
r.username(username)
r.uid(uid)
r.home(home)
@@ -242,15 +247,8 @@ describe Chef::Provider::User::Useradd, metadata do
expect(pw_entry.home).to eq(home)
end
- if %w{rhel fedora wrlinux}.include?(OHAI_SYSTEM["platform_family"])
- # Inconsistent behavior. See: CHEF-2205
- it "creates the home dir when not explicitly asked to on RHEL (XXX)" do
- expect(File).to exist(home)
- end
- else
- it "does not create the home dir without `manage_home'" do
- expect(File).not_to exist(home)
- end
+ it "does not create the home dir without `manage_home'" do
+ expect(File).not_to exist(home)
end
context "and manage_home is enabled" do
@@ -260,6 +258,14 @@ describe Chef::Provider::User::Useradd, metadata do
expect(File).to exist(home)
end
end
+
+ context "and manage_home is the default" do
+ let(:manage_home) { nil }
+
+ it "does not create the home dir without `manage_home'" do
+ expect(File).not_to exist(home)
+ end
+ end
end
context "when a password is specified" do
@@ -310,8 +316,8 @@ describe Chef::Provider::User::Useradd, metadata do
let(:existing_comment) { nil }
let(:existing_user) do
- r = Chef::Resource::User.new("TEST USER RESOURCE", run_context)
- # username is identity attr, must match.
+ r = resource_for_platform("TEST USER RESOURCE", run_context)
+ # username is identity attr, must match.
r.username(username)
r.uid(existing_uid)
r.home(existing_home)
@@ -631,12 +637,23 @@ describe Chef::Provider::User::Useradd, metadata do
context "and has no password" do
# TODO: platform_family should be setup in spec_helper w/ tags
- if %w{suse opensuse}.include?(OHAI_SYSTEM["platform_family"])
- # suse gets this right:
+ if %w{opensuse}.include?(OHAI_SYSTEM["platform_family"]) ||
+ (%w{suse}.include?(OHAI_SYSTEM["platform_family"]) &&
+ OHAI_SYSTEM["platform_version"].to_f < 12.0)
+ # suse 11.x gets this right:
it "errors out trying to unlock the user" do
expect(@error).to be_a(Mixlib::ShellOut::ShellCommandFailed)
expect(@error.message).to include("Cannot unlock the password")
end
+ elsif %w{rhel}.include?(OHAI_SYSTEM["platform_family"]) &&
+ (Chef::VersionConstraint.new("~> 6.8").include?(OHAI_SYSTEM["platform_version"].to_f) || Chef::VersionConstraint.new("~> 7.3").include?(OHAI_SYSTEM["platform_version"].to_f))
+ # RHEL 6.8 and 7.3 ship with a fixed `usermod` command
+ # Reference: https://access.redhat.com/errata/RHBA-2016:0864
+ # Reference: https://access.redhat.com/errata/RHBA-2016:2322
+ it "errors out trying to unlock the user" do
+ expect(@error).to be_a(Mixlib::ShellOut::ShellCommandFailed)
+ expect(@error.message).to include("You should set a password")
+ end
else
# borked on all other platforms:
diff --git a/spec/functional/resource/user/windows_spec.rb b/spec/functional/resource/user/windows_spec.rb
index f61a51c636..56ae962ee4 100644
--- a/spec/functional/resource/user/windows_spec.rb
+++ b/spec/functional/resource/user/windows_spec.rb
@@ -31,6 +31,7 @@ describe Chef::Provider::User::Windows, :windows_only do
end
let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
let(:run_context) { Chef::RunContext.new(node, {}, events) }
let(:new_resource) do
Chef::Resource::User.new(username, run_context).tap do |r|
@@ -45,6 +46,7 @@ describe Chef::Provider::User::Windows, :windows_only do
before do
delete_user(username)
+ allow(run_context).to receive(:logger).and_return(logger)
end
describe "action :create" do
@@ -69,7 +71,7 @@ describe Chef::Provider::User::Windows, :windows_only do
context "with a gid specified" do
it "warns unsupported" do
- expect(Chef::Log).to receive(:warn).with(/not implemented/)
+ expect(logger).to receive(:warn).with(/not implemented/)
new_resource.gid("agroup")
new_resource.run_action(:create)
end
diff --git a/spec/functional/resource/windows_env_spec.rb b/spec/functional/resource/windows_env_spec.rb
new file mode 100644
index 0000000000..a6c6b39970
--- /dev/null
+++ b/spec/functional/resource/windows_env_spec.rb
@@ -0,0 +1,285 @@
+#
+# Author:: Adam Edwards (<adamed@chef.io>)
+# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsEnv, :windows_only do
+ context "when running on Windows" do
+ let(:chef_env_test_lower_case) { "chefenvtest" }
+ let(:chef_env_test_mixed_case) { "chefENVtest" }
+ let(:chef_env_with_delim) { "chef_env_with_delim" }
+ let(:chef_env_delim) { ";" }
+ let(:chef_env_test_delim) { "#{value1};#{value2}" }
+ let(:env_dne_key) { "env_dne_key" }
+ let(:env_value1) { "value1" }
+ let(:env_value2) { "value2" }
+ let(:delim_value) { "#{env_value1};#{env_value2}" }
+ let(:env_user) { ENV["USERNAME"].upcase }
+ let(:default_env_user) { "<SYSTEM>" }
+
+ let(:env_obj) do
+ wmi = WmiLite::Wmi.new
+ environment_variables = wmi.query("select * from Win32_Environment where name = '#{test_resource.key_name}'")
+ if environment_variables && environment_variables.length > 0
+ environment_variables.each do |env|
+ env_obj = env.wmi_ole_object
+ return env_obj if env_obj.username.split('\\').last.casecmp(test_resource.user) == 0
+ end
+ end
+ nil
+ end
+
+ let(:env_value_expandable) { "%SystemRoot%" }
+ let(:test_run_context) do
+ node = Chef::Node.new
+ node.default["os"] = "windows"
+ node.default["platform"] = "windows"
+ node.default["platform_version"] = "6.1"
+ empty_events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, empty_events)
+ end
+ let(:test_resource) do
+ Chef::Resource::WindowsEnv.new("unknown", test_run_context)
+ end
+
+ before(:each) do
+ resource_lower = Chef::Resource::WindowsEnv.new(chef_env_test_lower_case, test_run_context)
+ resource_lower.run_action(:delete)
+ resource_lower = Chef::Resource::WindowsEnv.new(chef_env_test_lower_case, test_run_context)
+ resource_lower.user(env_user)
+ resource_lower.run_action(:delete)
+ resource_mixed = Chef::Resource::WindowsEnv.new(chef_env_test_mixed_case, test_run_context)
+ resource_mixed.run_action(:delete)
+ resource_mixed = Chef::Resource::WindowsEnv.new(chef_env_test_mixed_case, test_run_context)
+ resource_lower.user(env_user)
+ resource_mixed.run_action(:delete)
+ end
+
+ context "when the create action is invoked" do
+ it "should create an environment variable for action create" do
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ test_resource.run_action(:create)
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ end
+
+ it "should create an environment variable with default user System for action create" do
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ test_resource.run_action(:create)
+ expect(env_obj.username.upcase).to eq(default_env_user)
+ end
+
+ it "should create an environment variable with user for action create" do
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ test_resource.user(env_user)
+ test_resource.run_action(:create)
+ expect(env_obj.username.split('\\').last.upcase).to eq(env_user)
+ end
+
+ context "when env variable exist with same name" do
+ before(:each) do
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ test_resource.run_action(:create)
+ end
+ it "should modify an existing variable's value to a new value" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.value(env_value2)
+ test_resource.run_action(:create)
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
+ end
+
+ it "should not modify an existing variable's value to a new value if the users are different" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.value(env_value2)
+ test_resource.user(env_user)
+ test_resource.run_action(:create)
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.user(default_env_user)
+ expect(env_obj.variablevalue).to eq(env_value1)
+ end
+
+ it "should modify an existing variable's value to a new value if the variable name case differs from the existing variable" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.key_name(chef_env_test_mixed_case)
+ test_resource.value(env_value2)
+ test_resource.run_action(:create)
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
+ end
+ end
+
+ it "should not expand environment variables if the variable is not PATH" do
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value_expandable)
+ test_resource.run_action(:create)
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value_expandable)
+ end
+ end
+
+ context "when the modify action is invoked" do
+ it "should raise an exception for modify if the variable doesn't exist" do
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ expect { test_resource.run_action(:modify) }.to raise_error(Chef::Exceptions::WindowsEnv)
+ end
+
+ context "when env variable exist with same name" do
+ before(:each) do
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ test_resource.run_action(:create)
+ end
+
+ it "should modify an existing variable's value to a new value" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.value(env_value2)
+ test_resource.run_action(:modify)
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
+ end
+
+ # This examlpe covers Chef Issue #1754
+ it "should modify an existing variable's value to a new value if the variable name case differs from the existing variable" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.key_name(chef_env_test_mixed_case)
+ test_resource.value(env_value2)
+ test_resource.run_action(:modify)
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
+ end
+
+ it "should not expand environment variables if the variable is not PATH" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.value(env_value_expandable)
+ test_resource.run_action(:modify)
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value_expandable)
+ end
+ end
+
+ context "when using PATH" do
+ let(:random_name) { Time.now.to_i }
+ let(:env_val) { "#{env_value_expandable}_#{random_name}" }
+ let!(:path_before) { test_resource.provider_for_action(test_resource.action).env_value("PATH") || "" }
+ let!(:env_path_before) { ENV["PATH"] }
+
+ it "should expand PATH" do
+ expect(path_before).not_to include(env_val)
+ test_resource.key_name("PATH")
+ test_resource.value("#{path_before};#{env_val}")
+ test_resource.run_action(:create)
+ expect(ENV["PATH"]).not_to include(env_val)
+ expect(ENV["PATH"]).to include("#{random_name}")
+ end
+
+ after(:each) do
+ # cleanup so we don't flood the path
+ test_resource.key_name("PATH")
+ test_resource.value(path_before)
+ test_resource.run_action(:create)
+ ENV["PATH"] = env_path_before
+ end
+ end
+
+ end
+
+ context "when the delete action is invoked" do
+ context "when env variable exist with same name" do
+ before(:each) do
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ test_resource.run_action(:create)
+ end
+
+ it "should delete a System environment variable" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.run_action(:delete)
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ end
+
+ it "should not delete an System environment variable if user are passed" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.user(env_user)
+ test_resource.run_action(:delete)
+ test_resource.user(default_env_user)
+ expect(env_obj).not_to be_nil
+ end
+ end
+
+ context "when env variable exist with same name" do
+ before(:each) do
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ test_resource.user(env_user)
+ test_resource.run_action(:create)
+ end
+
+ it "should delete a user environment variable" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.run_action(:delete)
+ expect(env_obj).to eq(nil)
+ end
+
+ it "should not delete an user environment variable if user is not passed" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.user(default_env_user)
+ test_resource.run_action(:delete)
+ test_resource.user(env_user)
+ expect(env_obj).not_to be_nil
+ end
+ end
+
+ context "when env variable exist with same name" do
+ before(:each) do
+ test_resource.key_name(chef_env_with_delim)
+ test_resource.delim(chef_env_delim)
+ test_resource.value(delim_value)
+ test_resource.run_action(:create)
+ end
+
+ it "should not delete variable when a delim present" do
+ expect(ENV[chef_env_with_delim]).to eq(delim_value)
+ test_resource.value(env_value1)
+ test_resource.run_action(:delete)
+ expect(ENV[chef_env_with_delim]).to eq(env_value2)
+ end
+ end
+
+ it "should not raise an exception when a non-existent environment variable is deleted" do
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ expect { test_resource.run_action(:delete) }.not_to raise_error
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ end
+
+ it "should delete a value from the current process even if it is not in the registry" do
+ expect(ENV[env_dne_key]).to eq(nil)
+ ENV[env_dne_key] = env_value1
+ test_resource.key_name(env_dne_key)
+ test_resource.run_action(:delete)
+ expect(ENV[env_dne_key]).to eq(nil)
+ end
+
+ end
+ end
+end
diff --git a/spec/functional/resource/windows_path_spec.rb b/spec/functional/resource/windows_path_spec.rb
new file mode 100644
index 0000000000..912abe6b24
--- /dev/null
+++ b/spec/functional/resource/windows_path_spec.rb
@@ -0,0 +1,64 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 2017 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsPath, :windows_only do
+ let(:path) { "test_path" }
+
+ before(:all) do
+ @old_path = ENV["PATH"].dup
+ end
+
+ after(:all) do
+ ENV["PATH"] = @old_path
+ end
+
+ subject do
+ new_resource = Chef::Resource::WindowsPath.new(path, run_context)
+ new_resource
+ end
+
+ describe "adding path" do
+ after { remove_path }
+
+ it "appends the user given path in the Environment variable Path" do
+ subject.run_action(:add)
+ expect(ENV["PATH"]).to include(path)
+ end
+ end
+
+ describe "removing path" do
+ before { add_path }
+
+ it "removes the user given path from the Environment variable Path" do
+ subject.run_action(:remove)
+ expect(ENV["PATH"]).not_to include(path)
+ end
+ end
+
+ def remove_path
+ new_resource = Chef::Resource::WindowsPath.new(path, run_context)
+ new_resource.run_action(:remove)
+ end
+
+ def add_path
+ new_resource = Chef::Resource::WindowsPath.new(path, run_context)
+ new_resource.run_action(:add)
+ end
+end
diff --git a/spec/functional/resource/windows_service_spec.rb b/spec/functional/resource/windows_service_spec.rb
index b4af1e9e6a..531f9e9250 100644
--- a/spec/functional/resource/windows_service_spec.rb
+++ b/spec/functional/resource/windows_service_spec.rb
@@ -27,19 +27,19 @@ describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_
let(:qualified_username) { "#{ENV['COMPUTERNAME']}\\#{username}" }
let(:password) { "1a2b3c4X!&narf" }
- let(:user_resource) {
- r = Chef::Resource::User.new(username, run_context)
+ let(:user_resource) do
+ r = Chef::Resource::User::WindowsUser.new(username, run_context)
r.username(username)
r.password(password)
r.comment("temp spec user")
r
- }
+ end
- let(:global_service_file_path) {
+ let(:global_service_file_path) do
"#{ENV['WINDIR']}\\temp\\#{File.basename(test_service[:service_file_path])}"
- }
+ end
- let(:service_params) {
+ let(:service_params) do
id = "#{$$}_#{rand(1000)}"
@@ -51,19 +51,19 @@ describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_
service_description: "Test service for running the windows_service functional spec.",
service_file_path: global_service_file_path,
} )
- }
+ end
- let(:manager) {
+ let(:manager) do
Chef::Application::WindowsServiceManager.new(service_params)
- }
+ end
- let(:service_resource) {
+ let(:service_resource) do
r = Chef::Resource::WindowsService.new(service_params[:service_name], run_context)
[:run_as_user, :run_as_password].each { |prop| r.send(prop, service_params[prop]) }
r
- }
+ end
- before {
+ before do
user_resource.run_action(:create)
# the service executable has to be outside the current user's home
@@ -81,13 +81,13 @@ describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_
file.run_action(:create)
manager.run(%w{--action install})
- }
+ end
- after {
+ after do
user_resource.run_action(:remove)
manager.run(%w{--action uninstall})
File.delete(global_service_file_path)
- }
+ end
describe "logon as a service" do
it "successfully runs a service as another user" do
diff --git a/spec/functional/resource/windows_task_spec.rb b/spec/functional/resource/windows_task_spec.rb
new file mode 100644
index 0000000000..621802bd44
--- /dev/null
+++ b/spec/functional/resource/windows_task_spec.rb
@@ -0,0 +1,1454 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/provider/windows_task"
+
+describe Chef::Resource::WindowsTask, :windows_only do
+ let(:task_name) { "chef-client" }
+ let(:new_resource) { Chef::Resource::WindowsTask.new(task_name) }
+ let(:windows_task_provider) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ Chef::Provider::WindowsTask.new(new_resource, run_context)
+ end
+
+ describe "action :create" do
+ after { delete_task }
+ context "when frequency_modifier are not passed" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ # Make sure MM/DD/YYYY is accepted
+ new_resource.start_day "09/20/2017"
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ it "creates a scheduled task to run every 1 hr starting on 09/20/2017" do
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ trigger_details = current_resource.task.trigger(0)
+ expect(trigger_details[:start_year]).to eq("2017")
+ expect(trigger_details[:start_month]).to eq("09")
+ expect(trigger_details[:start_day]).to eq("20")
+ expect(trigger_details[:minutes_interval]).to eq(60)
+ expect(trigger_details[:trigger_type]).to eq(1)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "frequency :minute" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :minute
+ new_resource.frequency_modifier 15
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "creates a scheduled task that runs after every 15 minutes" do
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:minutes_interval]).to eq(15)
+ expect(trigger_details[:trigger_type]).to eq(1)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "updates a scheduled task when frequency_modifier updated to 20" do
+ subject.run_action(:create)
+ current_resource = call_for_load_current_resource
+ trigger_details = current_resource.task.trigger(0)
+ expect(trigger_details[:minutes_interval]).to eq(15)
+ subject.frequency_modifier 20
+ subject.run_action(:create)
+ expect(subject).to be_updated_by_last_action
+ # #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:minutes_interval]).to eq(20)
+ expect(trigger_details[:trigger_type]).to eq(1)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ end
+ end
+
+ context "frequency :hourly" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :hourly
+ new_resource.frequency_modifier 3
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "creates a scheduled task that runs after every 3 hrs" do
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:minutes_interval]).to eq(180)
+ expect(trigger_details[:trigger_type]).to eq(1)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "updates a scheduled task to run every 5 hrs when frequency modifer updated to 5" do
+ subject.run_action(:create)
+ current_resource = call_for_load_current_resource
+ trigger_details = current_resource.task.trigger(0)
+ expect(trigger_details[:minutes_interval]).to eq(180)
+ # updating frequency modifer to 5 from 3
+ subject.frequency_modifier 5
+ subject.run_action(:create)
+ expect(subject).to be_updated_by_last_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:minutes_interval]).to eq(300)
+ expect(trigger_details[:trigger_type]).to eq(1)
+ end
+ end
+
+ context "frequency :daily" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :daily
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "creates a scheduled task to run daily" do
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(2)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days_interval]).to eq(1)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "frequency :monthly" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :monthly
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ context "with start_day and start_time" do
+ before do
+ subject.start_day "02/12/2018"
+ subject.start_time "05:15"
+ end
+
+ it "if day property is not set creates a scheduled task to run monthly on first day of the month" do
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(1)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates a scheduled task to run monthly on first, second and third day of the month" do
+ subject.day "1, 2, 3"
+ call_for_create_action
+ #loading current resource again to check new task is created and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(7)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "1, 2, 3"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates a scheduled task to run monthly on 1, 2, 3, 4, 8, 20, 21, 15, 28, 31 day of the month" do
+ subject.day "1, 2, 3, 4, 8, 20, 21, 15, 28, 31"
+ call_for_create_action
+ #loading current resource again to check new task is created and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(1209548943) #TODO:: windows_task_provider.send(:days_of_month)
+ expect(trigger_details[:type][:months]).to eq(4095) #windows_task_provider.send(:months_of_year)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "1, 2, 3, 4, 8, 20, 21, 15, 28, 31"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates a scheduled task to run monthly on Jan, Feb, Apr, Dec on 1st 2nd 3rd 4th 8th and 20th day of these months" do
+ subject.day "1, 2, 3, 4, 8, 20, 21, 30"
+ subject.months "Jan, Feb, May, Sep, Dec"
+ call_for_create_action
+ #loading current resource again to check new task is created and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(538443919) #TODO:windows_task_provider.send(:days_of_month)
+ expect(trigger_details[:type][:months]).to eq(2323) #windows_task_provider.send(:months_of_year)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "1, 2, 3, 4, 8, 20, 21, 30"
+ subject.months "Jan, Feb, May, Sep, Dec"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates a scheduled task to run monthly by giving day option with frequency_modifier" do
+ subject.frequency_modifier "First"
+ subject.day "Mon, Fri, Sun"
+ call_for_create_action
+ #loading current resource again to check new task is created and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(5)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days_of_week]).to eq(35)
+ expect(trigger_details[:type][:weeks_of_month]).to eq(1)
+ expect(trigger_details[:type][:months]).to eq(4095) #windows_task_provider.send(:months_of_year)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency_modifier "First"
+ subject.day "Mon, Fri, Sun"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "with frequency_modifier" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :monthly
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "raises argument error if frequency_modifier is 'first, second' and day is not provided." do
+ subject.frequency_modifier "first, second"
+ expect { subject.after_created }.to raise_error("Please select day on which you want to run the task e.g. 'Mon, Tue'. Multiple values must be seprated by comma.")
+ end
+
+ it "raises argument error if months is passed along with frequency_modifier" do
+ subject.frequency_modifier 3
+ subject.months "Jan, Mar"
+ expect { subject.after_created }.to raise_error("For frequency :monthly either use property months or frequency_modifier to set months.")
+ end
+
+ it "not raises any Argument error if frequency_modifier set as 'first, second, third' and day is provided" do
+ subject.frequency_modifier "first, second, third"
+ subject.day "Mon, Fri"
+ expect { subject.after_created }.not_to raise_error(ArgumentError)
+ end
+
+ it "not raises any Argument error if frequency_modifier 2 " do
+ subject.frequency_modifier 2
+ subject.day "Mon, Sun"
+ expect { subject.after_created }.not_to raise_error(ArgumentError)
+ end
+
+ it "raises argument error if frequency_modifier > 12" do
+ subject.frequency_modifier 13
+ expect { subject.after_created }.to raise_error("frequency_modifier value 13 is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST'.")
+ end
+
+ it "raises argument error if frequency_modifier < 1" do
+ subject.frequency_modifier 0
+ expect { subject.after_created }.to raise_error("frequency_modifier value 0 is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST'.")
+ end
+
+ it "creates scheduled task to run task monthly on Monday and Friday of first, second and thrid week of month" do
+ subject.frequency_modifier "first, second, third"
+ subject.day "Mon, Fri"
+ expect { subject.after_created }.not_to raise_error(ArgumentError)
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(5)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ expect(trigger_details[:type][:weeks_of_month]).to eq(7)
+ expect(trigger_details[:type][:days_of_week]).to eq(34)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency_modifier "first, second, third"
+ subject.day "Mon, Fri"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates scheduled task to run task monthly on every 6 months when frequency_modifier is 6 and to run on 1st and 2nd day of month" do
+ subject.frequency_modifier 6
+ subject.day "1, 2"
+ expect { subject.after_created }.not_to raise_error(ArgumentError)
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(trigger_details[:type][:months]).to eq(2080)
+ expect(trigger_details[:type][:days]).to eq(3)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency_modifier 6
+ subject.day "1, 2"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when day is set as last or lastday for frequency :monthly" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :monthly
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "creates scheduled task to run monthly to run last day of the month" do
+ subject.day "last"
+ expect { subject.after_created }.not_to raise_error(ArgumentError)
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ expect(trigger_details[:type][:days]).to eq(0)
+ expect(trigger_details[:run_on_last_day_of_month]).to eq(true)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "last"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "day property set as 'lastday' creates scheduled task to run monthly to run last day of the month" do
+ subject.day "lastday"
+ expect { subject.after_created }.not_to raise_error(ArgumentError)
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ expect(trigger_details[:type][:days]).to eq(0)
+ expect(trigger_details[:run_on_last_day_of_month]).to eq(true)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "lastday"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when frequency_modifier is set as last for frequency :monthly" do
+ it "creates scheduled task to run monthly on last week of the month" do
+ subject.frequency_modifier "last"
+ subject.day "Mon, Fri"
+ expect { subject.after_created }.not_to raise_error(ArgumentError)
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(5)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ expect(trigger_details[:type][:days_of_week]).to eq(34)
+ expect(trigger_details[:run_on_last_week_of_month]).to eq(true)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency_modifier "last"
+ subject.day "Mon, Fri"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when wild card (*) set as months" do
+ it "creates the scheduled task to run on 1st day of the all months" do
+ subject.months "*"
+ expect { subject.after_created }.not_to raise_error(ArgumentError)
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ expect(trigger_details[:type][:days]).to eq(1)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.months "*"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when wild card (*) set as day" do
+ it "raises argument error" do
+ subject.day "*"
+ expect { subject.after_created }.to raise_error("day wild card (*) is only valid with frequency :weekly")
+ end
+ end
+
+ context "Pass either start day or start time by passing day compulsory or only pass frequency_modifier" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :monthly
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "creates a scheduled task to run monthly on second day of the month" do
+ subject.day "2"
+ subject.start_day "03/07/2018"
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(2)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "2"
+ subject.start_day "03/07/2018"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates a scheduled task to run monthly on first, second and third day of the month" do
+ subject.day "1,2,3"
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(7)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "1,2,3"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates a scheduled task to run monthly on each wednesday of the month" do
+ subject.frequency_modifier "1"
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(1)
+ expect(trigger_details[:type][:months]).to eq(4095) #windows_task_provider.send(:months_of_year)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency_modifier "2"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates a scheduled task to run monthly on each wednesday of the month" do
+ subject.frequency_modifier "2"
+ subject.months = nil
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ #loading current resource
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(1)
+ expect(trigger_details[:type][:months]).to eq(2730) #windows_task_provider.send(:months_of_year)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency_modifier "2"
+ subject.months = nil
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ ## ToDO: Add functional specs to handle frequency monthly with frequency modifier set as 1-12
+ context "frequency :once" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :once
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ context "when start_time is not provided" do
+ it "raises argument error" do
+ expect { subject.after_created }.to raise_error("`start_time` needs to be provided with `frequency :once`")
+ end
+ end
+
+ context "when start_time is provided" do
+ it "creates the scheduled task to run once at 5pm" do
+ subject.start_time "17:00"
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(1)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect("#{trigger_details[:start_hour]}:#{trigger_details[:start_minute]}" ).to eq(subject.start_time)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.start_time "17:00"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ context "frequency :weekly" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :weekly
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "creates the scheduled task to run weekly" do
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(3)
+ expect(trigger_details[:type][:weeks_interval]).to eq(1)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ context "when wild card (*) is set as day" do
+ it "creates hte scheduled task for all days of week" do
+ subject.day "*"
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(3)
+ expect(trigger_details[:type][:weeks_interval]).to eq(1)
+ expect(trigger_details[:type][:days_of_week]).to eq(127)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "*"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when days are provided" do
+ it "creates the scheduled task to run on particular days" do
+ subject.day "Mon, Fri"
+ subject.frequency_modifier 2
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(3)
+ expect(trigger_details[:type][:weeks_interval]).to eq(2)
+ expect(trigger_details[:type][:days_of_week]).to eq(34)
+ end
+
+ it "updates the scheduled task to run on if frequency_modifier is updated" do
+ subject.day "sun"
+ subject.frequency_modifier 2
+ subject.run_action(:create)
+ current_resource = call_for_load_current_resource
+ trigger_details = current_resource.task.trigger(0)
+ expect(trigger_details[:type][:weeks_interval]).to eq(2)
+ expect(trigger_details[:type][:days_of_week]).to eq(1)
+ subject.day "Mon, Sun"
+ subject.frequency_modifier 3
+ # call for update
+ subject.run_action(:create)
+ expect(subject).to be_updated_by_last_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(3)
+ expect(trigger_details[:type][:weeks_interval]).to eq(3)
+ expect(trigger_details[:type][:days_of_week]).to eq(3)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "Mon, Fri"
+ subject.frequency_modifier 3
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when day property set as last" do
+ it "raises argument error" do
+ subject.day "last"
+ expect { subject.after_created }.to raise_error("day values 1-31 or last is only valid with frequency :monthly")
+ end
+ end
+
+ context "when invalid day is passed" do
+ it "raises error" do
+ subject.day "abc"
+ expect { subject.after_created }.to raise_error("day property invalid. Only valid values are: MON, TUE, WED, THU, FRI, SAT, SUN, *. Multiple values must be separated by a comma.")
+ end
+ end
+
+ context "when months are passed" do
+ it "raises error that months are supported only when frequency=:monthly" do
+ subject.months "Jan"
+ expect { subject.after_created }.to raise_error("months property is only valid for tasks that run monthly")
+ end
+ end
+
+ context "when start_day is not set" do
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "updates the day if start_day is not provided and user updates day property" do
+ skip "Unable to run this test case since start_day is current system date which can be different each time so can't verify the dynamic values"
+ subject.run_action(:create)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(trigger_details[:type][:days_of_week]).to eq(8)
+ subject.day "Sat"
+ subject.run_action(:create)
+ # #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(3)
+ expect(trigger_details[:type][:weeks_interval]).to eq(1)
+ expect(trigger_details[:type][:days_of_week]).to eq(64)
+ end
+ end
+ end
+
+ context "frequency :onstart" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :onstart
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "creates the scheduled task to run at system start up" do
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(8)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ context "when start_day and start_time is set" do
+ it "creates task to activate on '09/10/2018' at '15:00' when start_day = '09/10/2018' and start_time = '15:00' provided" do
+ subject.start_day "09/10/2018"
+ subject.start_time "15:00"
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(8)
+ expect(trigger_details[:start_year]).to eq("2018")
+ expect(trigger_details[:start_month]).to eq("09")
+ expect(trigger_details[:start_day]).to eq("10")
+ expect(trigger_details[:start_hour]).to eq("15")
+ expect(trigger_details[:start_minute]).to eq("00")
+ end
+ end
+ end
+
+ context "frequency :on_logon" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :on_logon
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "creates the scheduled task to on logon" do
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(9)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ context "when start_day and start_time is set" do
+ it "creates task to activate on '09/10/2018' at '15:00' when start_day = '09/10/2018' and start_time = '15:00' provided" do
+ subject.start_day "09/10/2018"
+ subject.start_time "15:00"
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(9)
+ expect(trigger_details[:start_year]).to eq("2018")
+ expect(trigger_details[:start_month]).to eq("09")
+ expect(trigger_details[:start_day]).to eq("10")
+ expect(trigger_details[:start_hour]).to eq("15")
+ expect(trigger_details[:start_minute]).to eq("00")
+ end
+ end
+ end
+
+ context "frequency :on_idle" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :on_idle
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ context "when idle_time is not passed" do
+ it "raises error" do
+ expect { subject.after_created }.to raise_error("idle_time value should be set for :on_idle frequency.")
+ end
+ end
+
+ context "when idle_time is passed" do
+ it "creates the scheduled task to run when system is idle" do
+ subject.idle_time 20
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(6)
+ expect(current_resource.task.settings[:idle_settings][:idle_duration]).to eq("PT20M")
+ expect(current_resource.task.settings[:run_only_if_idle]).to eq(true)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.idle_time 20
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when start_day and start_time is set" do
+ it "creates task to activate on '09/10/2018' at '15:00' when start_day = '09/10/2018' and start_time = '15:00' provided" do
+ subject.idle_time 20
+ subject.start_day "09/10/2018"
+ subject.start_time "15:00"
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(trigger_details[:trigger_type]).to eq(6)
+ expect(trigger_details[:start_year]).to eq("2018")
+ expect(trigger_details[:start_month]).to eq("09")
+ expect(trigger_details[:start_day]).to eq("10")
+ expect(trigger_details[:start_hour]).to eq("15")
+ expect(trigger_details[:start_minute]).to eq("00")
+ end
+ end
+ end
+
+ context "when random_delay is passed" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "sets the random_delay for frequency :minute" do
+ subject.frequency :minute
+ subject.random_delay "20"
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(1)
+ expect(trigger_details[:random_minutes_interval]).to eq(20)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency :minute
+ subject.random_delay "20"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "raises error if invalid random_delay is passed" do
+ subject.frequency :minute
+ subject.random_delay "abc"
+ expect { subject.after_created }.to raise_error("Invalid value passed for `random_delay`. Please pass seconds as an Integer (e.g. 60) or a String with numeric values only (e.g. '60').")
+ end
+
+ it "raises error if random_delay is passed with frequency on_idle" do
+ subject.frequency :on_idle
+ subject.random_delay "20"
+ expect { subject.after_created }.to raise_error("`random_delay` property is supported only for frequency :once, :minute, :hourly, :daily, :weekly and :monthly")
+ end
+ end
+
+ context "frequency :none" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :none
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "creates the scheduled task to run on demand only" do
+ call_for_create_action
+ #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+
+ expect(current_resource.task.application_name).to eq("chef-client")
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(current_resource.task.trigger_count).to eq(0)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ describe "Examples of idempotent checks for each frequency" do
+ after { delete_task }
+ context "For frequency :once" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :once
+ new_resource.start_time "17:00"
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "create task by adding frequency_modifier as 1" do
+ subject.frequency_modifier 1
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "create task by adding frequency_modifier as 5" do
+ subject.frequency_modifier 5
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "For frequency :none" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource.frequency :none
+ new_resource
+ end
+
+ it "create task by adding frequency_modifier as 1" do
+ subject.frequency_modifier 1
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "create task by adding frequency_modifier as 5" do
+ subject.frequency_modifier 5
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "For frequency :weekly" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :weekly
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "create task by adding start_day" do
+ subject.start_day "12/28/2018"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "create task by adding frequency_modifier and random_delay" do
+ subject.frequency_modifier 3
+ subject.random_delay "60"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "For frequency :monthly" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :once
+ new_resource.start_time "17:00"
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "create task by adding frequency_modifier as 1" do
+ subject.frequency_modifier 1
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "create task by adding frequency_modifier as 5" do
+ subject.frequency_modifier 5
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "For frequency :hourly" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :hourly
+ new_resource.frequency_modifier 5
+ new_resource.random_delay "2400"
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "create task by adding frequency_modifier and random_delay" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "For frequency :daily" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :daily
+ new_resource.frequency_modifier 2
+ new_resource.random_delay "2400"
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "create task by adding frequency_modifier and random_delay" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "For frequency :on_logon" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.frequency :on_logon
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "create task by adding frequency_modifier and random_delay" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "create task by adding frequency_modifier as 5" do
+ subject.frequency_modifier 5
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "For frequency :onstart" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :onstart
+ new_resource.frequency_modifier 20
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ it "create task by adding frequency_modifier as 20" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ describe "#after_created" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource
+ end
+
+ context "when start_day is passed with frequency :onstart" do
+ it "not raises error" do
+ subject.frequency :onstart
+ subject.start_day "09/20/2017"
+ expect { subject.after_created }.not_to raise_error
+ end
+ end
+
+ context "when a non-system user is passed without password" do
+ it "raises error" do
+ subject.user "Administrator"
+ subject.frequency :onstart
+ expect { subject.after_created }.to raise_error(%q{Cannot specify a user other than the system users without specifying a password!. Valid passwordless users: 'NT AUTHORITY\SYSTEM', 'SYSTEM', 'NT AUTHORITY\LOCALSERVICE', 'NT AUTHORITY\NETWORKSERVICE', 'BUILTIN\USERS', 'USERS'})
+ end
+ end
+
+ context "when interactive_enabled is passed for a System user without password" do
+ it "raises error" do
+ subject.interactive_enabled true
+ subject.frequency :onstart
+ expect { subject.after_created }.to raise_error("Please provide the password when attempting to set interactive/non-interactive.")
+ end
+ end
+
+ context "when frequency_modifier > 1439 is passed for frequency=:minute" do
+ it "raises error" do
+ subject.frequency_modifier 1450
+ subject.frequency :minute
+ expect { subject.after_created }.to raise_error("frequency_modifier value 1450 is invalid. Valid values for :minute frequency are 1 - 1439.")
+ end
+ end
+
+ context "when frequency_modifier > 23 is passed for frequency=:minute" do
+ it "raises error" do
+ subject.frequency_modifier 24
+ subject.frequency :hourly
+ expect { subject.after_created }.to raise_error("frequency_modifier value 24 is invalid. Valid values for :hourly frequency are 1 - 23.")
+ end
+ end
+
+ context "when frequency_modifier > 23 is passed for frequency=:minute" do
+ it "raises error" do
+ subject.frequency_modifier 366
+ subject.frequency :daily
+ expect { subject.after_created }.to raise_error("frequency_modifier value 366 is invalid. Valid values for :daily frequency are 1 - 365.")
+ end
+ end
+
+ context "when frequency_modifier > 52 is passed for frequency=:minute" do
+ it "raises error" do
+ subject.frequency_modifier 53
+ subject.frequency :weekly
+ expect { subject.after_created }.to raise_error("frequency_modifier value 53 is invalid. Valid values for :weekly frequency are 1 - 52.")
+ end
+ end
+
+ context "when invalid frequency_modifier is passed for :monthly frequency" do
+ it "raises error" do
+ subject.frequency :monthly
+ subject.frequency_modifier "13"
+ expect { subject.after_created }.to raise_error("frequency_modifier value 13 is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST'.")
+ end
+ end
+
+ context "when invalid frequency_modifier is passed for :monthly frequency" do
+ it "raises error" do
+ subject.frequency :monthly
+ subject.frequency_modifier "xyz"
+ expect { subject.after_created }.to raise_error("frequency_modifier value xyz is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST'.")
+ end
+ end
+
+ context "when invalid months are passed" do
+ it "raises error" do
+ subject.months "xyz"
+ subject.frequency :monthly
+ expect { subject.after_created }.to raise_error("months property invalid. Only valid values are: JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC, *. Multiple values must be separated by a comma.")
+ end
+ end
+
+ context "when idle_time > 999 is passed" do
+ it "raises error" do
+ subject.idle_time 1000
+ subject.frequency :on_idle
+ expect { subject.after_created }.to raise_error("idle_time value 1000 is invalid. Valid values for :on_idle frequency are 1 - 999.")
+ end
+ end
+
+ context "when idle_time is passed for frequency=:monthly" do
+ it "raises error" do
+ subject.idle_time 300
+ subject.frequency :monthly
+ expect { subject.after_created }.to raise_error("idle_time property is only valid for tasks that run on_idle")
+ end
+ end
+ end
+
+ describe "action :delete" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:delete)
+ subject.run_action(:delete)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:delete)
+ subject.run_action(:delete)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "action :run" do
+ after { delete_task }
+
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command "dir"
+ new_resource.run_level :highest
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ it "runs the existing task" do
+ subject.run_action(:create)
+ subject.run_action(:run)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.status).to eq("queued").or eq("running").or eq("ready") # queued or can be running
+ end
+ end
+
+ describe "action :end", :volatile do
+ after { delete_task }
+
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command "dir"
+ new_resource.run_level :highest
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since
+ new_resource
+ end
+
+ it "ends the running task" do
+ subject.run_action(:create)
+ subject.run_action(:run)
+ subject.run_action(:end)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.status).to eq("queued").or eq("ready") #queued or can be ready
+ end
+ end
+
+ describe "action :enable" do
+ after { delete_task }
+
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ it "enables the disabled task" do
+ subject.run_action(:create)
+ subject.run_action(:disable)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.status).to eq("not scheduled")
+ subject.run_action(:enable)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.status).to eq("ready")
+ end
+ end
+
+ describe "action :disable" do
+ after { delete_task }
+
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ it "disables the task" do
+ subject.run_action(:create)
+ subject.run_action(:disable)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.status).to eq("not scheduled")
+ end
+ end
+
+ describe "action :change" do
+ after { delete_task }
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ it "call action_create since change action is alias for create" do
+ subject.run_action(:change)
+ expect(subject).to be_updated_by_last_action
+ end
+ end
+
+ def delete_task
+ task_to_delete = Chef::Resource::WindowsTask.new(task_name, run_context)
+ task_to_delete.run_action(:delete)
+ end
+
+ def call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(false)
+ subject.run_action(:create)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ def call_for_load_current_resource
+ windows_task_provider.send(:load_current_resource)
+ end
+end
diff --git a/spec/functional/resource/yum_package_spec.rb b/spec/functional/resource/yum_package_spec.rb
new file mode 100644
index 0000000000..7c45a64ae5
--- /dev/null
+++ b/spec/functional/resource/yum_package_spec.rb
@@ -0,0 +1,957 @@
+#
+# Copyright:: Copyright 2016-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "functional/resource/base"
+require "chef/mixin/shell_out"
+
+# run this test only for following platforms.
+exclude_test = !(%w{rhel fedora}.include?(ohai[:platform_family]) && !File.exist?("/usr/bin/dnf"))
+describe Chef::Resource::YumPackage, :requires_root, :external => exclude_test do
+ include Chef::Mixin::ShellOut
+
+ # NOTE: every single test here either needs to explicitly call flush_cache or needs to explicitly
+ # call preinstall (which explicitly calls flush_cache). It is your responsibility to do one or the
+ # other in order to minimize calling flush_cache a half dozen times per test.
+
+ def flush_cache
+ Chef::Resource::YumPackage.new("shouldnt-matter", run_context).run_action(:flush_cache)
+ end
+
+ def preinstall(*rpms)
+ rpms.each do |rpm|
+ shell_out!("rpm -ivh #{CHEF_SPEC_ASSETS}/yumrepo/#{rpm}")
+ end
+ flush_cache
+ end
+
+ before(:all) do
+ shell_out!("yum -y install yum-utils")
+ end
+
+ before(:each) do
+ File.open("/etc/yum.repos.d/chef-yum-localtesting.repo", "w+") do |f|
+ f.write <<-EOF
+[chef-yum-localtesting]
+name=Chef DNF spec testing repo
+baseurl=file://#{CHEF_SPEC_ASSETS}/yumrepo
+enable=1
+gpgcheck=0
+ EOF
+ end
+ shell_out!("rpm -qa --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' | grep chef_rpm | xargs -r rpm -e")
+ # next line is useful cleanup if you happen to have been testing both yum + dnf func tests on the same box and
+ # have some dnf garbage left around
+ FileUtils.rm_f "/etc/yum.repos.d/chef-dnf-localtesting.repo"
+ end
+
+ after(:all) do
+ shell_out!("rpm -qa --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' | grep chef_rpm | xargs -r rpm -e")
+ FileUtils.rm_f "/etc/yum.repos.d/chef-yum-localtesting.repo"
+ end
+
+ let(:package_name) { "chef_rpm" }
+ let(:yum_package) do
+ r = Chef::Resource::YumPackage.new(package_name, run_context)
+ r.options("--nogpgcheck")
+ r
+ end
+
+ def pkg_arch
+ ohai[:kernel][:machine]
+ end
+
+ describe ":install" do
+ context "vanilla use case" do
+ let(:package_name) { "chef_rpm" }
+
+ it "installs if the package is not installed" do
+ flush_cache
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "does not install if the package is installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "does not install twice" do
+ flush_cache
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "does not install if the prior version package is installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "does not install if the i686 package is installed", :intel_64bit do
+ skip "FIXME: do nothing, or install the #{pkg_arch} version?"
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ end
+
+ it "does not install if the prior version i686 package is installed", :intel_64bit do
+ skip "FIXME: do nothing, or install the #{pkg_arch} version?"
+ preinstall("chef_rpm-1.2-1.i686.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.i686$")
+ end
+ end
+
+ context "with versions or globs in the name" do
+ it "works with a version" do
+ flush_cache
+ yum_package.package_name("chef_rpm-1.10")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "works with an older version" do
+ flush_cache
+ yum_package.package_name("chef_rpm-1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "works with an evra" do
+ flush_cache
+ yum_package.package_name("chef_rpm-0:1.2-1.#{pkg_arch}")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "works with version and release" do
+ flush_cache
+ yum_package.package_name("chef_rpm-1.2-1")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "works with a version glob" do
+ flush_cache
+ yum_package.package_name("chef_rpm-1*")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "works with a name glob + version glob" do
+ flush_cache
+ yum_package.package_name("chef_rp*-1*")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "upgrades when the installed version does not match the version string" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm-1.10")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}")
+ end
+
+ it "downgrades when the installed version is higher than the package_name version" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade true
+ yum_package.package_name("chef_rpm-1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ # version only matches the actual yum version, does not work with epoch or release or combined evr
+ context "with version property" do
+ it "matches the full version" do
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.version("1.10")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "matches with a glob" do
+ # we are unlikely to ever fix this. if you've found this comment you should use e.g. "tcpdump-4*" in
+ # the name field rather than trying to use a name of "tcpdump" and a version of "4*".
+ pending "this does not work, is not easily supported by the underlying yum libraries, but does work in the new dnf_package provider"
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.version("1*")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "matches the vr" do
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.version("1.10-1")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "matches the evr" do
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.version("0:1.10-1")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "matches with a vr glob" do
+ pending "doesn't work on command line either"
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.version("1.10-1*")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "matches with an evr glob" do
+ pending "doesn't work on command line either"
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.version("0:1.10-1*")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+ end
+
+ context "downgrades" do
+ it "downgrades the package when allow_downgrade" do
+ flush_cache
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm")
+ yum_package.allow_downgrade true
+ yum_package.version("1.2-1")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ context "with arches", :intel_64bit do
+ it "installs with 64-bit arch in the name" do
+ flush_cache
+ yum_package.package_name("chef_rpm.#{pkg_arch}")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "installs with 32-bit arch in the name" do
+ flush_cache
+ yum_package.package_name("chef_rpm.i686")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ end
+
+ it "installs with 64-bit arch in the property" do
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.arch("#{pkg_arch}")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "installs with 32-bit arch in the property" do
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.arch("i686")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ end
+ end
+
+ context "with constraints" do
+ it "with nothing installed, it installs the latest version", not_rhel5: true do
+ flush_cache
+ yum_package.package_name("chef_rpm >= 1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "when it is met, it does nothing", not_rhel5: true do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm >= 1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "when it is met, it does nothing", not_rhel5: true do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm >= 1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with nothing intalled, it installs the latest version", not_rhel5: true do
+ flush_cache
+ yum_package.package_name("chef_rpm > 1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "when it is not met by an installed rpm, it upgrades", not_rhel5: true do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm > 1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with an equality constraint, when it is not met by an installed rpm, it upgrades", not_rhel5: true do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm = 1.10")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with an equality constraint, when it is met by an installed rpm, it does nothing", not_rhel5: true do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm = 1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "when it is met by an installed rpm, it does nothing", not_rhel5: true do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm > 1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "when there is no solution to the contraint", not_rhel5: true do
+ flush_cache
+ yum_package.package_name("chef_rpm > 2.0")
+ expect { yum_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+
+ it "when there is no solution to the contraint but an rpm is preinstalled", not_rhel5: true do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm > 2.0")
+ expect { yum_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+
+ it "with a less than constraint, when nothing is installed, it installs", not_rhel5: true do
+ flush_cache
+ yum_package.allow_downgrade true
+ yum_package.package_name("chef_rpm < 1.10")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "with a less than constraint, when the install version matches, it does nothing", not_rhel5: true do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade true
+ yum_package.package_name("chef_rpm < 1.10")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "with a less than constraint, when the install version fails, it should downgrade", not_rhel5: true do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade true
+ yum_package.package_name("chef_rpm < 1.10")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ context "with source arguments" do
+ it "raises an exception when the package does not exist" do
+ flush_cache
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/this-file-better-not-exist.rpm")
+ expect { yum_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+
+ it "does not raise a hard exception in why-run mode when the package does not exist" do
+ Chef::Config[:why_run] = true
+ flush_cache
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/this-file-better-not-exist.rpm")
+ yum_package.run_action(:install)
+ expect { yum_package.run_action(:install) }.not_to raise_error
+ end
+
+ it "installs the package when using the source argument" do
+ flush_cache
+ yum_package.name "something"
+ yum_package.package_name "somethingelse"
+ yum_package.source("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "installs the package when the name is a path to a file" do
+ flush_cache
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "downgrade on a local file raises an error", not_rhel5: true do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.version "1.2-1"
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ expect { yum_package.run_action(:install) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
+ end
+
+ it "downgrade on a local file with allow_downgrade true works" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.version "1.2-1"
+ yum_package.allow_downgrade true
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "does not downgrade the package with :install" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "does not upgrade the package with :install" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "is idempotent when the package is already installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ context "with no available version" do
+ it "works when a package is installed" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-yum-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "works with a local source" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-yum-localtesting.repo"
+ flush_cache
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ context "multipackage with arches", :intel_64bit do
+ it "installs two rpms" do
+ flush_cache
+ yum_package.package_name([ "chef_rpm.#{pkg_arch}", "chef_rpm.i686" ] )
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ end
+
+ it "does nothing if both are installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm", "chef_rpm-1.10-1.i686.rpm")
+ flush_cache
+ yum_package.package_name([ "chef_rpm.#{pkg_arch}", "chef_rpm.i686" ] )
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ end
+
+ it "installs the second rpm if the first is installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name([ "chef_rpm.#{pkg_arch}", "chef_rpm.i686" ] )
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ end
+
+ it "installs the first rpm if the second is installed" do
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ yum_package.package_name([ "chef_rpm.#{pkg_arch}", "chef_rpm.i686" ] )
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "installs two rpms with multi-arch" do
+ flush_cache
+ yum_package.package_name(%w{chef_rpm chef_rpm} )
+ yum_package.arch([pkg_arch, "i686"])
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "installs the second rpm if the first is installed (muti-arch)" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name(%w{chef_rpm chef_rpm} )
+ yum_package.arch([pkg_arch, "i686"])
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "installs the first rpm if the second is installed (muti-arch)" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name(%w{chef_rpm chef_rpm} )
+ yum_package.arch([pkg_arch, "i686"])
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "does nothing if both are installed (muti-arch)" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm", "chef_rpm-1.10-1.i686.rpm")
+ yum_package.package_name(%w{chef_rpm chef_rpm} )
+ yum_package.arch([pkg_arch, "i686"])
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "repo controls" do
+ it "should fail with the repo disabled" do
+ flush_cache
+ yum_package.options("--disablerepo=chef-yum-localtesting")
+ expect { yum_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+
+ it "should work with disablerepo first" do
+ flush_cache
+ yum_package.options(["--disablerepo=*", "--enablerepo=chef-yum-localtesting"])
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "should work to enable a disabled repo", not_rhel5: true do
+ shell_out!("yum-config-manager --disable chef-yum-localtesting")
+ flush_cache
+ expect { yum_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ flush_cache
+ yum_package.options("--enablerepo=chef-yum-localtesting")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "when an idempotent install action is run, does not leave repos disabled" do
+ flush_cache
+ # this is a bit tricky -- we need this action to be idempotent, so that it doesn't recycle any
+ # caches, but need it to hit whatavailable with the repo disabled. using :upgrade like this
+ # accomplishes both those goals (it would be easier if we had other rpms in this repo, but with
+ # one rpm we neeed to do this).
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.options("--disablerepo=chef-yum-localtesting")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ # now we're still using the same cache in the yum_helper.py cache and we test to see if the
+ # repo that we temporarily disabled is enabled on this pass.
+ yum_package.package_name("chef_rpm-1.10-1.#{pkg_arch}")
+ yum_package.options(nil)
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+ end
+ end
+
+ describe ":upgrade" do
+
+ context "with source arguments" do
+ it "installs the package when using the source argument" do
+ flush_cache
+ yum_package.name "something"
+ yum_package.package_name "somethingelse"
+ yum_package.source("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "installs the package when the name is a path to a file" do
+ flush_cache
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "downgrades the package when allow_downgrade is true" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade true
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "upgrades the package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "is idempotent when the package is already installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ context "with no available version" do
+ it "works when a package is installed" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-yum-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "works with a local source" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-yum-localtesting.repo"
+ flush_cache
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ context "version pinning" do
+ it "with an equality pin in the name it upgrades a prior package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm-1.10")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with a prco equality pin in the name it upgrades a prior package", not_rhel5: true do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm == 1.10")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with an equality pin in the name it downgrades a later package" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade true
+ yum_package.package_name("chef_rpm-1.2")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "with a prco equality pin in the name it downgrades a later package", not_rhel5: true do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade true
+ yum_package.package_name("chef_rpm == 1.2")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "with a > pin in the name and no rpm installed it installs", not_rhel5: true do
+ flush_cache
+ yum_package.package_name("chef_rpm > 1.2")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with a < pin in the name and no rpm installed it installs", not_rhel5: true do
+ flush_cache
+ yum_package.package_name("chef_rpm < 1.10")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "with a > pin in the name and matching rpm installed it does nothing", not_rhel5: true do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm > 1.2")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with a < pin in the name and no rpm installed it installs", not_rhel5: true do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm < 1.10")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "with a > pin in the name and non-matching rpm installed it upgrades", not_rhel5: true do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm > 1.2")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with a < pin in the name and non-matching rpm installed it downgrades", not_rhel5: true do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade true
+ yum_package.package_name("chef_rpm < 1.10")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+ end
+
+ describe ":remove" do
+ context "vanilla use case" do
+ let(:package_name) { "chef_rpm" }
+ it "does nothing if the package is not installed" do
+ flush_cache
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the package is installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "does not remove the package twice" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the prior version package is installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the i686 package is installed", :intel_64bit do
+ skip "FIXME: should this be fixed or is the current behavior correct?"
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the prior version i686 package is installed", :intel_64bit do
+ skip "FIXME: should this be fixed or is the current behavior correct?"
+ preinstall("chef_rpm-1.2-1.i686.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+ end
+
+ context "with 64-bit arch", :intel_64bit do
+ let(:package_name) { "chef_rpm.#{pkg_arch}" }
+ it "does nothing if the package is not installed" do
+ flush_cache
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the package is installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the prior version package is installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "does nothing if the i686 package is installed" do
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ end
+
+ it "does nothing if the prior version i686 package is installed" do
+ preinstall("chef_rpm-1.2-1.i686.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.i686$")
+ end
+ end
+
+ context "with 32-bit arch", :intel_64bit do
+ let(:package_name) { "chef_rpm.i686" }
+ it "removes only the 32-bit arch if both are installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm", "chef_rpm-1.10-1.i686.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+ end
+
+ context "with no available version" do
+ it "works when a package is installed" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-yum-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+ end
+ end
+
+ describe ":lock and :unlock" do
+ before(:all) do
+ shell_out!("yum -y install yum-versionlock")
+ end
+
+ before(:each) do
+ shell_out("yum versionlock delete '*:chef_rpm-*'") # will exit with error when nothing is locked, we don't care
+ end
+
+ it "locks an rpm" do
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.run_action(:lock)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("yum versionlock list").stdout.chomp).to match("^0:chef_rpm-")
+ end
+
+ it "does not lock if its already locked" do
+ flush_cache
+ shell_out!("yum versionlock add chef_rpm")
+ yum_package.package_name("chef_rpm")
+ yum_package.run_action(:lock)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("yum versionlock list").stdout.chomp).to match("^0:chef_rpm-")
+ end
+
+ it "unlocks an rpm" do
+ flush_cache
+ shell_out!("yum versionlock add chef_rpm")
+ yum_package.package_name("chef_rpm")
+ yum_package.run_action(:unlock)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("yum versionlock list").stdout.chomp).not_to match("^0:chef_rpm-")
+ end
+
+ it "does not unlock an already locked rpm" do
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.run_action(:unlock)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("yum versionlock list").stdout.chomp).not_to match("^0:chef_rpm-")
+ end
+
+ it "check that we can lock based on provides" do
+ flush_cache
+ yum_package.package_name("chef_rpm_provides")
+ yum_package.run_action(:lock)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("yum versionlock list").stdout.chomp).to match("^0:chef_rpm-")
+ end
+
+ it "check that we can unlock based on provides" do
+ flush_cache
+ shell_out!("yum versionlock add chef_rpm")
+ yum_package.package_name("chef_rpm_provides")
+ yum_package.run_action(:unlock)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("yum versionlock list").stdout.chomp).not_to match("^0:chef_rpm-")
+ end
+ end
+end
diff --git a/spec/functional/rest_spec.rb b/spec/functional/rest_spec.rb
deleted file mode 100644
index adafc18e5a..0000000000
--- a/spec/functional/rest_spec.rb
+++ /dev/null
@@ -1,95 +0,0 @@
-#
-# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-require "tiny_server"
-require "support/shared/functional/http"
-
-describe Chef::REST do
- include ChefHTTPShared
-
- let(:http_client) { described_class.new(source) }
- let(:http_client_disable_gzip) { described_class.new(source, Chef::Config[:node_name], Chef::Config[:client_key], { :disable_gzip => true } ) }
-
- shared_examples_for "downloads requests correctly" do
- it "successfully downloads a streaming request" do
- tempfile = http_client.streaming_request(source, {})
- tempfile.close
- expect(Digest::MD5.hexdigest(binread(tempfile.path))).to eq(Digest::MD5.hexdigest(expected_content))
- end
-
- it "successfully downloads a GET request" do
- tempfile = http_client.get(source, {})
- tempfile.close
- expect(Digest::MD5.hexdigest(binread(tempfile.path))).to eq(Digest::MD5.hexdigest(expected_content))
- end
- end
-
- shared_examples_for "validates content length and throws an exception" do
- it "fails validation on a streaming download" do
- expect { http_client.streaming_request(source, {}) }.to raise_error(Chef::Exceptions::ContentLengthMismatch)
- end
-
- it "fails validation on a GET request" do
- expect { http_client.get(source, {}) }.to raise_error(Chef::Exceptions::ContentLengthMismatch)
- end
- end
-
- shared_examples_for "an endpoint that 403s" do
- it "fails with a Net::HTTPServerException on a streaming download" do
- expect { http_client.streaming_request(source, {}) }.to raise_error(Net::HTTPServerException)
- end
-
- it "fails with a Net::HTTPServerException on a GET request" do
- expect { http_client.get(source, {}) }.to raise_error(Net::HTTPServerException)
- end
- end
-
- # see CHEF-5100
- shared_examples_for "a 403 after a successful request when reusing the request object" do
- it "fails with a Net::HTTPServerException on a streaming download" do
- tempfile = http_client.streaming_request(source, {})
- tempfile.close
- expect(Digest::MD5.hexdigest(binread(tempfile.path))).to eq(Digest::MD5.hexdigest(expected_content))
- expect { http_client.streaming_request(source2, {}) }.to raise_error(Net::HTTPServerException)
- end
-
- it "fails with a Net::HTTPServerException on a GET request" do
- tempfile = http_client.get(source, {})
- tempfile.close
- expect(Digest::MD5.hexdigest(binread(tempfile.path))).to eq(Digest::MD5.hexdigest(expected_content))
- expect { http_client.get(source2, {}) }.to raise_error(Net::HTTPServerException)
- end
- end
-
- before do
- Chef::Config[:node_name] = "webmonkey.example.com"
- Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem"
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- end
-
- before(:all) do
- start_tiny_server
- end
-
- after(:all) do
- stop_tiny_server
- end
-
- it_behaves_like "downloading all the things"
-end
diff --git a/spec/functional/root_alias_spec.rb b/spec/functional/root_alias_spec.rb
new file mode 100644
index 0000000000..a0a63011b4
--- /dev/null
+++ b/spec/functional/root_alias_spec.rb
@@ -0,0 +1,78 @@
+#
+# Copyright:: Copyright 2017, Noah Kantrowitz
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe "root aliases" do
+ let(:chef_repo_path) { File.expand_path(File.join(CHEF_SPEC_DATA, "root_alias_cookbooks")) }
+ let(:cookbook_collection) do
+ cl = Chef::CookbookLoader.new(chef_repo_path)
+ cl.load_cookbooks
+ Chef::CookbookCollection.new(cl)
+ end
+ let(:node) do
+ node = Chef::Node.new
+ node.automatic[:recipes] = []
+ node
+ end
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) }
+ before do
+ node.run_context = run_context
+ end
+
+ describe "attributes root aliases" do
+ it "should load attributes.rb when included directly" do
+ node.include_attribute("simple")
+ expect(node["aliased"]["attr"]).to eq "value"
+ end
+
+ it "should load attributes.rb when loading a cookbook" do
+ node.run_list << "simple"
+ run_context.load(node.run_list.expand("_default"))
+ expect(node["aliased"]["attr"]).to eq "value"
+ end
+
+ context "with both an attributes.rb and attributes/default.rb" do
+ it "should log an error and ignore attributes/default.rb" do
+ expect_any_instance_of(Mixlib::Log::Child).to receive(:error).with("Cookbook dup_attr contains both attributes.rb and and attributes/default.rb, ignoring attributes/default.rb")
+ node.run_list << "dup_attr"
+ run_context.load(node.run_list.expand("_default"))
+ expect(node["aliased"]["attr"]).to eq "value"
+ end
+ end
+ end
+
+ describe "recipe root aliased" do
+ it "should load recipe.rb" do
+ node.run_list << "simple"
+ run_context.load(node.run_list.expand("_default"))
+ run_context.include_recipe("simple")
+ expect(run_context.resource_collection.map(&:to_s)).to eq ["ruby_block[root alias]"]
+ end
+
+ context "with both an recipe.rb and recipes/default.rb" do
+ it "should log an error and ignore recipes/default.rb" do
+ expect(Chef::Log).to receive(:error).with("Cookbook dup_recipe contains both recipe.rb and and recipes/default.rb, ignoring recipes/default.rb")
+ node.run_list << "dup_recipe"
+ run_context.load(node.run_list.expand("_default"))
+ run_context.include_recipe("dup_recipe")
+ expect(run_context.resource_collection.map(&:to_s)).to eq ["ruby_block[root alias]"]
+ end
+ end
+ end
+end
diff --git a/spec/functional/run_lock_spec.rb b/spec/functional/run_lock_spec.rb
index f1e480c917..d270803698 100644
--- a/spec/functional/run_lock_spec.rb
+++ b/spec/functional/run_lock_spec.rb
@@ -46,11 +46,9 @@ describe Chef::RunLock do
end
WAIT_ON_LOCK_TIME = 1.0
- def wait_on_lock
+ def wait_on_lock(from_fork)
Timeout.timeout(WAIT_ON_LOCK_TIME) do
- until File.exist?(lockfile)
- sleep 0.1
- end
+ from_fork.readline
end
rescue Timeout::Error
raise "Lockfile never created, abandoning test"
@@ -258,37 +256,48 @@ describe Chef::RunLock do
it "test returns true and acquires the lock" do
run_lock = Chef::RunLock.new(lockfile)
+ from_tests, to_fork = IO.pipe
+ from_fork, to_tests = IO.pipe
p1 = fork do
expect(run_lock.test).to eq(true)
- run_lock.save_pid
- sleep 2
- exit! 1
+ to_tests.puts "lock acquired"
+ # Wait for the test to tell us we can exit before exiting
+ from_tests.readline
+ exit! 0
end
- wait_on_lock
+ wait_on_lock(from_fork)
p2 = fork do
expect(run_lock.test).to eq(false)
exit! 0
end
- Process.waitpid2(p2)
- Process.waitpid2(p1)
+ pid, exit_status = Process.waitpid2(p2)
+ expect(exit_status).to eq(0)
+ to_fork.puts "you can exit now"
+ pid, exit_status = Process.waitpid2(p1)
+ expect(exit_status).to eq(0)
end
it "test returns without waiting when the lock is acquired" do
run_lock = Chef::RunLock.new(lockfile)
+ from_tests, to_fork = IO.pipe
+ from_fork, to_tests = IO.pipe
p1 = fork do
run_lock.acquire
- run_lock.save_pid
- sleep 2
- exit! 1
+ to_tests.puts "lock acquired"
+ # Wait for the test to tell us we can exit before exiting
+ from_tests.readline
+ exit! 0
end
- wait_on_lock
-
+ wait_on_lock(from_fork)
expect(run_lock.test).to eq(false)
- Process.waitpid2(p1)
+
+ to_fork.puts "you can exit now"
+ pid, exit_status = Process.waitpid2(p1)
+ expect(exit_status).to eq(0)
end
end
@@ -386,9 +395,7 @@ describe Chef::RunLock do
# Send it the kill signal over and over until it dies
Timeout.timeout(CLIENT_PROCESS_TIMEOUT) do
Process.kill(:KILL, pid)
- until Process.waitpid2(pid, Process::WNOHANG)
- sleep(0.05)
- end
+ sleep(0.05) until Process.waitpid2(pid, Process::WNOHANG)
end
example.log_event("#{name}.stop finished (stopped pid #{pid})")
# Process not found is perfectly fine when we're trying to kill a process :)
diff --git a/spec/functional/shell_spec.rb b/spec/functional/shell_spec.rb
index fe2abdb12a..08c791f2d2 100644
--- a/spec/functional/shell_spec.rb
+++ b/spec/functional/shell_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright 2012-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,7 +20,6 @@ require "spec_helper"
require "functional/resource/base"
require "chef/version"
require "chef/shell"
-require "chef/mixin/command/unix"
describe Shell do
@@ -28,7 +27,6 @@ describe Shell do
# not catch cases where chef-shell fails to boot because of changes in
# chef/client.rb
describe "smoke tests", :unix_only => true do
- include Chef::Mixin::Command::Unix
TIMEOUT = 300
@@ -79,44 +77,26 @@ describe Shell do
end
def run_chef_shell_with(options)
- case ohai[:platform]
- when "aix"
- config = File.expand_path("shef-config.rb", CHEF_SPEC_DATA)
- path_to_chef_shell = File.expand_path("../../../bin/chef-shell", __FILE__)
- output = ""
- status = popen4("#{path_to_chef_shell} -c #{config} #{options}", :waitlast => true) do |pid, stdin, stdout, stderr|
- read_until(stdout, "chef (#{Chef::VERSION})>")
- yield stdout, stdin if block_given?
- stdin.write("'done'\n")
- output = read_until(stdout, '=> "done"')
- stdin.print("exit\n")
- flush_output(stdout)
- end
+ # Windows ruby installs don't (always?) have PTY,
+ # so hide the require here
- [output, status.exitstatus]
- else
- # Windows ruby installs don't (always?) have PTY,
- # so hide the require here
- begin
- require "pty"
- config = File.expand_path("shef-config.rb", CHEF_SPEC_DATA)
- path_to_chef_shell = File.expand_path("../../../bin/chef-shell", __FILE__)
- reader, writer, pid = PTY.spawn("#{path_to_chef_shell} -c #{config} #{options}")
- read_until(reader, "chef (#{Chef::VERSION})>")
- yield reader, writer if block_given?
- writer.puts('"done"')
- output = read_until(reader, '=> "done"')
- writer.print("exit\n")
- flush_output(reader)
- writer.close
-
- exitstatus = wait_or_die(pid)
-
- [output, exitstatus]
- rescue PTY::ChildExited => e
- [output, e.status]
- end
- end
+ require "pty"
+ config = File.expand_path("shef-config.rb", CHEF_SPEC_DATA)
+ path_to_chef_shell = File.expand_path("../../../bin/chef-shell", __FILE__)
+ reader, writer, pid = PTY.spawn("#{path_to_chef_shell} -c #{config} #{options}")
+ read_until(reader, "chef (#{Chef::VERSION})>")
+ yield reader, writer if block_given?
+ writer.puts('"done"')
+ output = read_until(reader, '=> "done"')
+ writer.print("exit\n")
+ flush_output(reader)
+ writer.close
+
+ exitstatus = wait_or_die(pid)
+
+ [output, exitstatus]
+ rescue PTY::ChildExited => e
+ [output, e.status]
end
it "boots correctly with -lauto" do
@@ -135,9 +115,27 @@ describe Shell do
expect(exitstatus).to eq(0)
end
+ context "on solo mode" do
+ it "starts correctly" do
+ output, exitstatus = run_chef_shell_with("--solo")
+ expect(output).to include("done")
+ expect(exitstatus).to eq(0)
+ end
+
+ it "should be able to use the API" do
+ output, exitstatus = run_chef_shell_with("-s") do |out, keyboard|
+ simple_api_get = "api.get('data')"
+ keyboard.puts(simple_api_get)
+ read_until(out, simple_api_get)
+ end
+ expect(output).to include("{}")
+ expect(exitstatus).to eq(0)
+ end
+ end
+
it "sets the override_runlist from the command line" do
output, exitstatus = run_chef_shell_with("-o 'override::foo,override::bar'") do |out, keyboard|
- show_recipes_code = %q[puts "#{node.recipes.inspect}"]
+ show_recipes_code = %q[puts "#{node["recipes"].inspect}"]
keyboard.puts(show_recipes_code)
read_until(out, show_recipes_code)
end
diff --git a/spec/functional/tiny_server_spec.rb b/spec/functional/tiny_server_spec.rb
index 2a025a2ecd..1ec56bd490 100644
--- a/spec/functional/tiny_server_spec.rb
+++ b/spec/functional/tiny_server_spec.rb
@@ -65,14 +65,15 @@ end
describe TinyServer::Manager do
it "runs the server" do
- @server = TinyServer::Manager.new
- @server.start
+ server = TinyServer::Manager.new
+ server.start
+ begin
+ TinyServer::API.instance.get("/index", 200, "[\"hello\"]")
- TinyServer::API.instance.get("/index", 200, "[\"hello\"]")
-
- rest = Chef::HTTP.new("http://localhost:9000")
- expect(rest.get("index")).to eq("[\"hello\"]")
-
- @server.stop
+ rest = Chef::HTTP.new("http://localhost:9000")
+ expect(rest.get("index")).to eq("[\"hello\"]")
+ ensure
+ server.stop
+ end
end
end
diff --git a/spec/functional/win32/crypto_spec.rb b/spec/functional/win32/crypto_spec.rb
index 75a8bfbd24..145c9881b9 100644
--- a/spec/functional/win32/crypto_spec.rb
+++ b/spec/functional/win32/crypto_spec.rb
@@ -22,7 +22,7 @@ if Chef::Platform.windows?
end
describe "Chef::ReservedNames::Win32::Crypto", :windows_only do
- describe '#encrypt' do
+ describe "#encrypt" do
before(:all) do
new_node = Chef::Node.new
new_node.consume_external_attrs(OHAI_SYSTEM.data, {})
diff --git a/spec/functional/win32/registry_spec.rb b/spec/functional/win32/registry_spec.rb
index 4a6157a6d5..bcfa0ffd48 100644
--- a/spec/functional/win32/registry_spec.rb
+++ b/spec/functional/win32/registry_spec.rb
@@ -26,6 +26,7 @@ describe "Chef::Win32::Registry", :windows_only do
#Create a registry item
::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root"
::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Branch"
+ ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\B®anch"
::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Branch\\Flower"
::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root', Win32::Registry::KEY_ALL_ACCESS) do |reg|
reg["RootType1", Win32::Registry::REG_SZ] = "fibrous"
diff --git a/spec/functional/win32/security_spec.rb b/spec/functional/win32/security_spec.rb
index c4951f375c..f88cde0204 100644
--- a/spec/functional/win32/security_spec.rb
+++ b/spec/functional/win32/security_spec.rb
@@ -17,6 +17,8 @@
#
require "spec_helper"
+require "mixlib/shellout"
+require "chef/mixin/user_context"
if Chef::Platform.windows?
require "chef/win32/security"
end
@@ -26,13 +28,37 @@ describe "Chef::Win32::Security", :windows_only do
expect(Chef::ReservedNames::Win32::Security.has_admin_privileges?).to eq(true)
end
- # We've done some investigation adding a negative test and it turned
- # out to be a lot of work since mixlib-shellout doesn't have user
- # support for windows.
- #
- # TODO - Add negative tests once mixlib-shellout has user support
- it "has_admin_privileges? returns false when running as non-admin" do
- skip "requires user support in mixlib-shellout"
+ describe "running as non admin user" do
+ include Chef::Mixin::UserContext
+ let(:user) { "security_user" }
+ let(:password) { "Security@123" }
+
+ let(:domain) do
+ whoami = Mixlib::ShellOut.new("whoami")
+ whoami.run_command
+ whoami.error!
+ whoami.stdout.split("\\")[0]
+ end
+
+ before do
+ allow_any_instance_of(Chef::Mixin::UserContext).to receive(:node).and_return({ "platform_family" => "windows" })
+ add_user = Mixlib::ShellOut.new("net user #{user} #{password} /ADD")
+ add_user.run_command
+ add_user.error!
+ end
+
+ after do
+ delete_user = Mixlib::ShellOut.new("net user #{user} /delete")
+ delete_user.run_command
+ delete_user.error!
+ end
+
+ it "has_admin_privileges? returns false" do
+ has_admin_privileges = with_user_context(user, password, domain, :local) do
+ Chef::ReservedNames::Win32::Security.has_admin_privileges?
+ end
+ expect(has_admin_privileges).to eq(false)
+ end
end
describe "get_file_security" do
@@ -45,27 +71,27 @@ describe "Chef::Win32::Security", :windows_only do
end
describe "access_check" do
- let(:security_descriptor) {
+ let(:security_descriptor) do
Chef::ReservedNames::Win32::Security.get_file_security(
"C:\\Program Files")
- }
+ end
let(:token_rights) { Chef::ReservedNames::Win32::Security::TOKEN_ALL_ACCESS }
- let(:token) {
+ let(:token) do
Chef::ReservedNames::Win32::Security.open_process_token(
Chef::ReservedNames::Win32::Process.get_current_process,
token_rights).duplicate_token(:SecurityImpersonation)
- }
+ end
- let(:mapping) {
+ let(:mapping) do
mapping = Chef::ReservedNames::Win32::Security::GENERIC_MAPPING.new
mapping[:GenericRead] = Chef::ReservedNames::Win32::Security::FILE_GENERIC_READ
mapping[:GenericWrite] = Chef::ReservedNames::Win32::Security::FILE_GENERIC_WRITE
mapping[:GenericExecute] = Chef::ReservedNames::Win32::Security::FILE_GENERIC_EXECUTE
mapping[:GenericAll] = Chef::ReservedNames::Win32::Security::FILE_ALL_ACCESS
mapping
- }
+ end
let(:desired_access) { Chef::ReservedNames::Win32::Security::FILE_GENERIC_READ }
@@ -76,11 +102,11 @@ describe "Chef::Win32::Security", :windows_only do
end
describe "Chef::Win32::Security::Token" do
- let(:token) {
+ let(:token) do
Chef::ReservedNames::Win32::Security.open_process_token(
Chef::ReservedNames::Win32::Process.get_current_process,
token_rights)
- }
+ end
context "with all rights" do
let(:token_rights) { Chef::ReservedNames::Win32::Security::TOKEN_ALL_ACCESS }
@@ -97,4 +123,66 @@ describe "Chef::Win32::Security", :windows_only do
end
end
end
+
+ describe ".get_token_information_elevation_type" do
+ let(:token_rights) { Chef::ReservedNames::Win32::Security::TOKEN_READ }
+
+ let(:token) do
+ Chef::ReservedNames::Win32::Security.open_process_token(
+ Chef::ReservedNames::Win32::Process.get_current_process,
+ token_rights)
+ end
+
+ context "when the token is valid" do
+ let(:token_elevation_type) { [:TokenElevationTypeDefault, :TokenElevationTypeFull, :TokenElevationTypeLimited] }
+
+ it "returns the token elevation type" do
+ elevation_type = Chef::ReservedNames::Win32::Security.get_token_information_elevation_type(token)
+ expect(token_elevation_type).to include(elevation_type)
+ end
+ end
+
+ context "when the token is invalid" do
+ it "raises `handle invalid` error" do
+ # If `OpenProcessToken` is stubbed, `open_process_token` returns an invalid token
+ allow(Chef::ReservedNames::Win32::Security).to receive(:OpenProcessToken).and_return(true)
+ expect { Chef::ReservedNames::Win32::Security.get_token_information_elevation_type(token) }.to raise_error(Chef::Exceptions::Win32APIError)
+ end
+ end
+ end
+
+ describe ".get_account_right" do
+ let(:username) { ENV["USERNAME"] }
+
+ context "when given a valid username" do
+ it "returns an array of account right constants" do
+ Chef::ReservedNames::Win32::Security.add_account_right(username, "SeBatchLogonRight")
+ expect(Chef::ReservedNames::Win32::Security.get_account_right(username)).to include("SeBatchLogonRight")
+ end
+
+ it "passes an FFI::Pointer to LsaFreeMemory" do
+ Chef::ReservedNames::Win32::Security.add_account_right(username, "SeBatchLogonRight") # otherwise we return an empty array before LsaFreeMemory
+ expect(Chef::ReservedNames::Win32::Security).to receive(:LsaFreeMemory).with(instance_of(FFI::Pointer)).and_return(0) # not FFI::MemoryPointer
+ Chef::ReservedNames::Win32::Security.get_account_right(username)
+ end
+ end
+
+ context "when given an invalid username" do
+ let(:username) { "noooooooooope" }
+
+ it "raises an exception" do
+ expect { Chef::ReservedNames::Win32::Security.get_account_right(username) }.to raise_error(Chef::Exceptions::Win32APIError)
+ end
+ end
+ end
+
+ describe ".test_and_raise_lsa_nt_status" do
+ # NTSTATUS code: 0xC0000001 / STATUS_UNSUCCESSFUL
+ # Windows Error: ERROR_GEN_FAILURE / 31 / 0x1F / A device attached to the system is not functioning.
+ let(:status_unsuccessful) { 0xC0000001 }
+
+ it "raises an exception with the Win Error if the win32 result is not 0" do
+ expect { Chef::ReservedNames::Win32::Security.test_and_raise_lsa_nt_status(status_unsuccessful) }.to raise_error(Chef::Exceptions::Win32APIError)
+ end
+ end
end
diff --git a/spec/functional/win32/versions_spec.rb b/spec/functional/win32/versions_spec.rb
index d6e840ed7f..19bd0e3875 100644
--- a/spec/functional/win32/versions_spec.rb
+++ b/spec/functional/win32/versions_spec.rb
@@ -21,7 +21,7 @@ if Chef::Platform.windows?
require "chef/win32/version"
end
-describe "Chef::ReservedNames::Win32::Version", :windows_only, :not_supported_on_win2k3 do
+describe "Chef::ReservedNames::Win32::Version", :windows_only do
before do
wmi = WmiLite::Wmi.new
@@ -31,14 +31,12 @@ describe "Chef::ReservedNames::Win32::Version", :windows_only, :not_supported_on
# On Win2k8R2 and later, we can dynamically obtain marketing
# names for comparison from WMI so the test should not
# need to be modified when new Windows releases arise.
- # For Win2k3 and Win2k8, we use static names in this test
- # based on the version number information from WMI. The names
- # from WMI contain extended characters such as registered
- # trademark on Win2k8 and Win2k3 that we're not using in our
- # library, so we have to set the expectation statically.
- if Chef::Platform.windows_server_2003?
- @current_os_version = "Windows Server 2003 R2"
- elsif is_windows_server_2008?(host)
+ # For Win2k8 we use static names in this test based on the
+ # version number information from WMI. The names from WMI
+ # contain extended characters such as registered trademark
+ # on Win2k8 that we're not using in our library, so we have
+ # to set the expectation statically.
+ if is_windows_server_2008?(host)
@current_os_version = "Windows Server 2008"
else
# The name from WMI is actually what we want in Win2k8R2+.
diff --git a/spec/integration/client/client_spec.rb b/spec/integration/client/client_spec.rb
index f6e50066bf..8f2364d99a 100644
--- a/spec/integration/client/client_spec.rb
+++ b/spec/integration/client/client_spec.rb
@@ -19,11 +19,11 @@ describe "chef-client" do
#
# just a normal file
# (expected_content should be uncompressed)
- @api.get("/recipes.tgz", 200) {
+ @api.get("/recipes.tgz", 200) do
File.open(recipes_filename, "rb") do |f|
f.read
end
- }
+ end
end
def stop_tiny_server
@@ -46,6 +46,7 @@ describe "chef-client" do
# we're running `chef-client` from the source tree and not the external one.
# cf. CHEF-4914
let(:chef_client) { "ruby '#{chef_dir}/chef-client' --minimal-ohai" }
+ let(:chef_solo) { "ruby '#{chef_dir}/chef-solo' --legacy-mode --minimal-ohai" }
let(:critical_env_vars) { %w{_ORIGINAL_GEM_PATH GEM_PATH GEM_HOME GEM_ROOT BUNDLE_BIN_PATH BUNDLE_GEMFILE RUBYLIB RUBYOPT RUBY_ENGINE RUBY_ROOT RUBY_VERSION PATH}.map { |o| "#{o}=#{ENV[o]}" } .join(" ") }
@@ -293,19 +294,19 @@ chef_server_url 'http://omg.com/blah'
cookbook_path "#{path_to('cookbooks')}"
EOM
- result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -r 'x::default' -z", :cwd => chef_dir)
+ result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -r 'x::default' -z -l info", :cwd => chef_dir)
expect(result.stdout).not_to include("Overridden Run List")
expect(result.stdout).to include("Run List is [recipe[x::default]]")
- #puts result.stdout
result.error!
end
- it "should complete with success when using --profile-ruby and output a profile file" do
+ it "should complete with success when using --profile-ruby and output a profile file", :not_supported_on_aix do
file "config/client.rb", <<EOM
local_mode true
cookbook_path "#{path_to('cookbooks')}"
EOM
result = shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' -z --profile-ruby", :cwd => chef_dir)
+ result.error!
expect(File.exist?(path_to("config/local-mode-cache/cache/graph_profile.out"))).to be true
end
@@ -315,6 +316,7 @@ local_mode true
cookbook_path "#{path_to('cookbooks')}"
EOM
result = shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' -z", :cwd => chef_dir)
+ result.error!
expect(File.exist?(path_to("config/local-mode-cache/cache/graph_profile.out"))).to be false
end
end
@@ -376,6 +378,8 @@ EOM
end
it "the cheffish DSL tries to load but fails (because chef-provisioning is not there)" do
+ # we'd need to have a custom bundle to fix this that omitted chef-provisioning, but that would dig our crazy even deeper, so lets not
+ skip "but if chef-provisioning is in our bundle or in our gemset then this test, very annoyingly, always fails"
command = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --no-fork", :cwd => chef_dir)
expect(command.exitstatus).to eql(1)
expect(command.stdout).to match(/cannot load such file -- chef\/provisioning/)
@@ -385,18 +389,8 @@ EOM
when_the_repository "has a cookbook that generates deprecation warnings" do
before do
file "cookbooks/x/recipes/default.rb", <<-EOM
- class ::MyResource < Chef::Resource
- use_automatic_resource_name
- property :x, default: []
- property :y, default: {}
- end
-
- my_resource 'blah' do
- 1.upto(10) do
- x nil
- end
- x nil
- end
+ Chef.deprecated(:internal_api, "Test deprecation")
+ Chef.deprecated(:internal_api, "Test deprecation")
EOM
end
@@ -429,7 +423,7 @@ EOM
expect(run_complete).to be >= 0
# Make sure there is exactly one result for each, and that it occurs *after* the complete message.
- expect(match_indices(/An attempt was made to change x from \[\] to nil by calling x\(nil\). In Chef 12, this does a get rather than a set. In Chef 13, this will change to set the value to nil./, result.stdout)).to match([ be > run_complete ])
+ expect(match_indices(/Test deprecation/, result.stdout)).to match([ be > run_complete ])
end
end
@@ -452,7 +446,7 @@ control_group "control group without top level control" do
end
RECIPE
- result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'audit_test::succeed'", :cwd => chef_dir)
+ result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'audit_test::succeed' -l info", :cwd => chef_dir)
expect(result.error?).to be_falsey
expect(result.stdout).to include("Successfully executed all `control_group` blocks and contained examples")
end
@@ -472,13 +466,76 @@ end
end
end
+ when_the_repository "has a cookbook that deploys a file" do
+ before do
+ file "cookbooks/x/recipes/default.rb", <<-RECIPE
+cookbook_file #{path_to('tempfile.txt').inspect} do
+ source "my_file"
+end
+ RECIPE
+
+ file "cookbooks/x/files/my_file", <<-FILE
+this is my file
+ FILE
+ end
+
+ [true, false].each do |lazy|
+ context "with no_lazy_load set to #{lazy}" do
+ it "should create the file" do
+ file "config/client.rb", <<EOM
+no_lazy_load #{lazy}
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+EOM
+ result = shell_out("#{chef_client} -l debug -c \"#{path_to('config/client.rb')}\" -o 'x::default' --no-fork", :cwd => chef_dir)
+ result.error!
+
+ expect(IO.read(path_to("tempfile.txt")).strip).to eq("this is my file")
+ end
+ end
+ end
+ end
+
+ when_the_repository "has a cookbook with an ohai plugin" do
+ before do
+ file "cookbooks/x/recipes/default.rb", <<-RECIPE
+file #{path_to('tempfile.txt').inspect} do
+ content node["english"]["version"]
+end
+ RECIPE
+
+ file "cookbooks/x/ohai/english.rb", <<-OHAI
+ Ohai.plugin(:English) do
+ provides 'english'
+
+ collect_data do
+ english Mash.new
+ english[:version] = "2014"
+ end
+ end
+ OHAI
+
+ file "config/client.rb", <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+EOM
+ end
+
+ it "should run the ohai plugin" do
+ result = shell_out("#{chef_client} -l debug -c \"#{path_to('config/client.rb')}\" -o 'x::default' --no-fork", :cwd => chef_dir)
+ result.error!
+
+ expect(IO.read(path_to("tempfile.txt"))).to eq("2014")
+ end
+ end
+
# Fails on appveyor, but works locally on windows and on windows hosts in Ci.
context "when using recipe-url", :skip_appveyor do
- before(:all) do
+ before(:each) do
start_tiny_server
end
- after(:all) do
+ after(:each) do
stop_tiny_server
end
@@ -496,5 +553,179 @@ EOM
result = shell_out("#{chef_client} --recipe-url=http://localhost:9000/recipes.tgz", :cwd => tmp_dir)
expect(result.exitstatus).not_to eq(0)
end
+
+ it "should fail when passed --recipe-url with a file that doesn't exist" do
+ broken_path = File.join(CHEF_SPEC_DATA, "recipes_dont_exist.tgz")
+ result = shell_out("#{chef_client} --recipe-url=#{broken_path}", :cwd => tmp_dir)
+ expect(result.exitstatus).not_to eq(0)
+ end
+ end
+
+ when_the_repository "has a cookbook with broken metadata.rb, but has metadata.json" do
+ before do
+ file "cookbooks/x/recipes/default.rb", ""
+ file "cookbooks/x/metadata.rb", <<EOM
+name 'x'
+version '0.0.1'
+raise "TEH SADNESS"
+EOM
+ file "cookbooks/x/metadata.json", <<EOM
+{
+ "name": "x",
+ "version": "0.0.1"
+}
+EOM
+
+ file "config/client.rb", <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+EOM
+ end
+
+ it "the chef client run should succeed" do
+ command = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --no-fork", :cwd => chef_dir)
+ command.error!
+ end
+
+ it "a chef-solo run should succeed" do
+ command = shell_out("#{chef_solo} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --no-fork", :cwd => chef_dir)
+ command.error!
+ end
+ end
+
+ when_the_repository "has a cookbook that logs at the info level" do
+ before do
+ file "cookbooks/x/recipes/default.rb", <<EOM
+ log "info level" do
+ level :info
+ end
+EOM
+ file "config/client.rb", <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+EOM
+ end
+
+ it "a chef client run should not log to info by default" do
+ command = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --no-fork", :cwd => chef_dir)
+ command.error!
+ expect(command.stdout).not_to include("INFO")
+ end
+
+ it "a chef client run to a pipe should not log to info by default" do
+ command = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --no-fork | tee #{path_to('chefrun.out')}", :cwd => chef_dir)
+ command.error!
+ expect(command.stdout).not_to include("INFO")
+ end
+
+ it "a chef solo run should not log to info by default" do
+ command = shell_out("#{chef_solo} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --no-fork", :cwd => chef_dir)
+ command.error!
+ expect(command.stdout).not_to include("INFO")
+ end
+
+ it "a chef solo run to a pipe should not log to info by default" do
+ command = shell_out("#{chef_solo} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --no-fork | tee #{path_to('chefrun.out')}", :cwd => chef_dir)
+ command.error!
+ expect(command.stdout).not_to include("INFO")
+ end
+ end
+
+ when_the_repository "has a cookbook that knows if we're running forked" do
+ before do
+ file "cookbooks/x/recipes/default.rb", <<~EOM
+ puts Chef::Config[:client_fork] ? "WITHFORK" : "NOFORK"
+EOM
+ file "config/client.rb", <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+EOM
+ end
+
+ it "chef-client runs by default with no supervisor" do
+ command = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+ command.error!
+ expect(command.stdout).to include("NOFORK")
+ end
+
+ it "chef-solo runs by default with no supervisor" do
+ command = shell_out("#{chef_solo} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+ command.error!
+ expect(command.stdout).to include("NOFORK")
+ end
+
+ it "chef-client --no-fork does not fork" do
+ command = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --no-fork", :cwd => chef_dir)
+ command.error!
+ expect(command.stdout).to include("NOFORK")
+ end
+
+ it "chef-solo --no-fork does not fork" do
+ command = shell_out("#{chef_solo} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --no-fork", :cwd => chef_dir)
+ command.error!
+ expect(command.stdout).to include("NOFORK")
+ end
+
+ it "chef-client with --fork uses a supervisor" do
+ command = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --fork", :cwd => chef_dir)
+ command.error!
+ expect(command.stdout).to include("WITHFORK")
+ end
+
+ it "chef-solo with --fork uses a supervisor" do
+ command = shell_out("#{chef_solo} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --fork", :cwd => chef_dir)
+ command.error!
+ expect(command.stdout).to include("WITHFORK")
+ end
+ end
+
+ when_the_repository "has a cookbook that knows if we're running forked, and configures forking in config.rb" do
+ before do
+ file "cookbooks/x/recipes/default.rb", <<~EOM
+ puts Chef::Config[:client_fork] ? "WITHFORK" : "NOFORK"
+EOM
+ file "config/client.rb", <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+client_fork true
+EOM
+ end
+
+ it "chef-client uses a supervisor" do
+ command = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+ command.error!
+ expect(command.stdout).to include("WITHFORK")
+ end
+
+ it "chef-solo uses a supervisor" do
+ command = shell_out("#{chef_solo} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+ command.error!
+ expect(command.stdout).to include("WITHFORK")
+ end
+ end
+
+ when_the_repository "has a cookbook that knows if we're running forked, and configures no-forking in config.rb" do
+ before do
+ file "cookbooks/x/recipes/default.rb", <<~EOM
+ puts Chef::Config[:client_fork] ? "WITHFORK" : "NOFORK"
+EOM
+ file "config/client.rb", <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+client_fork false
+EOM
+ end
+
+ it "chef-client uses a supervisor" do
+ command = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+ command.error!
+ expect(command.stdout).to include("NOFORK")
+ end
+
+ it "chef-solo uses a supervisor" do
+ command = shell_out("#{chef_solo} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+ command.error!
+ expect(command.stdout).to include("NOFORK")
+ end
end
end
diff --git a/spec/integration/client/exit_code_spec.rb b/spec/integration/client/exit_code_spec.rb
new file mode 100644
index 0000000000..4397426723
--- /dev/null
+++ b/spec/integration/client/exit_code_spec.rb
@@ -0,0 +1,141 @@
+
+require "support/shared/integration/integration_helper"
+require "chef/mixin/shell_out"
+require "tiny_server"
+require "tmpdir"
+require "chef/platform"
+
+describe "chef-client" do
+
+ include IntegrationSupport
+ include Chef::Mixin::ShellOut
+
+ let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") }
+
+ # Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the
+ # following constraints are satisfied:
+ # * Windows: windows can only run batch scripts as bare executables. Rubygems
+ # creates batch wrappers for installed gems, but we don't have batch wrappers
+ # in the source tree.
+ # * Other `chef-client` in PATH: A common case is running the tests on a
+ # machine that has omnibus chef installed. In that case we need to ensure
+ # we're running `chef-client` from the source tree and not the external one.
+ # cf. CHEF-4914
+ let(:chef_client) { "ruby '#{chef_dir}/chef-client' --no-fork --minimal-ohai" }
+
+ let(:critical_env_vars) { %w{PATH RUBYOPT BUNDLE_GEMFILE GEM_PATH}.map { |o| "#{o}=#{ENV[o]}" } .join(" ") }
+
+ when_the_repository "uses RFC 062 defined exit codes" do
+
+ def setup_client_rb
+ file "config/client.rb", <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+EOM
+ end
+
+ def setup_client_rb_with_audit_mode
+ file "config/client.rb", <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+audit_mode :audit_only
+EOM
+ end
+
+ def run_chef_client_and_expect_exit_code(exit_code)
+ shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'",
+ :cwd => chef_dir,
+ :returns => [exit_code])
+ end
+
+ context "has a cookbook" do
+ context "with a library" do
+ context "which cannot be loaded" do
+ before do
+ file "cookbooks/x/recipes/default.rb", ""
+ file "cookbooks/x/libraries/error.rb", "require 'does/not/exist'"
+ end
+
+ it "exits with GENERIC_FAILURE, 1" do
+ setup_client_rb
+ run_chef_client_and_expect_exit_code 1
+ end
+ end
+ end
+
+ context "with an audit recipe" do
+ context "which fails" do
+ before do
+ file "cookbooks/x/recipes/default.rb", <<-RECIPE
+control_group "control group without top level control" do
+ it "should fail" do
+ expect(4 - 4).to eq(1)
+ end
+end
+RECIPE
+ end
+
+ it "exits with AUDIT_MODE_FAILURE, 42" do
+ setup_client_rb_with_audit_mode
+ run_chef_client_and_expect_exit_code 42
+ end
+ end
+ end
+
+ context "with a recipe" do
+ context "which throws an error" do
+ before { file "cookbooks/x/recipes/default.rb", "raise 'BOOM'" }
+
+ it "exits with GENERIC_FAILURE, 1" do
+ setup_client_rb
+ run_chef_client_and_expect_exit_code 1
+ end
+ end
+
+ context "with a recipe which calls Chef::Application.fatal with a non-RFC exit code" do
+ before { file "cookbooks/x/recipes/default.rb", "Chef::Application.fatal!('BOOM', 123)" }
+
+ it "exits with the GENERIC_FAILURE exit code, 1" do
+ setup_client_rb
+ run_chef_client_and_expect_exit_code 1
+ end
+ end
+
+ context "with a recipe which calls Chef::Application.exit with a non-RFC exit code" do
+ before { file "cookbooks/x/recipes/default.rb", "Chef::Application.exit!('BOOM', 231)" }
+
+ it "exits with the GENERIC_FAILURE exit code, 1" do
+ setup_client_rb
+ run_chef_client_and_expect_exit_code 1
+ end
+ end
+
+ context "when a reboot exception is raised (like from the reboot resource)" do
+ before do
+ file "cookbooks/x/recipes/default.rb", <<EOM
+raise Chef::Exceptions::Reboot.new
+EOM
+ end
+
+ it "exits with REBOOT_SCHEDULED, 35" do
+ setup_client_rb
+ run_chef_client_and_expect_exit_code 35
+ end
+ end
+
+ context "when an attempt to reboot fails (like from the reboot resource)" do
+ before do
+ file "cookbooks/x/recipes/default.rb", <<EOM
+raise Chef::Exceptions::RebootFailed.new
+EOM
+ end
+
+ it "exits with REBOOT_FAILED, 41" do
+ setup_client_rb
+ run_chef_client_and_expect_exit_code 41
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/integration/client/ipv6_spec.rb b/spec/integration/client/ipv6_spec.rb
index 68c58bb8ea..6452db1e39 100644
--- a/spec/integration/client/ipv6_spec.rb
+++ b/spec/integration/client/ipv6_spec.rb
@@ -83,7 +83,7 @@ END_CLIENT_RB
# Some Solaris test platforms are too old for IPv6. These tests should not
# otherwise be platform dependent, so exclude solaris
- when_the_chef_server "is running on IPv6", :not_supported_on_solaris, :not_supported_on_gce do
+ when_the_chef_server "is running on IPv6", :not_supported_on_solaris, :not_supported_on_gce, :not_supported_on_aix do
when_the_repository "has a cookbook with a no-op recipe" do
before do
diff --git a/spec/integration/knife/chef_repo_path_spec.rb b/spec/integration/knife/chef_repo_path_spec.rb
index cf12b7ddfe..e609fa60b3 100644
--- a/spec/integration/knife/chef_repo_path_spec.rb
+++ b/spec/integration/knife/chef_repo_path_spec.rb
@@ -48,7 +48,7 @@ describe "chef_repo_path tests", :workstation do
directory "chef_repo2" do
file "clients/client3.json", {}
- file "cookbooks/cookbook3/metadata.rb", ""
+ file "cookbooks/cookbook3/metadata.rb", "name 'cookbook3'"
file "data_bags/bag3/item3.json", {}
file "environments/env3.json", {}
file "nodes/node3.json", {}
@@ -79,6 +79,75 @@ describe "chef_repo_path tests", :workstation do
EOM
end
+ it "knife list --local -Rfp --chef-repo-path chef_r~1 / grabs chef_repo2 stuff", :windows_only do
+ Chef::Config.delete(:chef_repo_path)
+ knife("list --local -Rfp --chef-repo-path #{path_to('chef_r~1')} /").should_succeed <<EOM
+/clients/
+/clients/client3.json
+/cookbooks/
+/cookbooks/cookbook3/
+/cookbooks/cookbook3/metadata.rb
+/data_bags/
+/data_bags/bag3/
+/data_bags/bag3/item3.json
+/environments/
+/environments/env3.json
+/nodes/
+/nodes/node3.json
+/roles/
+/roles/role3.json
+/users/
+/users/user3.json
+EOM
+ end
+
+ it "knife list --local -Rfp --chef-repo-path chef_r~1 / grabs chef_repo2 stuff", :windows_only do
+ Chef::Config.delete(:chef_repo_path)
+ knife("list -z -Rfp --chef-repo-path #{path_to('chef_r~1')} /").should_succeed <<EOM
+/acls/
+/acls/clients/
+/acls/clients/client3.json
+/acls/containers/
+/acls/cookbook_artifacts/
+/acls/cookbooks/
+/acls/cookbooks/cookbook3.json
+/acls/data_bags/
+/acls/data_bags/bag3.json
+/acls/environments/
+/acls/environments/env3.json
+/acls/groups/
+/acls/nodes/
+/acls/nodes/node3.json
+/acls/organization.json
+/acls/policies/
+/acls/policy_groups/
+/acls/roles/
+/acls/roles/role3.json
+/clients/
+/clients/client3.json
+/containers/
+/cookbook_artifacts/
+/cookbooks/
+/cookbooks/cookbook3/
+/cookbooks/cookbook3/metadata.rb
+/data_bags/
+/data_bags/bag3/
+/data_bags/bag3/item3.json
+/environments/
+/environments/env3.json
+/groups/
+/invitations.json
+/members.json
+/nodes/
+/nodes/node3.json
+/org.json
+/policies/
+/policy_groups/
+/roles/
+/roles/role3.json
+EOM
+ end
+
context "when all _paths are set to alternates" do
before :each do
%w{client cookbook data_bag environment node role user}.each do |object_name|
@@ -774,7 +843,7 @@ EOM
context "when data_bag_path and chef_repo_path are set, and nothing else" do
before :each do
- %w{client cookbook environment node role user}.each do |object_name|
+ %w{client cookbook environment node role user}.each do |object_name|
Chef::Config.delete("#{object_name}_path".to_sym)
end
Chef::Config.data_bag_path = File.join(Chef::Config.chef_repo_path, "data_bags")
@@ -834,7 +903,7 @@ EOM
include_context "default config options"
before :each do
- %w{client cookbook environment node role user}.each do |object_name|
+ %w{client cookbook environment node role user}.each do |object_name|
Chef::Config.delete("#{object_name}_path".to_sym)
end
Chef::Config.delete(:chef_repo_path)
diff --git a/spec/integration/knife/chef_repository_file_system_spec.rb b/spec/integration/knife/chef_repository_file_system_spec.rb
index cc538c98c0..222d3aee8a 100644
--- a/spec/integration/knife/chef_repository_file_system_spec.rb
+++ b/spec/integration/knife/chef_repository_file_system_spec.rb
@@ -158,103 +158,6 @@ EOM
end
end
- when_the_repository "has extraneous subdirectories and files under a cookbook" do
- before do
- directory "cookbooks/cookbook1" do
- file "a.rb", ""
- file "blarghle/blah.rb", ""
- directory "attributes" do
- file "a.rb", ""
- file "b.json", {}
- file "c/d.rb", ""
- file "c/e.json", {}
- end
- directory "definitions" do
- file "a.rb", ""
- file "b.json", {}
- file "c/d.rb", ""
- file "c/e.json", {}
- end
- directory "recipes" do
- file "a.rb", ""
- file "b.json", {}
- file "c/d.rb", ""
- file "c/e.json", {}
- end
- directory "libraries" do
- file "a.rb", ""
- file "b.json", {}
- file "c/d.rb", ""
- file "c/e.json", {}
- end
- directory "templates" do
- file "a.rb", ""
- file "b.json", {}
- file "c/d.rb", ""
- file "c/e.json", {}
- end
- directory "files" do
- file "a.rb", ""
- file "b.json", {}
- file "c/d.rb", ""
- file "c/e.json", {}
- end
- directory "resources" do
- file "a.rb", ""
- file "b.json", {}
- file "c/d.rb", ""
- file "c/e.json", {}
- end
- directory "providers" do
- file "a.rb", ""
- file "b.json", {}
- file "c/d.rb", ""
- file "c/e.json", {}
- end
- end
- end
-
- it "knife list --local -Rfp / should NOT return them" do
- knife("list --local -Rfp /").should_succeed <<EOM
-/cookbooks/
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/a.rb
-/cookbooks/cookbook1/attributes/
-/cookbooks/cookbook1/attributes/a.rb
-/cookbooks/cookbook1/definitions/
-/cookbooks/cookbook1/definitions/a.rb
-/cookbooks/cookbook1/files/
-/cookbooks/cookbook1/files/a.rb
-/cookbooks/cookbook1/files/b.json
-/cookbooks/cookbook1/files/c/
-/cookbooks/cookbook1/files/c/d.rb
-/cookbooks/cookbook1/files/c/e.json
-/cookbooks/cookbook1/libraries/
-/cookbooks/cookbook1/libraries/a.rb
-/cookbooks/cookbook1/libraries/b.json
-/cookbooks/cookbook1/libraries/c/
-/cookbooks/cookbook1/libraries/c/d.rb
-/cookbooks/cookbook1/libraries/c/e.json
-/cookbooks/cookbook1/providers/
-/cookbooks/cookbook1/providers/a.rb
-/cookbooks/cookbook1/providers/c/
-/cookbooks/cookbook1/providers/c/d.rb
-/cookbooks/cookbook1/recipes/
-/cookbooks/cookbook1/recipes/a.rb
-/cookbooks/cookbook1/resources/
-/cookbooks/cookbook1/resources/a.rb
-/cookbooks/cookbook1/resources/c/
-/cookbooks/cookbook1/resources/c/d.rb
-/cookbooks/cookbook1/templates/
-/cookbooks/cookbook1/templates/a.rb
-/cookbooks/cookbook1/templates/b.json
-/cookbooks/cookbook1/templates/c/
-/cookbooks/cookbook1/templates/c/d.rb
-/cookbooks/cookbook1/templates/c/e.json
-EOM
- end
- end
-
when_the_repository "has a file in cookbooks/" do
before { file "cookbooks/file", "" }
it "does not show up in list -Rfp" do
diff --git a/spec/integration/knife/client_bulk_delete_spec.rb b/spec/integration/knife/client_bulk_delete_spec.rb
new file mode 100644
index 0000000000..a422401af6
--- /dev/null
+++ b/spec/integration/knife/client_bulk_delete_spec.rb
@@ -0,0 +1,130 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife client bulk delete", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some clients" do
+ before do
+ client "concat", {}
+ client "cons", {}
+ client "car", {}
+ client "cdr", {}
+ client "cat", {}
+ end
+
+ it "deletes all matching clients" do
+ knife("client bulk delete ^ca.*", input: "Y").should_succeed <<EOM
+The following clients will be deleted:
+
+car cat
+
+Are you sure you want to delete these clients? (Y/N) Deleted client car
+Deleted client cat
+EOM
+
+ knife("client list").should_succeed <<EOM
+cdr
+chef-validator
+chef-webui
+concat
+cons
+EOM
+ end
+
+ it "deletes all matching clients when unanchored" do
+ knife("client bulk delete ca.*", input: "Y").should_succeed <<EOM
+The following clients will be deleted:
+
+car cat concat
+
+Are you sure you want to delete these clients? (Y/N) Deleted client car
+Deleted client cat
+Deleted client concat
+EOM
+
+ knife("client list").should_succeed <<EOM
+cdr
+chef-validator
+chef-webui
+cons
+EOM
+ end
+ end
+
+ when_the_chef_server "has a validator client" do
+ before do
+ client "cons", {}
+ client "car", {}
+ client "car-validator", { validator: true }
+ client "cdr", {}
+ client "cat", {}
+ end
+
+ it "refuses to delete a validator normally" do
+ knife("client bulk delete ^ca.*", input: "Y").should_succeed <<EOM
+The following clients are validators and will not be deleted:
+
+car-validator
+
+You must specify --delete-validators to delete the validator clients
+The following clients will be deleted:
+
+car cat
+
+Are you sure you want to delete these clients? (Y/N) Deleted client car
+Deleted client cat
+EOM
+
+ knife("client list").should_succeed <<EOM
+car-validator
+cdr
+chef-validator
+chef-webui
+cons
+EOM
+ end
+
+ it "deletes a validator when told to" do
+ knife("client bulk delete ^ca.* -D", input: "Y\nY").should_succeed <<EOM
+The following validators will be deleted:
+
+car-validator
+
+Are you sure you want to delete these validators? (Y/N) Deleted client car-validator
+The following clients will be deleted:
+
+car cat
+
+Are you sure you want to delete these clients? (Y/N) Deleted client car
+Deleted client cat
+EOM
+
+ knife("client list").should_succeed <<EOM
+cdr
+chef-validator
+chef-webui
+cons
+EOM
+ end
+ end
+end
diff --git a/spec/integration/knife/client_create_spec.rb b/spec/integration/knife/client_create_spec.rb
new file mode 100644
index 0000000000..10172833c8
--- /dev/null
+++ b/spec/integration/knife/client_create_spec.rb
@@ -0,0 +1,69 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "openssl"
+
+describe "knife client create", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ let(:out) { "Created client[bah]\n" }
+
+ when_the_chef_server "is empty" do
+ it "creates a new client" do
+ knife("client create -k bah").should_succeed stderr: out
+ end
+
+ it "creates a new validator client" do
+ knife("client create -k --validator bah").should_succeed stderr: out
+ knife("client show bah").should_succeed <<EOM
+admin: false
+chef_type: client
+name: bah
+validator: true
+EOM
+ end
+
+ it "refuses to add an existing client" do
+ pending "Knife client create must not blindly overwrite an existing client"
+ knife("client create -k bah").should_succeed stderr: out
+ expect { knife("client create -k bah") }.to raise_error(Net::HTTPServerException)
+ end
+
+ it "saves the private key to a file" do
+ Dir.mktmpdir do |tgt|
+ knife("client create -f #{tgt}/bah.pem bah").should_succeed stderr: out
+ expect(File).to exist("#{tgt}/bah.pem")
+ end
+ end
+
+ it "reads the public key from a file" do
+ Dir.mktmpdir do |tgt|
+ key = OpenSSL::PKey::RSA.generate(1024)
+ File.open("#{tgt}/public.pem", "w") { |pub| pub.write(key.public_key.to_pem) }
+ knife("client create -p #{tgt}/public.pem bah").should_succeed stderr: out
+ end
+ end
+
+ it "refuses to run if conflicting options are passed" do
+ knife("client create -p public.pem --prevent-keygen blah").should_fail stderr: "FATAL: You cannot pass --public-key and --prevent-keygen\n", stdout: /^USAGE.*/
+ end
+ end
+end
diff --git a/spec/integration/knife/client_delete_spec.rb b/spec/integration/knife/client_delete_spec.rb
new file mode 100644
index 0000000000..d135dd0a5b
--- /dev/null
+++ b/spec/integration/knife/client_delete_spec.rb
@@ -0,0 +1,63 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife client delete", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some clients" do
+ before do
+ client "cons", {}
+ client "car", {}
+ client "car-validator", { validator: true }
+ client "cdr", {}
+ client "cat", {}
+ end
+
+ it "deletes a client" do
+ knife("client delete car", input: "Y").should_succeed <<EOM
+Do you really want to delete car? (Y/N) Deleted client[car]
+EOM
+
+ knife("client list").should_succeed <<EOM
+car-validator
+cat
+cdr
+chef-validator
+chef-webui
+cons
+EOM
+ end
+
+ it "refuses to delete a validator normally" do
+ knife("client delete car-validator", input: "Y").should_fail exit_code: 2, stdout: "Do you really want to delete car-validator? (Y/N) ", stderr: <<EOM
+FATAL: You must specify --delete-validators to delete the validator client car-validator
+EOM
+ end
+
+ it "deletes a validator correctly" do
+ knife("client delete car-validator -D", input: "Y").should_succeed <<EOM
+Do you really want to delete car-validator? (Y/N) Deleted client[car-validator]
+EOM
+ end
+
+ end
+end
diff --git a/spec/integration/knife/client_key_create_spec.rb b/spec/integration/knife/client_key_create_spec.rb
new file mode 100644
index 0000000000..b588afbe50
--- /dev/null
+++ b/spec/integration/knife/client_key_create_spec.rb
@@ -0,0 +1,65 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "openssl"
+
+describe "knife client key create", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ let(:out) { "Created key: new" }
+
+ when_the_chef_server "has a client" do
+ before do
+ client "bah", {}
+ end
+
+ it "creates a new client key" do
+ knife("client key create -k new bah").should_succeed stderr: /^#{out}/, stdout: /.*BEGIN RSA PRIVATE KEY/
+ end
+
+ it "creates a new client key with an expiration date" do
+ date = "2017-12-31T23:59:59Z"
+ knife("client key create -k new -e #{date} bah").should_succeed stderr: /^#{out}/, stdout: /.*BEGIN RSA PRIVATE KEY/
+ knife("client key show bah new").should_succeed /expiration_date:.*#{date}/
+ end
+
+ it "refuses to add an already existing key" do
+ knife("client key create -k new bah")
+ expect { knife("client key create -k new bah") }.to raise_error(Net::HTTPServerException)
+ end
+
+ it "saves the private key to a file" do
+ Dir.mktmpdir do |tgt|
+ knife("client key create -f #{tgt}/bah.pem -k new bah").should_succeed stderr: /^#{out}/
+ expect(File).to exist("#{tgt}/bah.pem")
+ end
+ end
+
+ it "reads the public key from a file" do
+ Dir.mktmpdir do |tgt|
+ key = OpenSSL::PKey::RSA.generate(1024)
+ File.open("#{tgt}/public.pem", "w") { |pub| pub.write(key.public_key.to_pem) }
+ knife("client key create -p #{tgt}/public.pem -k new bah").should_succeed stderr: /^#{out}/
+ end
+ end
+
+ end
+end
diff --git a/spec/integration/knife/client_key_delete_spec.rb b/spec/integration/knife/client_key_delete_spec.rb
new file mode 100644
index 0000000000..d5827aa545
--- /dev/null
+++ b/spec/integration/knife/client_key_delete_spec.rb
@@ -0,0 +1,42 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife client key delete", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has a client" do
+ before do
+ client "car", {}
+ end
+
+ it "deletes a client" do
+ out = "Do you really want to delete the key named new for the client named car? (Y/N) "
+ knife("client key create -k new car")
+ knife("client key delete car new", input: "Y").should_succeed stdout: out, stderr: <<EOM
+Deleted key named new for the client named car
+EOM
+
+ knife("client key list car").should_succeed ""
+ end
+
+ end
+end
diff --git a/spec/integration/knife/client_key_list_spec.rb b/spec/integration/knife/client_key_list_spec.rb
new file mode 100644
index 0000000000..de9894622e
--- /dev/null
+++ b/spec/integration/knife/client_key_list_spec.rb
@@ -0,0 +1,60 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "date"
+
+describe "knife client key list", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ let(:now) { DateTime.now }
+ let(:last_month) { (now << 1).strftime("%FT%TZ") }
+ let(:next_month) { (now >> 1).strftime("%FT%TZ") }
+
+ when_the_chef_server "has a client" do
+ before do
+ client "cons", {}
+ knife("client key create cons -k new")
+ knife("client key create cons -k next_month -e #{next_month}")
+ knife("client key create cons -k expired -e #{last_month}")
+ end
+
+ it "lists the keys for a client" do
+ knife("client key list cons").should_succeed "expired\nnew\nnext_month\n"
+ end
+
+ it "shows detailed output" do
+ knife("client key list -w cons").should_succeed <<EOM
+expired: http://127.0.0.1:8900/clients/cons/keys/expired (expired)
+new: http://127.0.0.1:8900/clients/cons/keys/new
+next_month: http://127.0.0.1:8900/clients/cons/keys/next_month
+EOM
+ end
+
+ it "lists the expired keys for a client" do
+ knife("client key list -e cons").should_succeed "expired\n"
+ end
+
+ it "lists the unexpired keys for a client" do
+ knife("client key list -n cons").should_succeed "new\nnext_month\n"
+ end
+
+ end
+end
diff --git a/spec/integration/knife/client_key_show_spec.rb b/spec/integration/knife/client_key_show_spec.rb
new file mode 100644
index 0000000000..e96ff3b6fe
--- /dev/null
+++ b/spec/integration/knife/client_key_show_spec.rb
@@ -0,0 +1,44 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "date"
+
+describe "knife client key show", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ let(:now) { DateTime.now }
+ let(:last_month) { (now << 1).strftime("%FT%TZ") }
+ let(:next_month) { (now >> 1).strftime("%FT%TZ") }
+
+ when_the_chef_server "has a client" do
+ before do
+ client "cons", {}
+ knife("client key create cons -k new")
+ knife("client key create cons -k next_month -e #{next_month}")
+ knife("client key create cons -k expired -e #{last_month}")
+ end
+
+ it "shows a key for a client" do
+ knife("client key show cons new").should_succeed stdout: /.*name:.*new/
+ end
+
+ end
+end
diff --git a/spec/integration/knife/client_list_spec.rb b/spec/integration/knife/client_list_spec.rb
new file mode 100644
index 0000000000..4159df73f1
--- /dev/null
+++ b/spec/integration/knife/client_list_spec.rb
@@ -0,0 +1,48 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife client list", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some clients" do
+ before do
+ client "cons", {}
+ client "car", {}
+ client "car-validator", { validator: true }
+ client "cdr", {}
+ client "cat", {}
+ end
+
+ it "lists the clients" do
+ knife("client list").should_succeed <<EOM
+car
+car-validator
+cat
+cdr
+chef-validator
+chef-webui
+cons
+EOM
+ end
+
+ end
+end
diff --git a/spec/integration/knife/client_show_spec.rb b/spec/integration/knife/client_show_spec.rb
new file mode 100644
index 0000000000..23ac204d77
--- /dev/null
+++ b/spec/integration/knife/client_show_spec.rb
@@ -0,0 +1,36 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife client show", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has a client" do
+ before do
+ client "cons", {}
+ end
+
+ it "shows a client" do
+ knife("client show cons").should_succeed stdout: /.*name:.*cons/
+ end
+
+ end
+end
diff --git a/spec/integration/knife/common_options_spec.rb b/spec/integration/knife/common_options_spec.rb
index c941dcc1ee..6b6b83aafe 100644
--- a/spec/integration/knife/common_options_spec.rb
+++ b/spec/integration/knife/common_options_spec.rb
@@ -22,6 +22,14 @@ describe "knife common options", :workstation do
include IntegrationSupport
include KnifeSupport
+ before do
+ # Allow this for testing the various port binding stuffs. Remove when
+ # we kill off --listen.
+ Chef::Config.treat_deprecation_warnings_as_errors(false)
+ end
+
+ let(:local_listen_warning) { /\Awarn:.*local.*listen.*$/i }
+
when_the_repository "has a node" do
before { file "nodes/x.json", {} }
@@ -30,15 +38,20 @@ describe "knife common options", :workstation do
Chef::Config.chef_zero.enabled = true
end
- it "knife raw /nodes/x should retrieve the node" do
+ it "knife raw /nodes/x should retrieve the node in socketless mode" do
+ Chef::Config.treat_deprecation_warnings_as_errors(true)
knife("raw /nodes/x").should_succeed( /"name": "x"/ )
end
+ it "knife raw /nodes/x should retrieve the node" do
+ knife("raw --listen /nodes/x").should_succeed( /"name": "x"/, stderr: local_listen_warning )
+ end
+
context "And chef_zero.port is 9999" do
before(:each) { Chef::Config.chef_zero.port = 9999 }
it "knife raw /nodes/x should retrieve the node" do
- knife("raw /nodes/x").should_succeed( /"name": "x"/ )
+ knife("raw --listen /nodes/x").should_succeed( /"name": "x"/, stderr: local_listen_warning )
expect(Chef::Config.chef_server_url).to eq("chefzero://localhost:9999")
end
end
@@ -48,7 +61,7 @@ describe "knife common options", :workstation do
before(:each) { Chef::Config.chef_zero.host = "0.0.0.0" }
it "knife raw /nodes/x should retrieve the role" do
- knife("raw /nodes/x").should_succeed( /"name": "x"/ )
+ knife("raw --listen /nodes/x").should_succeed( /"name": "x"/, stderr: local_listen_warning )
end
end
@@ -86,21 +99,26 @@ EOM
end
it "knife raw /nodes/x should retrieve the node" do
- knife("raw /nodes/x").should_succeed( /"name": "x"/ )
+ knife("raw --listen /nodes/x").should_succeed( /"name": "x"/, stderr: local_listen_warning )
end
end
end
- it "knife raw -z /nodes/x retrieves the node" do
+ it "knife raw -z /nodes/x retrieves the node in socketless mode" do
+ Chef::Config.treat_deprecation_warnings_as_errors(true)
knife("raw -z /nodes/x").should_succeed( /"name": "x"/ )
end
+ it "knife raw -z /nodes/x retrieves the node" do
+ knife("raw -z --listen /nodes/x").should_succeed( /"name": "x"/, stderr: local_listen_warning )
+ end
+
it "knife raw --local-mode /nodes/x retrieves the node" do
- knife("raw --local-mode /nodes/x").should_succeed( /"name": "x"/ )
+ knife("raw --local-mode --listen /nodes/x").should_succeed( /"name": "x"/, stderr: local_listen_warning )
end
it "knife raw -z --chef-zero-port=9999 /nodes/x retrieves the node" do
- knife("raw -z --chef-zero-port=9999 /nodes/x").should_succeed( /"name": "x"/ )
+ knife("raw -z --chef-zero-port=9999 --listen /nodes/x").should_succeed( /"name": "x"/, stderr: local_listen_warning )
expect(Chef::Config.chef_server_url).to eq("chefzero://localhost:9999")
end
@@ -118,7 +136,7 @@ EOM
end
it "knife raw -z /nodes/x retrieves the node" do
- knife("raw -z /nodes/x").should_succeed( /"name": "x"/ )
+ knife("raw -z --listen /nodes/x").should_succeed( /"name": "x"/, stderr: local_listen_warning )
expect(URI(Chef::Config.chef_server_url).port).to be > 8889
end
end
@@ -137,18 +155,18 @@ EOM
end
it "knife raw -z --chef-zero-port=9999-20000 /nodes/x" do
- knife("raw -z --chef-zero-port=9999-20000 /nodes/x").should_succeed( /"name": "x"/ )
+ knife("raw -z --chef-zero-port=9999-20000 --listen /nodes/x").should_succeed( /"name": "x"/, stderr: local_listen_warning )
expect(URI(Chef::Config.chef_server_url).port).to be > 9999
end
it "knife raw -z --chef-zero-port=9999-9999,19423" do
- knife("raw -z --chef-zero-port=9999-9999,19423 /nodes/x").should_succeed( /"name": "x"/ )
+ knife("raw -z --chef-zero-port=9999-9999,19423 --listen /nodes/x").should_succeed( /"name": "x"/, stderr: local_listen_warning )
expect(URI(Chef::Config.chef_server_url).port).to be == 19423
end
end
it "knife raw -z --chef-zero-port=9999 /nodes/x retrieves the node" do
- knife("raw -z --chef-zero-port=9999 /nodes/x").should_succeed( /"name": "x"/ )
+ knife("raw -z --chef-zero-port=9999 --listen /nodes/x").should_succeed( /"name": "x"/, stderr: local_listen_warning )
expect(Chef::Config.chef_server_url).to eq("chefzero://localhost:9999")
end
end
diff --git a/spec/integration/knife/cookbook_bulk_delete_spec.rb b/spec/integration/knife/cookbook_bulk_delete_spec.rb
new file mode 100644
index 0000000000..912bd3bed5
--- /dev/null
+++ b/spec/integration/knife/cookbook_bulk_delete_spec.rb
@@ -0,0 +1,64 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/cookbook_bulk_delete"
+
+describe "knife cookbook bulk delete", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has a cookbook" do
+ before do
+ cookbook "foo", "1.0.0"
+ cookbook "foo", "0.6.5"
+ cookbook "fox", "0.6.0"
+ cookbook "fox", "0.6.5"
+ cookbook "fax", "0.6.0"
+ cookbook "zfa", "0.6.5"
+ end
+
+ # rubocop:disable Layout/TrailingWhitespace
+ it "knife cookbook bulk delete deletes all matching cookbooks" do
+ stdout = <<EOM
+All versions of the following cookbooks will be deleted:
+
+foo fox
+
+Do you really want to delete these cookbooks? (Y/N)
+EOM
+
+ stderr = <<EOM
+Deleted cookbook foo [1.0.0]
+Deleted cookbook foo [0.6.5]
+Deleted cookbook fox [0.6.5]
+Deleted cookbook fox [0.6.0]
+EOM
+
+ knife("cookbook bulk delete ^fo.*", input: "Y").should_succeed(stderr: stderr, stdout: stdout)
+
+ knife("cookbook list -a").should_succeed <<EOM
+fax 0.6.0
+zfa 0.6.5
+EOM
+ end
+ # rubocop:enable Layout/TrailingWhitespace
+
+ end
+end
diff --git a/spec/integration/knife/cookbook_download_spec.rb b/spec/integration/knife/cookbook_download_spec.rb
new file mode 100644
index 0000000000..2e64cac133
--- /dev/null
+++ b/spec/integration/knife/cookbook_download_spec.rb
@@ -0,0 +1,71 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/cookbook_download"
+require "tmpdir"
+
+describe "knife cookbook download", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ let(:tmpdir) { Dir.mktmpdir }
+
+ when_the_chef_server "has only one cookbook" do
+ before do
+ cookbook "x", "1.0.1"
+ end
+
+ it "knife cookbook download downloads the latest version" do
+ knife("cookbook download -d #{tmpdir} x").should_succeed stderr: <<EOM
+Downloading x cookbook version 1.0.1
+Downloading root_files
+Cookbook downloaded to #{tmpdir}/x-1.0.1
+EOM
+ end
+
+ it "knife cookbook download with a version downloads the specified version" do
+ knife("cookbook download -d #{tmpdir} x 1.0.1").should_succeed stderr: <<EOM
+Downloading x cookbook version 1.0.1
+Downloading root_files
+Cookbook downloaded to #{tmpdir}/x-1.0.1
+EOM
+ end
+
+ it "knife cookbook download with an unknown version raises an error" do
+ expect { knife("cookbook download -d #{tmpdir} x 1.0.0") }.to raise_error(Net::HTTPServerException)
+ end
+ end
+
+ when_the_chef_server "has multiple cookbook versions" do
+ before do
+ cookbook "x", "1.0.1"
+ cookbook "x", "1.0.0"
+ end
+
+ it "knife cookbook download with no version prompts" do
+ knife("cookbook download -d #{tmpdir} x", input: "2\n").should_succeed(stderr: <<EOM, stdout: "Which version do you want to download?\n1. x 1.0.0\n2. x 1.0.1\n\n"
+Downloading x cookbook version 1.0.1
+Downloading root_files
+Cookbook downloaded to #{tmpdir}/x-1.0.1
+EOM
+)
+ end
+ end
+end
diff --git a/spec/integration/knife/cookbook_list_spec.rb b/spec/integration/knife/cookbook_list_spec.rb
new file mode 100644
index 0000000000..65578696f2
--- /dev/null
+++ b/spec/integration/knife/cookbook_list_spec.rb
@@ -0,0 +1,54 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/cookbook_list"
+
+describe "knife cookbook list", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has a cookbook" do
+ before do
+ cookbook "x", "1.0.0"
+ cookbook "x", "0.6.5"
+ cookbook "x", "0.6.0"
+ cookbook "y", "0.6.5"
+ cookbook "y", "0.6.0"
+ cookbook "z", "0.6.5"
+ end
+
+ it "knife cookbook list shows all the cookbooks" do
+ knife("cookbook list").should_succeed <<EOM
+x 1.0.0
+y 0.6.5
+z 0.6.5
+EOM
+ end
+
+ it "knife cookbook list -a shows all the versions of all the cookbooks" do
+ knife("cookbook list -a").should_succeed <<EOM
+x 1.0.0 0.6.5 0.6.0
+y 0.6.5 0.6.0
+z 0.6.5
+EOM
+ end
+
+ end
+end
diff --git a/spec/integration/knife/cookbook_show_spec.rb b/spec/integration/knife/cookbook_show_spec.rb
new file mode 100644
index 0000000000..b89a26198a
--- /dev/null
+++ b/spec/integration/knife/cookbook_show_spec.rb
@@ -0,0 +1,148 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/cookbook_show"
+
+describe "knife cookbook show", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has a cookbook" do
+ before do
+ cookbook "x", "1.0.0", { "recipes" => { "default.rb" => "file 'n'", "x.rb" => "" } }
+ cookbook "x", "0.6.5"
+ end
+
+ it "knife cookbook show x shows all the versions" do
+ knife("cookbook show x").should_succeed "x 1.0.0 0.6.5\n"
+ end
+
+ # rubocop:disable Layout/TrailingWhitespace
+ it "knife cookbook show x 1.0.0 shows the correct version" do
+ knife("cookbook show x 1.0.0").should_succeed <<EOM
+cookbook_name: x
+frozen?: false
+metadata:
+ attributes:
+ chef_versions:
+ dependencies:
+ description:
+ gems:
+ issues_url:
+ license: All rights reserved
+ long_description:
+ maintainer:
+ maintainer_email:
+ name: x
+ ohai_versions:
+ platforms:
+ privacy: false
+ providing:
+ x: >= 0.0.0
+ x::x: >= 0.0.0
+ recipes:
+ x:
+ x::x:
+ source_url:
+ version: 1.0.0
+name: x-1.0.0
+recipes:
+ checksum: 4631b34cf58de10c5ef1304889941b2e
+ name: recipes/default.rb
+ path: recipes/default.rb
+ specificity: default
+ url: http://127.0.0.1:8900/file_store/checksums/4631b34cf58de10c5ef1304889941b2e
+
+ checksum: d41d8cd98f00b204e9800998ecf8427e
+ name: recipes/x.rb
+ path: recipes/x.rb
+ specificity: default
+ url: http://127.0.0.1:8900/file_store/checksums/d41d8cd98f00b204e9800998ecf8427e
+root_files:
+ checksum: 8226671f751ba102dea6a6b6bd32fa8d
+ name: metadata.rb
+ path: metadata.rb
+ specificity: default
+ url: http://127.0.0.1:8900/file_store/checksums/8226671f751ba102dea6a6b6bd32fa8d
+version: 1.0.0
+EOM
+ end
+
+ it "knife cookbook show x 1.0.0 metadata shows the metadata" do
+ knife("cookbook show x 1.0.0 metadata").should_succeed <<EOM
+attributes:
+chef_versions:
+dependencies:
+description:
+gems:
+issues_url:
+license: All rights reserved
+long_description:
+maintainer:
+maintainer_email:
+name: x
+ohai_versions:
+platforms:
+privacy: false
+providing:
+ x: >= 0.0.0
+ x::x: >= 0.0.0
+recipes:
+ x:
+ x::x:
+source_url:
+version: 1.0.0
+EOM
+ end
+
+ it "knife cookbook show x 1.0.0 recipes shows all the recipes" do
+ knife("cookbook show x 1.0.0 recipes").should_succeed <<EOM
+checksum: 4631b34cf58de10c5ef1304889941b2e
+name: recipes/default.rb
+path: recipes/default.rb
+specificity: default
+url: http://127.0.0.1:8900/file_store/checksums/4631b34cf58de10c5ef1304889941b2e
+
+checksum: d41d8cd98f00b204e9800998ecf8427e
+name: recipes/x.rb
+path: recipes/x.rb
+specificity: default
+url: http://127.0.0.1:8900/file_store/checksums/d41d8cd98f00b204e9800998ecf8427e
+EOM
+ end
+ # rubocop:enable Layout/TrailingWhitespace
+
+ it "knife cookbook show x 1.0.0 recipes default.rb shows the default recipe" do
+ knife("cookbook show x 1.0.0 recipes default.rb").should_succeed "file 'n'\n"
+ end
+
+ it "knife cookbook show with a non-existent file displays an error" do
+ expect { knife("cookbook show x 1.0.0 recipes moose.rb") }.to raise_error(Chef::Exceptions::FileNotFound)
+ end
+
+ it "knife cookbook show with a non-existent version displays an error" do
+ expect { knife("cookbook show x 1.0.1") }.to raise_error(Net::HTTPServerException)
+ end
+
+ it "knife cookbook show with a non-existent cookbook displays an error" do
+ expect { knife("cookbook show y") }.to raise_error(Net::HTTPServerException)
+ end
+ end
+end
diff --git a/spec/integration/knife/cookbook_upload_spec.rb b/spec/integration/knife/cookbook_upload_spec.rb
new file mode 100644
index 0000000000..a0de725603
--- /dev/null
+++ b/spec/integration/knife/cookbook_upload_spec.rb
@@ -0,0 +1,90 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/cookbook_upload"
+
+describe "knife cookbook upload", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ let (:cb_dir) { "#{@repository_dir}/cookbooks" }
+
+ when_the_chef_server "is empty" do
+ when_the_repository "has a cookbook" do
+ before do
+ file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
+ end
+
+ it "knife cookbook upload uploads the cookbook" do
+ knife("cookbook upload x -o #{cb_dir}").should_succeed stderr: <<EOM
+Uploading x [1.0.0]
+Uploaded 1 cookbook.
+EOM
+ end
+
+ it "knife cookbook upload --freeze uploads and freezes the cookbook" do
+ knife("cookbook upload x -o #{cb_dir} --freeze").should_succeed stderr: <<EOM
+Uploading x [1.0.0]
+Uploaded 1 cookbook.
+EOM
+ # Modify the file, attempt to reupload
+ file "cookbooks/x/metadata.rb", 'name "x"; version "1.0.0"#different'
+ knife("cookbook upload x -o #{cb_dir} --freeze").should_fail stderr: <<EOM
+Uploading x [1.0.0]
+ERROR: Version 1.0.0 of cookbook x is frozen. Use --force to override.
+WARNING: Not updating version constraints for x in the environment as the cookbook is frozen.
+ERROR: Failed to upload 1 cookbook.
+EOM
+ end
+ end
+
+ when_the_repository "has a cookbook that depends on another cookbook" do
+ before do
+ file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0", "\ndepends 'y'")
+ file "cookbooks/y/metadata.rb", cb_metadata("y", "1.0.0")
+ end
+
+ it "knife cookbook upload --include-dependencies uploads both cookbooks" do
+ knife("cookbook upload --include-dependencies x -o #{cb_dir}").should_succeed stderr: <<EOM
+Uploading x [1.0.0]
+Uploading y [1.0.0]
+Uploaded 2 cookbooks.
+EOM
+ end
+
+ it "knife cookbook upload fails due to missing dependencies" do
+ knife("cookbook upload x -o #{cb_dir}").should_fail stderr: <<EOM
+Uploading x [1.0.0]
+ERROR: Cookbook x depends on cookbooks which are not currently
+ERROR: being uploaded and cannot be found on the server.
+ERROR: The missing cookbook(s) are: 'y' version '>= 0.0.0'
+EOM
+ end
+
+ it "knife cookbook upload -a uploads both cookbooks" do
+ knife("cookbook upload -a -o #{cb_dir}").should_succeed stderr: <<EOM
+Uploading x [1.0.0]
+Uploading y [1.0.0]
+Uploaded all cookbooks.
+EOM
+ end
+ end
+ end
+end
diff --git a/spec/integration/knife/data_bag_create_spec.rb b/spec/integration/knife/data_bag_create_spec.rb
new file mode 100644
index 0000000000..dc61d55fd5
--- /dev/null
+++ b/spec/integration/knife/data_bag_create_spec.rb
@@ -0,0 +1,55 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/data_bag_create"
+
+describe "knife data bag create", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ let(:err) { "Created data_bag[foo]\n" }
+ let(:out) { "Created data_bag_item[bar]\n" }
+ let(:exists) { "Data bag foo already exists\n" }
+
+ when_the_chef_server "is empty" do
+ it "creates a new data bag" do
+ knife("data bag create foo").should_succeed stderr: err
+ end
+
+ it "creates a new data bag and item" do
+ knife("data bag create foo bar").should_succeed stdout: out, stderr: err
+ end
+
+ it "adds a new item to an existing bag" do
+ knife("data bag create foo").should_succeed stderr: err
+ knife("data bag create foo bar").should_succeed stdout: out, stderr: exists
+ end
+
+ it "refuses to add an existing data bag" do
+ knife("data bag create foo").should_succeed stderr: err
+ knife("data bag create foo").should_succeed stderr: exists
+ end
+
+ it "fails to add an existing item" do
+ knife("data bag create foo bar").should_succeed stdout: out, stderr: err
+ expect { knife("data bag create foo bar") }.to raise_error(Net::HTTPServerException)
+ end
+ end
+end
diff --git a/spec/integration/knife/data_bag_delete_spec.rb b/spec/integration/knife/data_bag_delete_spec.rb
new file mode 100644
index 0000000000..96345b0d2b
--- /dev/null
+++ b/spec/integration/knife/data_bag_delete_spec.rb
@@ -0,0 +1,58 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/data_bag_delete"
+
+describe "knife data bag delete", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some data bags" do
+ before do
+ data_bag "x", {}
+ data_bag "canteloupe", {}
+ data_bag "rocket", { "falcon9" => { heavy: "true" }, "atlas" => {}, "ariane" => {} }
+ end
+
+ it "with an empty data bag" do
+ knife("data bag delete canteloupe", input: "y").should_succeed <<EOM
+Do you really want to delete canteloupe? (Y/N) Deleted data_bag[canteloupe]
+EOM
+ end
+
+ it "with a bag with some items" do
+ knife("data bag delete rocket", input: "y").should_succeed <<EOM
+Do you really want to delete rocket? (Y/N) Deleted data_bag[rocket]
+EOM
+ end
+
+ it "with a single item" do
+ knife("data bag delete rocket falcon9", input: "y").should_succeed <<EOM
+Do you really want to delete falcon9? (Y/N) Deleted data_bag_item[falcon9]
+EOM
+ end
+
+ it "choosing not to delete" do
+ knife("data bag delete rocket falcon9", input: "n").should_succeed <<EOM, exit_code: 3
+Do you really want to delete falcon9? (Y/N) You said no, so I'm done here.
+EOM
+ end
+ end
+end
diff --git a/spec/integration/knife/data_bag_from_file_spec.rb b/spec/integration/knife/data_bag_from_file_spec.rb
new file mode 100644
index 0000000000..ca8f743487
--- /dev/null
+++ b/spec/integration/knife/data_bag_from_file_spec.rb
@@ -0,0 +1,115 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife data bag from file", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ let (:db_dir) { "#{@repository_dir}/data_bags" }
+
+ when_the_chef_server "has an empty data bag" do
+ before do
+ data_bag "foo", {}
+ data_bag "bar", {}
+ end
+
+ when_the_repository "has some data bag items" do
+ before do
+ file "data_bags/foo/bar.json", { "id" => "bar", "foo" => "bar " }
+ file "data_bags/foo/bzr.json", { "id" => "bzr", "foo" => "bar " }
+ file "data_bags/foo/cat.json", { "id" => "cat", "foo" => "bar " }
+ file "data_bags/foo/dog.json", { "id" => "dog", "foo" => "bar " }
+ file "data_bags/foo/encrypted.json", <<EOM
+{
+ "id": "encrypted",
+ "password": {
+ "encrypted_data": "H6ab5RY9a9JAkS8A0RCMspXtOJh0ai8cNeA4Q3gLO8s=\\n",
+ "iv": "uWKKKxrJgtELlGMCOLJdkA==\\n",
+ "version": 1,
+ "cipher": "aes-256-cbc"
+ }
+}
+EOM
+ file "data_bags/bar/round_trip.json", <<EOM
+{
+ "name": "data_bag_item_bar_round_trip",
+ "json_class": "Chef::DataBagItem",
+ "chef_type": "data_bag_item",
+ "data_bag": "bar",
+ "raw_data": {
+ "id": "round_trip",
+ "root_password": {
+ "encrypted_data": "noDOsTpsTAZlTU5sprhmYZzUDfr8du7hH/zRDOjRAmoTJHTZyfYoR221EOOW\\nXJ1D\\n",
+ "iv": "Bnqhfy6n0Hx1wCe9pxHLoA==\\n",
+ "version": 1,
+ "cipher": "aes-256-cbc"
+ },
+ "admin_password": {
+ "encrypted_data": "TcC7dU1gx6OnE5Ab4i/k42UEf0Nnr7cAyuTHId/LNjNOwpNf7XZc27DQSjuy\\nHPlt\\n",
+ "iv": "+TAWJuPWCI2+WB8lGJAyvw==\\n",
+ "version": 1,
+ "cipher": "aes-256-cbc"
+ }
+ }
+}
+EOM
+ end
+
+ it "uploads a single file" do
+ knife("data bag from file foo #{db_dir}/foo/bar.json").should_succeed stderr: <<EOM
+Updated data_bag_item[foo::bar]
+EOM
+ end
+
+ it "uploads a single encrypted file" do
+ knife("data bag from file foo #{db_dir}/foo/encrypted.json").should_succeed stderr: <<EOM
+Updated data_bag_item[foo::encrypted]
+EOM
+ end
+
+ it "uploads a file in chef's internal format" do
+ pending "chef/chef#4815"
+ knife("data bag from file bar #{db_dir}/bar/round_trip.json").should_succeed stderr: <<EOM
+Updated data_bag_item[bar::round_trip]
+EOM
+ end
+
+ it "uploads many files" do
+ knife("data bag from file foo #{db_dir}/foo/bar.json #{db_dir}/foo/bzr.json").should_succeed stderr: <<EOM
+Updated data_bag_item[foo::bar]
+Updated data_bag_item[foo::bzr]
+EOM
+ end
+
+ it "uploads a whole directory" do
+ knife("data bag from file foo #{db_dir}/foo")
+ knife("data bag show foo").should_succeed <<EOM
+bar
+bzr
+cat
+dog
+encrypted
+EOM
+ end
+
+ end
+ end
+end
diff --git a/spec/integration/knife/data_bag_list_spec.rb b/spec/integration/knife/data_bag_list_spec.rb
new file mode 100644
index 0000000000..7db9638660
--- /dev/null
+++ b/spec/integration/knife/data_bag_list_spec.rb
@@ -0,0 +1,43 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/data_bag_list"
+
+describe "knife data bag list", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some data bags" do
+ before do
+ data_bag "x", {}
+ data_bag "canteloupe", {}
+ data_bag "rocket", {}
+ end
+
+ it "knife data bag list shows all the cookbooks" do
+ knife("data bag list").should_succeed <<EOM
+canteloupe
+rocket
+x
+EOM
+ end
+
+ end
+end
diff --git a/spec/integration/knife/data_bag_show_spec.rb b/spec/integration/knife/data_bag_show_spec.rb
new file mode 100644
index 0000000000..38dfd8730d
--- /dev/null
+++ b/spec/integration/knife/data_bag_show_spec.rb
@@ -0,0 +1,53 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/data_bag_show"
+
+describe "knife data bag show", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some data bags" do
+ before do
+ data_bag "x", {}
+ data_bag "canteloupe", {}
+ data_bag "rocket", { "falcon9" => { heavy: "true" }, "atlas" => {}, "ariane" => {} }
+ end
+
+ it "with an empty data bag" do
+ knife("data bag show canteloupe").should_succeed "\n"
+ end
+
+ it "with a bag with some items" do
+ knife("data bag show rocket").should_succeed <<EOM
+ariane
+atlas
+falcon9
+EOM
+ end
+
+ it "with a single item" do
+ knife("data bag show rocket falcon9").should_succeed <<EOM
+heavy: true
+id: falcon9
+EOM
+ end
+ end
+end
diff --git a/spec/integration/knife/deps_spec.rb b/spec/integration/knife/deps_spec.rb
index de0872d39c..3f200396ba 100644
--- a/spec/integration/knife/deps_spec.rb
+++ b/spec/integration/knife/deps_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright 2013-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -211,20 +211,16 @@ depends "bar"'
depends "baz"'
file "cookbooks/baz/metadata.rb", 'name "baz"
depends "foo"'
- file "cookbooks/self/metadata.rb", 'name "self"
-depends "self"'
end
it "knife deps prints each once" do
- knife("deps /cookbooks/foo /cookbooks/self").should_succeed(
- stdout: "/cookbooks/baz\n/cookbooks/bar\n/cookbooks/foo\n/cookbooks/self\n",
- stderr: "WARN: Ignoring self-dependency in cookbook self, please remove it (in the future this will be fatal).\n"
+ knife("deps /cookbooks/foo").should_succeed(
+ stdout: "/cookbooks/baz\n/cookbooks/bar\n/cookbooks/foo\n"
)
end
it "knife deps --tree prints each once" do
- knife("deps --tree /cookbooks/foo /cookbooks/self").should_succeed(
- stdout: "/cookbooks/foo\n /cookbooks/bar\n /cookbooks/baz\n /cookbooks/foo\n/cookbooks/self\n",
- stderr: "WARN: Ignoring self-dependency in cookbook self, please remove it (in the future this will be fatal).\n"
+ knife("deps --tree /cookbooks/foo").should_succeed(
+ stdout: "/cookbooks/foo\n /cookbooks/bar\n /cookbooks/baz\n /cookbooks/foo\n"
)
end
end
@@ -246,7 +242,7 @@ EOM
it "knife deps --tree prints each once" do
knife("deps --tree /roles/foo.json /roles/self.json") do
expect(stdout).to eq("/roles/foo.json\n /roles/bar.json\n /roles/baz.json\n /roles/foo.json\n/roles/self.json\n /roles/self.json\n")
- expect(stderr).to eq("WARNING: No knife configuration file found\n")
+ expect(stderr).to eq("WARNING: No knife configuration file found. See https://docs.chef.io/config_rb_knife.html for details.\n")
end
end
end
@@ -289,11 +285,11 @@ EOM
:stderr => "ERROR: /cookbooks/x: No such file or directory\n"
)
end
- it "knife deps /data_bags/bag/item reports an error" do
- knife("deps /data_bags/bag/item").should_fail(
+ it "knife deps /data_bags/bag/item.json reports an error" do
+ knife("deps /data_bags/bag/item.json").should_fail(
:exit_code => 2,
- :stdout => "/data_bags/bag/item\n",
- :stderr => "ERROR: /data_bags/bag/item: No such file or directory\n"
+ :stdout => "/data_bags/bag/item.json\n",
+ :stderr => "ERROR: /data_bags/bag/item.json: No such file or directory\n"
)
end
end
@@ -584,7 +580,7 @@ EOM
it "knife deps --tree prints each once" do
knife("deps --remote --tree /roles/foo.json /roles/self.json") do
expect(stdout).to eq("/roles/foo.json\n /roles/bar.json\n /roles/baz.json\n /roles/foo.json\n/roles/self.json\n /roles/self.json\n")
- expect(stderr).to eq("WARNING: No knife configuration file found\n")
+ expect(stderr).to eq("WARNING: No knife configuration file found. See https://docs.chef.io/config_rb_knife.html for details.\n")
end
end
end
@@ -628,10 +624,10 @@ EOM
)
end
it "knife deps /data_bags/bag/item reports an error" do
- knife("deps --remote /data_bags/bag/item").should_fail(
+ knife("deps --remote /data_bags/bag/item.json").should_fail(
:exit_code => 2,
- :stdout => "/data_bags/bag/item\n",
- :stderr => "ERROR: /data_bags/bag/item: No such file or directory\n"
+ :stdout => "/data_bags/bag/item.json\n",
+ :stderr => "ERROR: /data_bags/bag/item.json: No such file or directory\n"
)
end
end
diff --git a/spec/integration/knife/diff_spec.rb b/spec/integration/knife/diff_spec.rb
index b7d2f4d1c3..b3bd23f48e 100644
--- a/spec/integration/knife/diff_spec.rb
+++ b/spec/integration/knife/diff_spec.rb
@@ -72,7 +72,7 @@ EOM
file "data_bags/x/y.json", {}
file "environments/_default.json", { "description" => "The default Chef environment" }
file "environments/x.json", {}
- file "nodes/x.json", {}
+ file "nodes/x.json", { "normal" => { "tags" => [] } }
file "roles/x.json", {}
file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
@@ -366,7 +366,7 @@ EOM
file "data_bags/x/y.json", {}
file "environments/_default.json", { "description" => "The default Chef environment" }
file "environments/x.json", {}
- file "nodes/x.json", {}
+ file "nodes/x.json", { "normal" => { "tags" => [] } }
file "roles/x.json", {}
file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
diff --git a/spec/integration/knife/download_spec.rb b/spec/integration/knife/download_spec.rb
index 39bf8d1c42..be0fc9d708 100644
--- a/spec/integration/knife/download_spec.rb
+++ b/spec/integration/knife/download_spec.rb
@@ -76,7 +76,7 @@ EOM
file "data_bags/x/y.json", {}
file "environments/_default.json", { "description" => "The default Chef environment" }
file "environments/x.json", {}
- file "nodes/x.json", {}
+ file "nodes/x.json", { "normal" => { "tags" => [] } }
file "roles/x.json", {}
file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
@@ -532,6 +532,25 @@ EOM
end
end
+ when_the_chef_server "has a role" do
+ before do
+ role "x", {}
+ end
+ when_the_repository "has the role in ruby" do
+ before do
+ file "roles/x.rb", <<EOM
+name "x"
+description "x"
+EOM
+ end
+
+ it "knife download refuses to change the role" do
+ knife("download /roles/x.json").should_succeed "", :stderr => "WARNING: /roles/x.rb cannot be updated (can't safely update ruby files).\n"
+ knife("diff --name-status /roles/x.json").should_succeed "M\t/roles/x.rb\n"
+ end
+ end
+ end
+
when_the_chef_server "has an environment" do
before do
environment "x", {}
@@ -626,7 +645,7 @@ EOM
file "data_bags/x/y.json", {}
file "environments/_default.json", { "description" => "The default Chef environment" }
file "environments/x.json", {}
- file "nodes/x.json", {}
+ file "nodes/x.json", { "normal" => { "tags" => [] } }
file "roles/x.json", {}
file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
@@ -1252,7 +1271,7 @@ EOM
file "groups/x.json", {}
file "invitations.json", [ "foo" ]
file "members.json", [ "bar" ]
- file "nodes/x.json", {}
+ file "nodes/x.json", { "normal" => { "tags" => [] } }
file "org.json", { "full_name" => "Something" }
file "policies/x-1.0.0.json", {}
file "policies/blah-1.0.0.json", {}
@@ -1276,7 +1295,7 @@ EOM
file "environments/x.json", { "description" => "foo" }
file "groups/x.json", { "description" => "foo" }
file "groups/x.json", { "groups" => [ "admin" ] }
- file "nodes/x.json", { "run_list" => [ "blah" ] }
+ file "nodes/x.json", { "normal" => { "tags" => [] }, "run_list" => [ "blah" ] }
file "org.json", { "full_name" => "Something Else " }
file "policies/x-1.0.0.json", { "run_list" => [ "blah" ] }
file "policy_groups/x.json", {
diff --git a/spec/integration/knife/environment_compare_spec.rb b/spec/integration/knife/environment_compare_spec.rb
new file mode 100644
index 0000000000..720bead8c0
--- /dev/null
+++ b/spec/integration/knife/environment_compare_spec.rb
@@ -0,0 +1,74 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife environment compare", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some environments" do
+ before do
+ cookbook "blah", "1.0.1"
+ cookbook "blah", "1.1.1"
+ cookbook "krad", "1.1.1"
+ environment "x", {
+ "cookbook_versions" => {
+ "blah" => "= 1.0.0",
+ "krad" => ">= 1.0.0",
+ },
+ }
+ environment "y", {
+ "cookbook_versions" => {
+ "blah" => "= 1.1.0",
+ "krad" => ">= 1.0.0",
+ },
+ }
+ end
+
+ # rubocop:disable Layout/TrailingWhitespace
+ it "displays the cookbooks for a single environment" do
+ knife("environment compare x").should_succeed <<EOM
+ x
+blah = 1.0.0
+krad >= 1.0.0
+
+EOM
+ end
+
+ it "compares the cookbooks for two environments" do
+ knife("environment compare x y").should_succeed <<EOM
+ x y
+blah = 1.0.0 = 1.1.0
+krad >= 1.0.0 >= 1.0.0
+
+EOM
+ end
+
+ it "compares the cookbooks for all environments" do
+ knife("environment compare --all").should_succeed <<EOM
+ x y
+blah = 1.0.0 = 1.1.0
+krad >= 1.0.0 >= 1.0.0
+
+EOM
+ end
+ # rubocop:enable Layout/TrailingWhitespace
+ end
+end
diff --git a/spec/integration/knife/environment_create_spec.rb b/spec/integration/knife/environment_create_spec.rb
new file mode 100644
index 0000000000..03fd4e63f7
--- /dev/null
+++ b/spec/integration/knife/environment_create_spec.rb
@@ -0,0 +1,40 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife environment create", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ let(:out) { "Created bah\n" }
+
+ when_the_chef_server "is empty" do
+ it "creates a new environment" do
+ knife("environment create bah").should_succeed out
+ end
+
+ it "refuses to add an existing environment" do
+ pending "Knife environment create must not blindly overwrite an existing environment"
+ knife("environment create bah").should_succeed out
+ expect { knife("environment create bah") }.to raise_error(Net::HTTPServerException)
+ end
+
+ end
+end
diff --git a/spec/integration/knife/environment_delete_spec.rb b/spec/integration/knife/environment_delete_spec.rb
new file mode 100644
index 0000000000..0f1fe5c4a8
--- /dev/null
+++ b/spec/integration/knife/environment_delete_spec.rb
@@ -0,0 +1,36 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife environment delete", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has an environment" do
+ before do
+ environment "y", {}
+ end
+
+ it "deletes an environment" do
+ knife("environment delete y", input: "y").should_succeed "Do you really want to delete y? (Y/N) Deleted y\n"
+ end
+
+ end
+end
diff --git a/spec/integration/knife/environment_from_file_spec.rb b/spec/integration/knife/environment_from_file_spec.rb
new file mode 100644
index 0000000000..67d4373939
--- /dev/null
+++ b/spec/integration/knife/environment_from_file_spec.rb
@@ -0,0 +1,115 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife environment from file", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ # include_context "default config options"
+
+ let (:env_dir) { "#{@repository_dir}/environments" }
+
+ when_the_chef_server "is empty" do
+ when_the_repository "has some environments" do
+ before do
+
+ file "environments/cons.json", <<EOM
+{
+ "name": "cons",
+ "description": "An environment",
+ "cookbook_versions": {
+
+ },
+ "json_class": "Chef::Environment",
+ "chef_type": "environment",
+ "default_attributes": {
+ "hola": "Amigos!"
+ },
+ "override_attributes": {
+
+ }
+}
+EOM
+
+ file "environments/car.json", <<EOM
+{
+ "name": "car",
+ "description": "An environment for list nodes",
+ "cookbook_versions": {
+
+ },
+ "json_class": "Chef::Environment",
+ "chef_type": "environment",
+ "default_attributes": {
+ "hola": "Amigos!"
+ },
+ "override_attributes": {
+
+ }
+}
+EOM
+
+ file "environments/cdr.json", <<EOM
+{
+ "name": "cdr",
+ "description": "An environment for last nodes",
+ "cookbook_versions": {
+
+ },
+ "json_class": "Chef::Environment",
+ "chef_type": "environment",
+ "default_attributes": {
+ "hola": "Amigos!"
+ },
+ "override_attributes": {
+
+ }
+}
+EOM
+
+ end
+
+ it "uploads a single file" do
+ knife("environment from file #{env_dir}/cons.json").should_succeed stderr: <<EOM
+Updated Environment cons
+EOM
+ end
+
+ it "uploads many files" do
+ knife("environment from file #{env_dir}/cons.json #{env_dir}/car.json #{env_dir}/cdr.json").should_succeed stderr: <<EOM
+Updated Environment cons
+Updated Environment car
+Updated Environment cdr
+EOM
+ end
+
+ it "uploads all environments in the repository" do
+ cwd(".")
+ knife("environment from file --all")
+ knife("environment list").should_succeed <<EOM
+_default
+car
+cdr
+cons
+EOM
+ end
+
+ end
+ end
+end
diff --git a/spec/integration/knife/environment_list_spec.rb b/spec/integration/knife/environment_list_spec.rb
new file mode 100644
index 0000000000..5e74453d1f
--- /dev/null
+++ b/spec/integration/knife/environment_list_spec.rb
@@ -0,0 +1,41 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife environment list", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some environments" do
+ before do
+ environment "b", {}
+ environment "y", {}
+ end
+
+ it "lists all the environments" do
+ knife("environment list").should_succeed <<EOM
+_default
+b
+y
+EOM
+ end
+
+ end
+end
diff --git a/spec/integration/knife/environment_show_spec.rb b/spec/integration/knife/environment_show_spec.rb
new file mode 100644
index 0000000000..dabdfac6a2
--- /dev/null
+++ b/spec/integration/knife/environment_show_spec.rb
@@ -0,0 +1,76 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife environment show", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some environments" do
+ before do
+ environment "b", {
+ "default_attributes" => { "foo" => "bar", "baz" => { "raz.my" => "mataz" } },
+ }
+ end
+
+ # rubocop:disable Layout/TrailingWhitespace
+ it "shows an environment" do
+ knife("environment show b").should_succeed <<EOM
+chef_type: environment
+cookbook_versions:
+default_attributes:
+ baz:
+ raz.my: mataz
+ foo: bar
+description:
+json_class: Chef::Environment
+name: b
+override_attributes:
+EOM
+ end
+ # rubocop:enable Layout/TrailingWhitespace
+
+ it "shows the requested attribute of an environment" do
+ knife("environment show b -a default_attributes").should_succeed <<EOM
+b:
+ default_attributes:
+ baz:
+ raz.my: mataz
+ foo: bar
+EOM
+ end
+
+ it "shows the requested nested attribute of an environment" do
+ knife("environment show b -a default_attributes.baz").should_succeed <<EON
+b:
+ default_attributes.baz:
+ raz.my: mataz
+EON
+ end
+
+ it "shows the requested attribute of an environment with custom field separator" do
+ knife("environment show b -S: -a default_attributes:baz").should_succeed <<EOT
+b:
+ default_attributes:baz:
+ raz.my: mataz
+EOT
+ end
+ end
+end
diff --git a/spec/integration/knife/list_spec.rb b/spec/integration/knife/list_spec.rb
index a5446dc08d..4aa74f3f0e 100644
--- a/spec/integration/knife/list_spec.rb
+++ b/spec/integration/knife/list_spec.rb
@@ -309,14 +309,6 @@ EOM
it "knife list /blarghle reports missing directory" do
knife("list /blarghle").should_fail "ERROR: /blarghle: No such file or directory\n"
end
-
- it "knife list /roles/blarghle reports missing directory" do
- knife("list /roles/blarghle").should_fail "ERROR: /roles/blarghle: No such file or directory\n"
- end
-
- it "knife list /roles/blarghle/blorghle reports missing directory" do
- knife("list /roles/blarghle/blorghle").should_fail "ERROR: /roles/blarghle/blorghle: No such file or directory\n"
- end
end
context "symlink tests" do
diff --git a/spec/integration/knife/node_bulk_delete_spec.rb b/spec/integration/knife/node_bulk_delete_spec.rb
new file mode 100644
index 0000000000..fa706cbd2b
--- /dev/null
+++ b/spec/integration/knife/node_bulk_delete_spec.rb
@@ -0,0 +1,51 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node bulk delete", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some nodes" do
+ before do
+ node "cons", {}
+ node "car", {}
+ node "cdr", {}
+ node "cat", {}
+ end
+
+ it "deletes all matching nodes" do
+ knife("node bulk delete ^ca.*", input: "Y").should_succeed <<EOM
+The following nodes will be deleted:
+
+car cat
+
+Are you sure you want to delete these nodes? (Y/N) Deleted node car
+Deleted node cat
+EOM
+
+ knife("node list").should_succeed <<EOM
+cdr
+cons
+EOM
+ end
+ end
+
+end
diff --git a/spec/integration/knife/node_create_spec.rb b/spec/integration/knife/node_create_spec.rb
new file mode 100644
index 0000000000..93a2f9ce6f
--- /dev/null
+++ b/spec/integration/knife/node_create_spec.rb
@@ -0,0 +1,46 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "openssl"
+
+describe "knife node create", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ let(:out) { "Created node[bah]\n" }
+
+ when_the_chef_server "is empty" do
+ it "creates a new node" do
+ knife("node create bah").should_succeed out
+ end
+
+ it "creates a new validator node" do
+ knife("node create bah").should_succeed out
+ knife("node show bah").should_succeed /Node Name: bah/
+ end
+
+ it "refuses to add an existing node" do
+ pending "Knife node create must not blindly overwrite an existing node"
+ knife("node create bah").should_succeed out
+ expect { knife("node create bah") }.to raise_error(Net::HTTPServerException)
+ end
+
+ end
+end
diff --git a/spec/integration/knife/node_delete_spec.rb b/spec/integration/knife/node_delete_spec.rb
new file mode 100644
index 0000000000..5d88af6d4f
--- /dev/null
+++ b/spec/integration/knife/node_delete_spec.rb
@@ -0,0 +1,47 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node delete", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some nodes" do
+ before do
+ node "cons", {}
+ node "car", {}
+ node "cdr", {}
+ node "cat", {}
+ end
+
+ it "deletes a node" do
+ knife("node delete car", input: "Y").should_succeed <<EOM
+Do you really want to delete car? (Y/N) Deleted node[car]
+EOM
+
+ knife("node list").should_succeed <<EOM
+cat
+cdr
+cons
+EOM
+ end
+
+ end
+end
diff --git a/spec/integration/knife/node_environment_set_spec.rb b/spec/integration/knife/node_environment_set_spec.rb
new file mode 100644
index 0000000000..10fec5723f
--- /dev/null
+++ b/spec/integration/knife/node_environment_set_spec.rb
@@ -0,0 +1,45 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node environment set", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has a node and an environment" do
+ before do
+ node "cons", {}
+ environment "lisp", {}
+ end
+
+ it "sets an environment on a node" do
+ knife("node environment set cons lisp").should_succeed /chef_environment:.*lisp/
+ knife("node show cons -a chef_environment").should_succeed <<EOM
+cons:
+ chef_environment: lisp
+EOM
+ end
+
+ it "with no environment" do
+ knife("node environment set adam").should_fail stderr: "FATAL: You must specify a node name and an environment.\n",
+ stdout: /^USAGE: knife node environment set NODE ENVIRONMENT\n/
+ end
+ end
+end
diff --git a/spec/integration/knife/node_from_file_spec.rb b/spec/integration/knife/node_from_file_spec.rb
new file mode 100644
index 0000000000..3430967a21
--- /dev/null
+++ b/spec/integration/knife/node_from_file_spec.rb
@@ -0,0 +1,58 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node from file", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ # include_context "default config options"
+
+ let (:node_dir) { "#{@repository_dir}/nodes" }
+
+ when_the_chef_server "is empty" do
+ when_the_repository "has some nodes" do
+ before do
+
+ file "nodes/cons.json", <<EOM
+{
+ "name": "cons",
+ "chef_environment": "_default",
+ "run_list": [
+ "recipe[cons]"
+]
+,
+ "normal": {
+ "tags": [
+
+ ]
+ }
+}
+EOM
+
+ end
+
+ it "uploads a single file" do
+ knife("node from file #{node_dir}/cons.json").should_succeed stderr: <<EOM
+Updated Node cons
+EOM
+ end
+
+ end
+ end
+end
diff --git a/spec/integration/knife/node_list_spec.rb b/spec/integration/knife/node_list_spec.rb
new file mode 100644
index 0000000000..76f5861e03
--- /dev/null
+++ b/spec/integration/knife/node_list_spec.rb
@@ -0,0 +1,44 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node list", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some nodes" do
+ before do
+ node "cons", {}
+ node "car", {}
+ node "cdr", {}
+ node "cat", {}
+ end
+
+ it "lists all cookbooks" do
+ knife("node list").should_succeed <<EOM
+car
+cat
+cdr
+cons
+EOM
+ end
+
+ end
+end
diff --git a/spec/integration/knife/node_run_list_add_spec.rb b/spec/integration/knife/node_run_list_add_spec.rb
new file mode 100644
index 0000000000..87d08e1975
--- /dev/null
+++ b/spec/integration/knife/node_run_list_add_spec.rb
@@ -0,0 +1,53 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node run list add", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has a node with no run_list" do
+ before do
+ node "cons", {}
+ end
+
+ it "sets the run list" do
+ knife("node run list add cons recipe[foo]").should_succeed /run_list:\s*recipe\[foo\]\n/
+ end
+ end
+
+ when_the_chef_server "has a node with a run_list" do
+ before do
+ node "cons", { run_list: ["recipe[bar]"] }
+ end
+
+ it "appends to the run list" do
+ knife("node run list add cons recipe[foo]").should_succeed /run_list:\n\s*recipe\[bar\]\n\s*recipe\[foo\]\n/m
+ end
+
+ it "adds to the run list before the specified item" do
+ knife("node run list add cons -b recipe[bar] recipe[foo]").should_succeed /run_list:\n\s*recipe\[foo\]\n\s*recipe\[bar\]\n/m
+ end
+
+ it "adds to the run list after the specified item" do
+ knife("node run list add cons -a recipe[bar] recipe[foo]").should_succeed /run_list:\n\s*recipe\[bar\]\n\s*recipe\[foo\]\n/m
+ end
+ end
+end
diff --git a/spec/integration/knife/node_run_list_remove_spec.rb b/spec/integration/knife/node_run_list_remove_spec.rb
new file mode 100644
index 0000000000..e85e3ed8e8
--- /dev/null
+++ b/spec/integration/knife/node_run_list_remove_spec.rb
@@ -0,0 +1,35 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node run list remove", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has a node with a run_list" do
+ before do
+ node "cons", { run_list: ["recipe[bar]", "recipe[foo]"] }
+ end
+
+ it "removes the item from the run list" do
+ knife("node run list remove cons recipe[bar]").should_succeed /run_list:\s*recipe\[foo\]\n/m
+ end
+ end
+end
diff --git a/spec/integration/knife/node_run_list_set_spec.rb b/spec/integration/knife/node_run_list_set_spec.rb
new file mode 100644
index 0000000000..ec6b08fb97
--- /dev/null
+++ b/spec/integration/knife/node_run_list_set_spec.rb
@@ -0,0 +1,40 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node run list set", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has a node with a run_list" do
+ before do
+ node "cons", { run_list: ["recipe[bar]", "recipe[foo]"] }
+ end
+
+ it "sets the run list" do
+ knife("node run list set cons recipe[bar]").should_succeed /run_list:\s*recipe\[bar\]\n/m
+ end
+
+ it "with no role or recipe" do
+ knife("node run list set cons").should_fail stderr: "FATAL: You must supply both a node name and a run list.\n",
+ stdout: /^USAGE: knife node run_list set NODE ENTRIES \(options\)/m
+ end
+ end
+end
diff --git a/spec/integration/knife/node_show_spec.rb b/spec/integration/knife/node_show_spec.rb
new file mode 100644
index 0000000000..dd890aed59
--- /dev/null
+++ b/spec/integration/knife/node_show_spec.rb
@@ -0,0 +1,35 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node show", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has a node with a run_list" do
+ before do
+ node "cons", { run_list: ["recipe[bar]", "recipe[foo]"] }
+ end
+
+ it "shows the node" do
+ knife("node show cons").should_succeed /Run List:\s*recipe\[bar\], recipe\[foo\]\n/m
+ end
+ end
+end
diff --git a/spec/integration/knife/raw_spec.rb b/spec/integration/knife/raw_spec.rb
index 9078bf09a1..5e0d3a3d11 100644
--- a/spec/integration/knife/raw_spec.rb
+++ b/spec/integration/knife/raw_spec.rb
@@ -49,7 +49,9 @@ describe "knife raw", :workstation do
},
"normal": {
+ "tags": [
+ ]
},
"default": {
diff --git a/spec/integration/knife/role_bulk_delete_spec.rb b/spec/integration/knife/role_bulk_delete_spec.rb
new file mode 100644
index 0000000000..0e7ff941e2
--- /dev/null
+++ b/spec/integration/knife/role_bulk_delete_spec.rb
@@ -0,0 +1,51 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife role bulk delete", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some roles" do
+ before do
+ role "cons", {}
+ role "car", {}
+ role "cdr", {}
+ role "cat", {}
+ end
+
+ it "deletes all matching roles" do
+ knife("role bulk delete ^ca.*", input: "Y").should_succeed <<EOM
+The following roles will be deleted:
+
+car cat
+
+Are you sure you want to delete these roles? (Y/N) Deleted role car
+Deleted role cat
+EOM
+
+ knife("role list").should_succeed <<EOM
+cdr
+cons
+EOM
+ end
+
+ end
+end
diff --git a/spec/integration/knife/role_create_spec.rb b/spec/integration/knife/role_create_spec.rb
new file mode 100644
index 0000000000..941eaf5cb6
--- /dev/null
+++ b/spec/integration/knife/role_create_spec.rb
@@ -0,0 +1,40 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife role create", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ let(:out) { "Created role[bah]\n" }
+
+ when_the_chef_server "is empty" do
+ it "creates a new role" do
+ knife("role create bah").should_succeed out
+ end
+
+ it "refuses to add an existing role" do
+ pending "Knife role create must not blindly overwrite an existing role"
+ knife("role create bah").should_succeed out
+ expect { knife("role create bah") }.to raise_error(Net::HTTPServerException)
+ end
+
+ end
+end
diff --git a/spec/integration/knife/role_delete_spec.rb b/spec/integration/knife/role_delete_spec.rb
new file mode 100644
index 0000000000..9fbd3758b9
--- /dev/null
+++ b/spec/integration/knife/role_delete_spec.rb
@@ -0,0 +1,47 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife role delete", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some roles" do
+ before do
+ role "cons", {}
+ role "car", {}
+ role "cdr", {}
+ role "cat", {}
+ end
+
+ it "deletes a role" do
+ knife("role delete car", input: "Y").should_succeed <<EOM
+Do you really want to delete car? (Y/N) Deleted role[car]
+EOM
+
+ knife("role list").should_succeed <<EOM
+cat
+cdr
+cons
+EOM
+ end
+
+ end
+end
diff --git a/spec/integration/knife/role_from_file_spec.rb b/spec/integration/knife/role_from_file_spec.rb
new file mode 100644
index 0000000000..60caa3fa88
--- /dev/null
+++ b/spec/integration/knife/role_from_file_spec.rb
@@ -0,0 +1,95 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife role from file", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ # include_context "default config options"
+
+ let (:role_dir) { "#{@repository_dir}/roles" }
+
+ when_the_chef_server "is empty" do
+ when_the_repository "has some roles" do
+ before do
+
+ file "roles/cons.json", <<EOM
+{
+ "name": "cons",
+ "description": "An role",
+ "json_class": "Chef::role",
+ "chef_type": "role",
+ "default_attributes": {
+ "hola": "Amigos!"
+ },
+ "override_attributes": {
+
+ }
+}
+EOM
+
+ file "roles/car.json", <<EOM
+{
+ "name": "car",
+ "description": "A role for list nodes",
+ "json_class": "Chef::Role",
+ "chef_type": "role",
+ "default_attributes": {
+ "hola": "Amigos!"
+ },
+ "override_attributes": {
+
+ }
+}
+EOM
+
+ file "roles/cdr.json", <<EOM
+{
+ "name": "cdr",
+ "description": "A role for last nodes",
+ "json_class": "Chef::Role",
+ "chef_type": "role",
+ "default_attributes": {
+ "hola": "Amigos!"
+ },
+ "override_attributes": {
+
+ }
+}
+EOM
+
+ end
+
+ it "uploads a single file" do
+ knife("role from file #{role_dir}/cons.json").should_succeed stderr: <<EOM
+Updated Role cons
+EOM
+ end
+
+ it "uploads many files" do
+ knife("role from file #{role_dir}/cons.json #{role_dir}/car.json #{role_dir}/cdr.json").should_succeed stderr: <<EOM
+Updated Role cons
+Updated Role car
+Updated Role cdr
+EOM
+ end
+
+ end
+ end
+end
diff --git a/spec/integration/knife/role_list_spec.rb b/spec/integration/knife/role_list_spec.rb
new file mode 100644
index 0000000000..36dc76be4c
--- /dev/null
+++ b/spec/integration/knife/role_list_spec.rb
@@ -0,0 +1,44 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife role list", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some roles" do
+ before do
+ role "cons", {}
+ role "car", {}
+ role "cdr", {}
+ role "cat", {}
+ end
+
+ it "lists all cookbooks" do
+ knife("role list").should_succeed <<EOM
+car
+cat
+cdr
+cons
+EOM
+ end
+
+ end
+end
diff --git a/spec/integration/knife/role_show_spec.rb b/spec/integration/knife/role_show_spec.rb
new file mode 100644
index 0000000000..f655c03e8d
--- /dev/null
+++ b/spec/integration/knife/role_show_spec.rb
@@ -0,0 +1,50 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife role show", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has some roles" do
+ before do
+ role "cons", {}
+ role "car", {}
+ role "cdr", {}
+ role "cat", {}
+ end
+
+ # rubocop:disable Layout/TrailingWhitespace
+ it "shows a cookbook" do
+ knife("role show cons").should_succeed <<EOM
+chef_type: role
+default_attributes:
+description:
+env_run_lists:
+json_class: Chef::Role
+name: cons
+override_attributes:
+run_list:
+EOM
+ end
+ # rubocop:enable Layout/TrailingWhitespace
+
+ end
+end
diff --git a/spec/integration/knife/search_node_spec.rb b/spec/integration/knife/search_node_spec.rb
new file mode 100644
index 0000000000..e3cda1a138
--- /dev/null
+++ b/spec/integration/knife/search_node_spec.rb
@@ -0,0 +1,39 @@
+#
+# Copyright:: Copyright 2013-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node show", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_chef_server "has a node with a run_list" do
+ before do
+ node "cons", { run_list: ["recipe[bar]", "recipe[foo]"] }
+ end
+
+ it "finds the node" do
+ knife("search node name:cons").should_succeed(/Node Name:\s*cons/, stderr: "1 items found\n\n")
+ end
+
+ it "does not find a node" do
+ knife("search node name:snoc").should_fail("", stderr: "0 items found\n\n", exit_code: 1)
+ end
+ end
+end
diff --git a/spec/integration/knife/serve_spec.rb b/spec/integration/knife/serve_spec.rb
index 72f0bb59ed..b0cdd8c070 100644
--- a/spec/integration/knife/serve_spec.rb
+++ b/spec/integration/knife/serve_spec.rb
@@ -24,33 +24,69 @@ describe "knife serve", :workstation do
include KnifeSupport
include AppServerSupport
+ def with_knife_serve
+ exception = nil
+ t = Thread.new do
+ begin
+ knife("serve --chef-zero-port=8890")
+ rescue
+ exception = $!
+ end
+ end
+ begin
+ Chef::Config.log_level = :debug
+ Chef::Config.chef_server_url = "http://localhost:8890"
+ Chef::Config.node_name = nil
+ Chef::Config.client_key = nil
+ api = Chef::ServerAPI.new
+ yield api
+ rescue
+ if exception
+ raise exception
+ else
+ raise
+ end
+ ensure
+ t.kill
+ sleep 0.5
+ end
+ end
+
when_the_repository "also has one of each thing" do
- before { file "nodes/x.json", { "foo" => "bar" } }
+ before do
+ file "nodes/a_node_in_json.json", { "foo" => "bar" }
+ file "nodes/a_node_in_ruby.rb", "name 'a_node_in_ruby'"
+ file "roles/a_role_in_json.json", { "foo" => "bar" }
+ file "roles/a_role_in_ruby.rb", "name 'a_role_in_ruby'"
+ end
- it "knife serve serves up /nodes/x" do
- exception = nil
- t = Thread.new do
- begin
- knife("serve --chef-zero-port=8890")
- rescue
- exception = $!
+ %w{a_node_in_json a_node_in_ruby}.each do |file_type|
+ context file_type do
+ it "knife serve serves up /nodes" do
+ with_knife_serve do |api|
+ expect(api.get("nodes")).to have_key(file_type)
+ end
+ end
+ it "knife serve serves up /nodes/#{file_type}" do
+ with_knife_serve do |api|
+ expect(api.get("nodes/#{file_type}")["name"]).to eq(file_type)
+ end
end
end
- begin
- Chef::Config.log_level = :debug
- Chef::Config.chef_server_url = "http://localhost:8890"
- Chef::Config.node_name = nil
- Chef::Config.client_key = nil
- api = Chef::ServerAPI.new
- expect(api.get("nodes/x")["name"]).to eq("x")
- rescue
- if exception
- raise exception
- else
- raise
+ end
+
+ %w{a_role_in_json a_role_in_ruby}.each do |file_type|
+ context file_type do
+ it "knife serve serves up /roles" do
+ with_knife_serve do |api|
+ expect(api.get("roles")).to have_key(file_type)
+ end
+ end
+ it "knife serve serves up /roles/#{file_type}" do
+ with_knife_serve do |api|
+ expect(api.get("roles/#{file_type}")["name"]).to eq(file_type)
+ end
end
- ensure
- t.kill
end
end
end
diff --git a/spec/integration/knife/upload_spec.rb b/spec/integration/knife/upload_spec.rb
index 038bbad216..dc713d02b3 100644
--- a/spec/integration/knife/upload_spec.rb
+++ b/spec/integration/knife/upload_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software, Inc.
+# Copyright:: Copyright 2013-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -96,7 +96,7 @@ EOM
file "data_bags/x/y.json", {}
file "environments/_default.json", { "description" => "The default Chef environment" }
file "environments/x.json", {}
- file "nodes/x.json", {}
+ file "nodes/x.json", { "normal" => { "tags" => [] } }
file "roles/x.json", {}
file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
@@ -154,21 +154,32 @@ EOM
end
end
- context "when cookbook metadata has a self-dependency" do
+ context "the role is in ruby" do
before do
- file "cookbooks/x/metadata.rb", "name 'x'; version '1.0.0'; depends 'x'"
+ file "roles/x.rb", <<EOM
+name "x"
+description "blargle"
+EOM
end
- it "should warn", chef: "< 13" do
- knife("upload /cookbooks").should_succeed(
- stdout: "Updated /cookbooks/x\n",
- stderr: "WARN: Ignoring self-dependency in cookbook x, please remove it (in the future this will be fatal).\n"
- )
+ it "knife upload changes the role" do
+ knife("upload /").should_succeed "Updated /roles/x.json\n"
knife("diff --name-status /").should_succeed ""
end
- it "should fail in Chef 13", chef: ">= 13" do
- knife("upload /cookbooks").should_fail ""
- # FIXME: include the error message here
+
+ it "knife upload --no-diff does not change the role" do
+ knife("upload --no-diff /").should_succeed ""
+ knife("diff --name-status /").should_succeed "M\t/roles/x.rb\n"
+ end
+ end
+
+ context "when cookbook metadata has a self-dependency" do
+ before do
+ file "cookbooks/x/metadata.rb", "name 'x'; version '1.0.0'; depends 'x'"
+ end
+
+ it "should fail in Chef 13" do
+ expect { knife("upload /cookbooks") }.to raise_error RuntimeError, /Cookbook depends on itself/
end
end
@@ -632,14 +643,14 @@ WARN: Parse error reading #{path_to('environments/x.json')} as JSON: parse error
ERROR: /environments/x.json failed to write: Parse error reading JSON: parse error: premature EOF
{
(right here) ------^
-EOH
+ EOH
warn = <<-EOH
WARN: Parse error reading #{path_to('environments/x.json')} as JSON: parse error: premature EOF
{
(right here) ------^
-EOH
+ EOH
knife("upload /environments/x.json").should_fail(error1)
knife("diff --name-status /environments/x.json").should_succeed("M\t/environments/x.json\n", :stderr => warn)
end
@@ -783,7 +794,7 @@ EOM
file "data_bags/x/y.json", {}
file "environments/_default.json", { "description" => "The default Chef environment" }
file "environments/x.json", {}
- file "nodes/x.json", {}
+ file "nodes/x.json", { "normal" => { "tags" => [] } }
file "roles/x.json", {}
file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
@@ -1045,7 +1056,7 @@ EOM
when_the_repository "has a modified, extra and missing file for the cookbook" do
before do
- file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0", '#modified')
+ file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0", "#modified")
file "cookbooks/x-1.0.0/y.rb", "hi"
end
@@ -1294,7 +1305,7 @@ EOM
file "invitations.json", [ "foo" ]
file "members.json", [ "bar" ]
file "org.json", { "full_name" => "wootles" }
- file "nodes/x.json", {}
+ file "nodes/x.json", { "normal" => { "tags" => [] } }
file "policies/x-1.0.0.json", {}
file "policies/blah-1.0.0.json", {}
file "policy_groups/x.json", { "policies" => { "x" => { "revision_id" => "1.0.0" }, "blah" => { "revision_id" => "1.0.0" } } }
diff --git a/spec/integration/recipes/accumulator_spec.rb b/spec/integration/recipes/accumulator_spec.rb
new file mode 100644
index 0000000000..4a193bd7f0
--- /dev/null
+++ b/spec/integration/recipes/accumulator_spec.rb
@@ -0,0 +1,232 @@
+require "support/shared/integration/integration_helper"
+require "chef/mixin/shell_out"
+
+describe "Accumulators" do
+ include IntegrationSupport
+ include Chef::Mixin::ShellOut
+
+ let(:chef_dir) { File.expand_path("../../../../bin", __FILE__) }
+
+ # Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the
+ # following constraints are satisfied:
+ # * Windows: windows can only run batch scripts as bare executables. Rubygems
+ # creates batch wrappers for installed gems, but we don't have batch wrappers
+ # in the source tree.
+ # * Other `chef-client` in PATH: A common case is running the tests on a
+ # machine that has omnibus chef installed. In that case we need to ensure
+ # we're running `chef-client` from the source tree and not the external one.
+ # cf. CHEF-4914
+ let(:chef_client) { "ruby '#{chef_dir}/chef-client' --minimal-ohai" }
+
+ let(:aliases_temppath) do
+ t = Tempfile.new("chef_accumulator_test")
+ path = t.path
+ t.close
+ t.unlink
+ path
+ end
+
+ when_the_repository "edit_resource-based delayed accumulators work" do
+ before do
+ directory "cookbooks/x" do
+ file "resources/email_alias.rb", <<-EOM
+ provides :email_alias
+ resource_name :email_alias
+
+ property :address, String, name_property: true, identity: true
+ property :recipients, Array
+
+ default_action :create
+
+ action :create do
+ with_run_context :root do
+ edit_resource(:template, "#{aliases_temppath}") do |new_resource|
+ source "aliases.erb"
+ variables[:aliases] ||= {}
+ variables[:aliases][new_resource.address] ||= []
+ variables[:aliases][new_resource.address] += new_resource.recipients
+ action :nothing
+ delayed_action :create
+ end
+ end
+ end
+ EOM
+
+ file "resources/nested.rb", <<-EOM
+ provides :nested
+ resource_name :nested
+
+ property :address, String, name_property: true, identity: true
+ property :recipients, Array
+
+ default_action :create
+
+ action :create do
+ email_alias new_resource.address do
+ recipients new_resource.recipients
+ end
+ end
+ EOM
+
+ file "resources/doubly_nested.rb", <<-EOM
+ provides :doubly_nested
+ resource_name :doubly_nested
+
+ property :address, String, name_property: true, identity: true
+ property :recipients, Array
+
+ default_action :create
+
+ action :create do
+ nested new_resource.address do
+ recipients new_resource.recipients
+ end
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ email_alias "outer1" do
+ recipients [ "out1a", "out1b" ]
+ end
+
+ nested "nested1" do
+ recipients [ "nested1a", "nested1b" ]
+ end
+
+ email_alias "outer2" do
+ recipients [ "out2a", "out2b" ]
+ end
+
+ doubly_nested "nested2" do
+ recipients [ "nested2a", "nested2b" ]
+ end
+
+ email_alias "outer3" do
+ recipients [ "out3a", "out3b" ]
+ end
+ EOM
+
+ file "templates/aliases.erb", <<-EOM.gsub(/^\s+/, "")
+ <%= pp @aliases %>
+ EOM
+ end # directory 'cookbooks/x'
+ end
+
+ it "should complete with success" do
+ file "config/client.rb", <<-EOM
+ local_mode true
+ cookbook_path "#{path_to('cookbooks')}"
+ log_level :warn
+ EOM
+
+ result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" --no-color -F doc -o 'x::default'", :cwd => chef_dir)
+ result.error!
+ # runs only a single template resource (in the outer run context, as a delayed resource)
+ expect(result.stdout.scan(/template\S+ action create/).size).to eql(1)
+ # hash order is insertion order in ruby >= 1.9, so this next line does test that all calls were in the correct order
+ expect(IO.read(aliases_temppath).chomp).to eql('{"outer1"=>["out1a", "out1b"], "nested1"=>["nested1a", "nested1b"], "outer2"=>["out2a", "out2b"], "nested2"=>["nested2a", "nested2b"], "outer3"=>["out3a", "out3b"]}')
+ end
+ end
+
+ when_the_repository "find_resource-based delayed accumulators work" do
+ before do
+ directory "cookbooks/x" do
+ file "resources/email_alias.rb", <<-EOM
+ provides :email_alias
+ resource_name :email_alias
+
+ property :address, String, name_property: true, identity: true
+ property :recipients, Array
+
+ default_action :create
+
+ action :create do
+ r = with_run_context :root do
+ find_resource(:template, "#{aliases_temppath}") do
+ source "aliases.erb"
+ variables[:aliases] = {}
+ action :nothing
+ delayed_action :create
+ end
+ end
+ r.variables[:aliases][new_resource.address] ||= []
+ r.variables[:aliases][new_resource.address] += new_resource.recipients
+ end
+ EOM
+
+ file "resources/nested.rb", <<-EOM
+ provides :nested
+ resource_name :nested
+
+ property :address, String, name_property: true, identity: true
+ property :recipients, Array
+
+ default_action :create
+
+ action :create do
+ email_alias new_resource.address do
+ recipients new_resource.recipients
+ end
+ end
+ EOM
+
+ file "resources/doubly_nested.rb", <<-EOM
+ provides :doubly_nested
+ resource_name :doubly_nested
+
+ property :address, String, name_property: true, identity: true
+ property :recipients, Array
+
+ default_action :create
+
+ action :create do
+ nested new_resource.address do
+ recipients new_resource.recipients
+ end
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ email_alias "outer1" do
+ recipients [ "out1a", "out1b" ]
+ end
+
+ nested "nested1" do
+ recipients [ "nested1a", "nested1b" ]
+ end
+
+ email_alias "outer2" do
+ recipients [ "out2a", "out2b" ]
+ end
+
+ doubly_nested "nested2" do
+ recipients [ "nested2a", "nested2b" ]
+ end
+
+ email_alias "outer3" do
+ recipients [ "out3a", "out3b" ]
+ end
+ EOM
+
+ file "templates/aliases.erb", <<-EOM.gsub(/^\s+/, "")
+ <%= pp @aliases %>
+ EOM
+ end # directory 'cookbooks/x'
+ end
+
+ it "should complete with success" do
+ file "config/client.rb", <<-EOM
+ local_mode true
+ cookbook_path "#{path_to('cookbooks')}"
+ log_level :warn
+ EOM
+
+ result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" --no-color -F doc -o 'x::default'", :cwd => chef_dir)
+ result.error!
+ # runs only a single template resource (in the outer run context, as a delayed resource)
+ expect(result.stdout.scan(/template\S+ action create/).size).to eql(1)
+ # hash order is insertion order in ruby >= 1.9, so this next line does test that all calls were in the correct order
+ expect(IO.read(aliases_temppath).chomp).to eql('{"outer1"=>["out1a", "out1b"], "nested1"=>["nested1a", "nested1b"], "outer2"=>["out2a", "out2b"], "nested2"=>["nested2a", "nested2b"], "outer3"=>["out3a", "out3b"]}')
+ end
+ end
+end
diff --git a/spec/integration/recipes/lwrp_inline_resources_spec.rb b/spec/integration/recipes/lwrp_inline_resources_spec.rb
index 46fd83423d..54ce94f263 100644
--- a/spec/integration/recipes/lwrp_inline_resources_spec.rb
+++ b/spec/integration/recipes/lwrp_inline_resources_spec.rb
@@ -19,14 +19,13 @@ describe "LWRPs with inline resources" do
let(:chef_client) { "ruby '#{chef_dir}/chef-client' --minimal-ohai" }
context "with a use_inline_resources provider with 'def action_a' instead of action :a" do
- class LwrpInlineResourcesTest < Chef::Resource::LWRPBase
+ class LwrpInlineResourcesTest < Chef::Resource
resource_name :lwrp_inline_resources_test
- actions :a, :nothing
+ allowed_actions :a, :nothing
default_action :a
property :ran_a
class Provider < Chef::Provider::LWRPBase
provides :lwrp_inline_resources_test
- use_inline_resources
def action_a
r = new_resource
ruby_block "run a" do
@@ -38,18 +37,18 @@ describe "LWRPs with inline resources" do
it "this is totally a bug, but for backcompat purposes, it adds the resources to the main resource collection and does not get marked updated" do
r = nil
- expect_recipe {
+ expect_recipe do
r = lwrp_inline_resources_test "hi"
- }.to have_updated("ruby_block[run a]", :run)
+ end.to have_updated("ruby_block[run a]", :run)
expect(r.ran_a).to eq "ran a"
end
end
context "with an inline resource with a property that shadows the enclosing provider's property" do
- class LwrpShadowedPropertyTest < Chef::Resource::LWRPBase
+ class LwrpShadowedPropertyTest < Chef::Resource
PATH = ::File.join(Dir.tmpdir, "shadow-property.txt")
use_automatic_resource_name
- actions :fiddle
+ allowed_actions :fiddle
property :content
action :fiddle do
file PATH do
@@ -64,25 +63,23 @@ describe "LWRPs with inline resources" do
# https://github.com/chef/chef/issues/4334
it "does not warn spuriously" do
expect(Chef::Log).to_not receive(:warn).with(/is declared in both/)
- expect_recipe {
+ expect_recipe do
lwrp_shadowed_property_test "fnord" do
action :fiddle
end
- }
+ end
end
end
context "with an inline_resources provider with two actions, one calling the other" do
- class LwrpInlineResourcesTest2 < Chef::Resource::LWRPBase
+ class LwrpInlineResourcesTest2 < Chef::Resource
resource_name :lwrp_inline_resources_test2
- actions :a, :b, :nothing
+ allowed_actions :a, :b, :nothing
default_action :b
property :ran_a
property :ran_b
class Provider < Chef::Provider::LWRPBase
provides :lwrp_inline_resources_test2
- use_inline_resources
-
action :a do
r = new_resource
ruby_block "run a" do
@@ -104,11 +101,11 @@ describe "LWRPs with inline resources" do
it "resources declared in b are executed immediately inline" do
r = nil
- expect_recipe {
+ expect_recipe do
r = lwrp_inline_resources_test2 "hi" do
action :b
end
- }.to have_updated("lwrp_inline_resources_test2[hi]", :b).
+ end.to have_updated("lwrp_inline_resources_test2[hi]", :b).
and have_updated("ruby_block[run a]", :run).
and have_updated("ruby_block[run b]", :run)
expect(r.ran_b).to eq "ran b: ran_a value was \"ran a\""
@@ -133,8 +130,7 @@ describe "LWRPs with inline resources" do
default_action :create
EOM
file "providers/my_machine.rb", <<-EOM
- use_inline_resources
- action :create do
+ action :create do
x_do_nothing 'a'
x_do_nothing 'b'
end
diff --git a/spec/integration/recipes/noop_resource_spec.rb b/spec/integration/recipes/noop_resource_spec.rb
index c8ff3e6b5e..db6b668553 100644
--- a/spec/integration/recipes/noop_resource_spec.rb
+++ b/spec/integration/recipes/noop_resource_spec.rb
@@ -4,20 +4,20 @@ describe "Resources with a no-op provider" do
include IntegrationSupport
context "with noop provider providing foo" do
- before(:context) {
+ before(:each) do
class NoOpFoo < Chef::Resource
resource_name "hi_there"
default_action :update
end
Chef::Provider::Noop.provides :hi_there
- }
+ end
it "does not blow up a run with a noop'd resource" do
- recipe = converge {
+ recipe = converge do
hi_there "blah" do
action :update
end
- }
+ end
expect(recipe.logged_warnings).to eq ""
end
end
diff --git a/spec/integration/recipes/notifies_spec.rb b/spec/integration/recipes/notifies_spec.rb
index 000f5e37bf..b008e4ade7 100644
--- a/spec/integration/recipes/notifies_spec.rb
+++ b/spec/integration/recipes/notifies_spec.rb
@@ -8,6 +8,37 @@ describe "notifications" do
let(:chef_dir) { File.expand_path("../../../../bin", __FILE__) }
let(:chef_client) { "ruby '#{chef_dir}/chef-client' --minimal-ohai" }
+ when_the_repository "notifies a nameless resource" do
+ before do
+ directory "cookbooks/x" do
+ file "recipes/default.rb", <<-EOM
+ apt_update do
+ action :nothing
+ end
+ log "foo" do
+ notifies :nothing, 'apt_update', :delayed
+ end
+ log "bar" do
+ notifies :nothing, 'apt_update[]', :delayed
+ end
+ EOM
+ end
+ end
+
+ it "should complete with success" do
+ file "config/client.rb", <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+log_level :warn
+EOM
+
+ result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" --no-color -F doc -o 'x::default'", :cwd => chef_dir)
+ # our delayed notification should run at the end of the parent run_context after the baz resource
+ expect(result.stdout).to match(/\* apt_update\[\] action nothing \(skipped due to action :nothing\)\s+\* log\[foo\] action write\s+\* log\[bar\] action write\s+\* apt_update\[\] action nothing \(skipped due to action :nothing\)/)
+ result.error!
+ end
+ end
+
when_the_repository "notifies delayed one" do
before do
directory "cookbooks/x" do
@@ -331,4 +362,33 @@ EOM
result.error!
end
end
+
+ when_the_repository "has resources that have arrays as the name" do
+ before do
+ directory "cookbooks/x" do
+ file "recipes/default.rb", <<-EOM
+ log [ "a", "b" ] do
+ action :nothing
+ end
+
+ log "doit" do
+ notifies :write, "log[a, b]"
+ end
+ EOM
+ end
+ end
+
+ it "notifying the resource should work" do
+ file "config/client.rb", <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+log_level :warn
+EOM
+
+ result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" --no-color -F doc -o 'x::default'", :cwd => chef_dir)
+ expect(result.stdout).to match /\* log\[a, b\] action write/
+ result.error!
+ end
+
+ end
end
diff --git a/spec/integration/recipes/provider_choice.rb b/spec/integration/recipes/provider_choice.rb
index a9fc06038a..1895d93891 100644
--- a/spec/integration/recipes/provider_choice.rb
+++ b/spec/integration/recipes/provider_choice.rb
@@ -26,9 +26,9 @@ describe "Recipe DSL methods" do
end
it "provider_thingy 'blah' runs the provider and warns" do
- recipe = converge {
- provider_thingy "blah" do; end
- }
+ recipe = converge do
+ provider_thingy("blah") {}
+ end
expect(recipe.logged_warnings).to match /hello from Chef::Provider::ProviderThingy/
expect(recipe.logged_warnings).to match /you must use 'provides' to provide DSL/i
end
diff --git a/spec/integration/recipes/recipe_dsl_spec.rb b/spec/integration/recipes/recipe_dsl_spec.rb
index f96be7ea91..0250786f0e 100644
--- a/spec/integration/recipes/recipe_dsl_spec.rb
+++ b/spec/integration/recipes/recipe_dsl_spec.rb
@@ -12,7 +12,7 @@ describe "Recipe DSL methods" do
before { Namer.current_index += 1 }
context "with resource 'base_thingy' declared as BaseThingy" do
- before(:context) {
+ before(:each) do
class BaseThingy < Chef::Resource
resource_name "base_thingy"
@@ -43,7 +43,7 @@ describe "Recipe DSL methods" do
module RecipeDSLSpecNamespace; end
module RecipeDSLSpecNamespace::Bar; end
- }
+ end
before :each do
BaseThingy.created_resource = nil
@@ -51,29 +51,38 @@ describe "Recipe DSL methods" do
end
it "creates base_thingy when you call base_thingy in a recipe" do
- recipe = converge {
- base_thingy "blah" do; end
- }
+ recipe = converge do
+ base_thingy("blah") {}
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_name).to eq "blah"
expect(BaseThingy.created_resource).to eq BaseThingy
end
- it "errors out when you call base_thingy do ... end in a recipe" do
- expect_converge {
- base_thingy do; end
- }.to raise_error(ArgumentError, "You must supply a name when declaring a base_thingy resource")
+ it "errors when you call base_thingy do ... end in a recipe" do
+ expect_converge do
+ base_thingy { ; }
+ end.to raise_error(Chef::Exceptions::ValidationFailed)
end
- it "emits a warning when you call base_thingy 'foo', 'bar' do ... end in a recipe" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- recipe = converge {
- base_thingy "foo", "bar" do
+ context "nameless resources" do
+ before(:each) do
+ class NamelessThingy < BaseThingy
+ resource_name :nameless_thingy
+ provides :nameless_thingy
+
+ property :name, String, default: ""
end
- }
- expect(recipe.logged_warnings).to match(/Cannot create resource base_thingy with more than one argument. All arguments except the name \("foo"\) will be ignored. This will cause an error in Chef 13. Arguments: \["foo", "bar"\]/)
- expect(BaseThingy.created_name).to eq "foo"
- expect(BaseThingy.created_resource).to eq BaseThingy
+ end
+
+ it "does not error when not given a name" do
+ recipe = converge do
+ nameless_thingy {}
+ end
+ expect(recipe.logged_warnings).to eq ""
+ expect(BaseThingy.created_name).to eq ""
+ expect(BaseThingy.created_resource).to eq NamelessThingy
+ end
end
context "Deprecated automatic resource DSL" do
@@ -81,133 +90,87 @@ describe "Recipe DSL methods" do
Chef::Config[:treat_deprecation_warnings_as_errors] = false
end
- context "with a resource 'backcompat_thingy' declared in Chef::Resource and Chef::Provider" do
- before(:context) {
-
- class Chef::Resource::BackcompatThingy < Chef::Resource
- default_action :create
- end
- class Chef::Provider::BackcompatThingy < Chef::Provider
- def load_current_resource
- end
-
- def action_create
- BaseThingy.created_resource = new_resource.class
- BaseThingy.created_provider = self.class
- end
- end
-
- }
-
- it "backcompat_thingy creates a Chef::Resource::BackcompatThingy" do
- recipe = converge {
- backcompat_thingy "blah" do; end
- }
- expect(BaseThingy.created_resource).to eq Chef::Resource::BackcompatThingy
- expect(BaseThingy.created_provider).to eq Chef::Provider::BackcompatThingy
- end
-
- context "and another resource 'backcompat_thingy' in BackcompatThingy with 'provides'" do
- before(:context) {
-
- class RecipeDSLSpecNamespace::BackcompatThingy < BaseThingy
- provides :backcompat_thingy
- resource_name :backcompat_thingy
- end
-
- }
-
- it "backcompat_thingy creates a BackcompatThingy" do
- recipe = converge {
- backcompat_thingy "blah" do; end
- }
- expect(recipe.logged_warnings).to match(/Class Chef::Provider::BackcompatThingy does not declare 'provides :backcompat_thingy'./)
- expect(BaseThingy.created_resource).not_to be_nil
- end
- end
- end
-
context "with a resource named RecipeDSLSpecNamespace::Bar::BarThingy" do
- before(:context) {
+ before(:each) do
class RecipeDSLSpecNamespace::Bar::BarThingy < BaseThingy
end
- }
+ end
it "bar_thingy does not work" do
- expect_converge {
- bar_thingy "blah" do; end
- }.to raise_error(NoMethodError)
+ expect_converge do
+ bar_thingy("blah") {}
+ end.to raise_error(NoMethodError)
end
end
context "with a resource named Chef::Resource::NoNameThingy with resource_name nil" do
- before(:context) {
+ before(:each) do
class Chef::Resource::NoNameThingy < BaseThingy
resource_name nil
end
- }
+ end
it "no_name_thingy does not work" do
- expect_converge {
- no_name_thingy "blah" do; end
- }.to raise_error(NoMethodError)
+ expect_converge do
+ no_name_thingy("blah") {}
+ end.to raise_error(NoMethodError)
end
end
context "with a resource named AnotherNoNameThingy with resource_name :another_thingy_name" do
- before(:context) {
+ before(:each) do
class AnotherNoNameThingy < BaseThingy
resource_name :another_thingy_name
end
- }
+ end
it "another_no_name_thingy does not work" do
- expect_converge {
- another_no_name_thingy "blah" do; end
- }.to raise_error(NoMethodError)
+ expect_converge do
+ another_no_name_thingy("blah") {}
+ end.to raise_error(NoMethodError)
end
it "another_thingy_name works" do
- recipe = converge {
- another_thingy_name "blah" do; end
- }
+ recipe = converge do
+ another_thingy_name("blah") {}
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq(AnotherNoNameThingy)
end
end
context "with a resource named AnotherNoNameThingy2 with resource_name :another_thingy_name2; resource_name :another_thingy_name3" do
- before(:context) {
+ before(:each) do
class AnotherNoNameThingy2 < BaseThingy
resource_name :another_thingy_name2
resource_name :another_thingy_name3
end
- }
+ end
it "another_no_name_thingy does not work" do
- expect_converge {
- another_no_name_thingy2 "blah" do; end
- }.to raise_error(NoMethodError)
+ expect_converge do
+ another_no_name_thingy2("blah") {}
+ end.to raise_error(NoMethodError)
end
it "another_thingy_name2 does not work" do
- expect_converge {
- another_thingy_name2 "blah" do; end
- }.to raise_error(NoMethodError)
+ expect_converge do
+ another_thingy_name2("blah") {}
+ end.to raise_error(NoMethodError)
end
it "yet_another_thingy_name3 works" do
- recipe = converge {
- another_thingy_name3 "blah" do; end
- }
+ recipe = converge do
+ another_thingy_name3("blah") {}
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq(AnotherNoNameThingy2)
end
@@ -215,36 +178,36 @@ describe "Recipe DSL methods" do
context "provides overriding resource_name" do
context "with a resource named AnotherNoNameThingy3 with provides :another_no_name_thingy3, os: 'blarghle'" do
- before(:context) {
+ before(:each) do
class AnotherNoNameThingy3 < BaseThingy
resource_name :another_no_name_thingy_3
provides :another_no_name_thingy3, os: "blarghle"
end
- }
+ end
it "and os = linux, another_no_name_thingy3 does not work" do
- expect_converge {
+ expect_converge do
# TODO this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "linux"
- another_no_name_thingy3 "blah" do; end
- }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ another_no_name_thingy3("blah") {}
+ end.to raise_error(Chef::Exceptions::NoSuchResourceType)
end
it "and os = blarghle, another_no_name_thingy3 works" do
- recipe = converge {
+ recipe = converge do
# TODO this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "blarghle"
- another_no_name_thingy3 "blah" do; end
- }
+ another_no_name_thingy3("blah") {}
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy3)
end
end
context "with a resource named AnotherNoNameThingy4 with two provides" do
- before(:context) {
+ before(:each) do
class AnotherNoNameThingy4 < BaseThingy
resource_name :another_no_name_thingy_4
@@ -252,183 +215,183 @@ describe "Recipe DSL methods" do
provides :another_no_name_thingy4, platform_family: "foo"
end
- }
+ end
it "and os = linux, another_no_name_thingy4 does not work" do
- expect_converge {
+ expect_converge do
# TODO this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "linux"
- another_no_name_thingy4 "blah" do; end
- }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ another_no_name_thingy4("blah") {}
+ end.to raise_error(Chef::Exceptions::NoSuchResourceType)
end
it "and os = blarghle, another_no_name_thingy4 works" do
- recipe = converge {
+ recipe = converge do
# TODO this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "blarghle"
- another_no_name_thingy4 "blah" do; end
- }
+ another_no_name_thingy4("blah") {}
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy4)
end
it "and platform_family = foo, another_no_name_thingy4 works" do
- recipe = converge {
+ recipe = converge do
# TODO this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:platform_family] = "foo"
- another_no_name_thingy4 "blah" do; end
- }
+ another_no_name_thingy4("blah") {}
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy4)
end
end
context "with a resource named AnotherNoNameThingy5, a different resource_name, and a provides with the original resource_name" do
- before(:context) {
+ before(:each) do
class AnotherNoNameThingy5 < BaseThingy
resource_name :another_thingy_name_for_another_no_name_thingy5
provides :another_no_name_thingy5, os: "blarghle"
end
- }
+ end
it "and os = linux, another_no_name_thingy5 does not work" do
- expect_converge {
+ expect_converge do
# this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "linux"
- another_no_name_thingy5 "blah" do; end
- }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ another_no_name_thingy5("blah") {}
+ end.to raise_error(Chef::Exceptions::NoSuchResourceType)
end
it "and os = blarghle, another_no_name_thingy5 works" do
- recipe = converge {
+ recipe = converge do
# this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "blarghle"
- another_no_name_thingy5 "blah" do; end
- }
+ another_no_name_thingy5("blah") {}
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy5)
end
it "the new resource name can be used in a recipe" do
- recipe = converge {
- another_thingy_name_for_another_no_name_thingy5 "blah" do; end
- }
+ recipe = converge do
+ another_thingy_name_for_another_no_name_thingy5("blah") {}
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy5)
end
end
context "with a resource named AnotherNoNameThingy6, a provides with the original resource name, and a different resource_name" do
- before(:context) {
+ before(:each) do
class AnotherNoNameThingy6 < BaseThingy
provides :another_no_name_thingy6, os: "blarghle"
resource_name :another_thingy_name_for_another_no_name_thingy6
end
- }
+ end
it "and os = linux, another_no_name_thingy6 does not work" do
- expect_converge {
+ expect_converge do
# this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "linux"
- another_no_name_thingy6 "blah" do; end
- }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ another_no_name_thingy6("blah") {}
+ end.to raise_error(Chef::Exceptions::NoSuchResourceType)
end
it "and os = blarghle, another_no_name_thingy6 works" do
- recipe = converge {
+ recipe = converge do
# this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "blarghle"
- another_no_name_thingy6 "blah" do; end
- }
+ another_no_name_thingy6("blah") {}
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy6)
end
it "the new resource name can be used in a recipe" do
- recipe = converge {
- another_thingy_name_for_another_no_name_thingy6 "blah" do; end
- }
+ recipe = converge do
+ another_thingy_name_for_another_no_name_thingy6("blah") {}
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy6)
end
end
context "with a resource named AnotherNoNameThingy7, a new resource_name, and provides with that new resource name" do
- before(:context) {
+ before(:each) do
class AnotherNoNameThingy7 < BaseThingy
resource_name :another_thingy_name_for_another_no_name_thingy7
provides :another_thingy_name_for_another_no_name_thingy7, os: "blarghle"
end
- }
+ end
it "and os = linux, another_thingy_name_for_another_no_name_thingy7 does not work" do
- expect_converge {
+ expect_converge do
# this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "linux"
- another_thingy_name_for_another_no_name_thingy7 "blah" do; end
- }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ another_thingy_name_for_another_no_name_thingy7("blah") {}
+ end.to raise_error(Chef::Exceptions::NoSuchResourceType)
end
it "and os = blarghle, another_thingy_name_for_another_no_name_thingy7 works" do
- recipe = converge {
+ recipe = converge do
# this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "blarghle"
- another_thingy_name_for_another_no_name_thingy7 "blah" do; end
- }
+ another_thingy_name_for_another_no_name_thingy7("blah") {}
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy7)
end
it "the old resource name does not work" do
- expect_converge {
+ expect_converge do
# this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "linux"
- another_no_name_thingy_7 "blah" do; end
- }.to raise_error(NoMethodError)
+ another_no_name_thingy_7("blah") {}
+ end.to raise_error(NoMethodError)
end
end
# opposite order from the previous test (provides, then resource_name)
context "with a resource named AnotherNoNameThingy8, a provides with a new resource name, and resource_name with that new resource name" do
- before(:context) {
+ before(:each) do
class AnotherNoNameThingy8 < BaseThingy
provides :another_thingy_name_for_another_no_name_thingy8, os: "blarghle"
resource_name :another_thingy_name_for_another_no_name_thingy8
end
- }
+ end
it "and os = linux, another_thingy_name_for_another_no_name_thingy8 does not work" do
- expect_converge {
+ expect_converge do
# this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "linux"
- another_thingy_name_for_another_no_name_thingy8 "blah" do; end
- }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ another_thingy_name_for_another_no_name_thingy8("blah") {}
+ end.to raise_error(Chef::Exceptions::NoSuchResourceType)
end
it "and os = blarghle, another_thingy_name_for_another_no_name_thingy8 works" do
- recipe = converge {
+ recipe = converge do
# this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "blarghle"
- another_thingy_name_for_another_no_name_thingy8 "blah" do; end
- }
+ another_thingy_name_for_another_no_name_thingy8("blah") {}
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy8)
end
it "the old resource name does not work" do
- expect_converge {
+ expect_converge do
# this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "linux"
- another_thingy_name8 "blah" do; end
- }.to raise_error(NoMethodError)
+ another_thingy_name8("blah") {}
+ end.to raise_error(NoMethodError)
end
end
end
@@ -436,74 +399,74 @@ describe "Recipe DSL methods" do
context "provides" do
context "when MySupplier provides :hemlock" do
- before(:context) {
+ before(:each) do
class RecipeDSLSpecNamespace::MySupplier < BaseThingy
resource_name :hemlock
end
- }
+ end
it "my_supplier does not work in a recipe" do
- expect_converge {
- my_supplier "blah" do; end
- }.to raise_error(NoMethodError)
+ expect_converge do
+ my_supplier("blah") {}
+ end.to raise_error(NoMethodError)
end
it "hemlock works in a recipe" do
- expect_recipe {
- hemlock "blah" do; end
- }.to emit_no_warnings_or_errors
+ expect_recipe do
+ hemlock("blah") {}
+ end.to emit_no_warnings_or_errors
expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::MySupplier
end
end
context "when Thingy3 has resource_name :thingy3" do
- before(:context) {
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy3 < BaseThingy
resource_name :thingy3
end
- }
+ end
it "thingy3 works in a recipe" do
- expect_recipe {
- thingy3 "blah" do; end
- }.to emit_no_warnings_or_errors
+ expect_recipe do
+ thingy3("blah") {}
+ end.to emit_no_warnings_or_errors
expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
end
context "and Thingy4 has resource_name :thingy3" do
- before(:context) {
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy4 < BaseThingy
resource_name :thingy3
end
- }
+ end
- it "thingy3 works in a recipe and yields Thingy3 (the alphabetical one)" do
- recipe = converge {
- thingy3 "blah" do; end
- }
- expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
+ it "thingy3 works in a recipe and yields Thingy4 (the last one)" do
+ recipe = converge do
+ thingy3("blah") {}
+ end
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy4
end
it "thingy4 does not work in a recipe" do
- expect_converge {
- thingy4 "blah" do; end
- }.to raise_error(NoMethodError)
+ expect_converge do
+ thingy4("blah") {}
+ end.to raise_error(NoMethodError)
end
it "resource_matching_short_name returns Thingy4" do
- expect(Chef::Resource.resource_matching_short_name(:thingy3)).to eq RecipeDSLSpecNamespace::Thingy3
+ expect(Chef::Resource.resource_matching_short_name(:thingy3)).to eq RecipeDSLSpecNamespace::Thingy4
end
end
end
context "when Thingy5 has resource_name :thingy5 and provides :thingy5reverse, :thingy5_2 and :thingy5_2reverse" do
- before(:context) {
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy5 < BaseThingy
resource_name :thingy5
@@ -512,62 +475,62 @@ describe "Recipe DSL methods" do
provides :thingy5_2reverse
end
- }
+ end
it "thingy5 works in a recipe" do
- expect_recipe {
- thingy5 "blah" do; end
- }.to emit_no_warnings_or_errors
+ expect_recipe do
+ thingy5("blah") {}
+ end.to emit_no_warnings_or_errors
expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy5
end
context "and Thingy6 provides :thingy5" do
- before(:context) {
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy6 < BaseThingy
resource_name :thingy6
provides :thingy5
end
- }
+ end
it "thingy6 works in a recipe and yields Thingy6" do
- recipe = converge {
- thingy6 "blah" do; end
- }
+ recipe = converge do
+ thingy6("blah") {}
+ end
expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy6
end
- it "thingy5 works in a recipe and yields Foo::Thingy5 (the alphabetical one)" do
- recipe = converge {
- thingy5 "blah" do; end
- }
- expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy5
+ it "thingy5 works in a recipe and yields Foo::Thingy6 (the last one)" do
+ recipe = converge do
+ thingy5("blah") {}
+ end
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy6
end
- it "resource_matching_short_name returns Thingy5" do
+ it "resource_matching_short_name returns Thingy6" do
expect(Chef::Resource.resource_matching_short_name(:thingy5)).to eq RecipeDSLSpecNamespace::Thingy5
end
context "and AThingy5 provides :thingy5reverse" do
- before(:context) {
+ before(:each) do
class RecipeDSLSpecNamespace::AThingy5 < BaseThingy
resource_name :thingy5reverse
end
- }
+ end
it "thingy5reverse works in a recipe and yields AThingy5 (the alphabetical one)" do
- recipe = converge {
- thingy5reverse "blah" do; end
- }
+ recipe = converge do
+ thingy5reverse("blah") {}
+ end
expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::AThingy5
end
end
context "and ZRecipeDSLSpecNamespace::Thingy5 provides :thingy5_2" do
- before(:context) {
+ before(:each) do
module ZRecipeDSLSpecNamespace
class Thingy5 < BaseThingy
@@ -575,18 +538,18 @@ describe "Recipe DSL methods" do
end
end
- }
+ end
- it "thingy5_2 works in a recipe and yields the RecipeDSLSpaceNamespace one (the alphabetical one)" do
- recipe = converge {
- thingy5_2 "blah" do; end
- }
- expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy5
+ it "thingy5_2 works in a recipe and yields the ZRecipeDSLSpaceNamespace one (the last one)" do
+ recipe = converge do
+ thingy5_2("blah") {}
+ end
+ expect(BaseThingy.created_resource).to eq ZRecipeDSLSpecNamespace::Thingy5
end
end
context "and ARecipeDSLSpecNamespace::Thingy5 provides :thingy5_2" do
- before(:context) {
+ before(:each) do
module ARecipeDSLSpecNamespace
class Thingy5 < BaseThingy
@@ -594,84 +557,84 @@ describe "Recipe DSL methods" do
end
end
- }
+ end
it "thingy5_2reverse works in a recipe and yields the ARecipeDSLSpaceNamespace one (the alphabetical one)" do
- recipe = converge {
- thingy5_2reverse "blah" do; end
- }
+ recipe = converge do
+ thingy5_2reverse("blah") {}
+ end
expect(BaseThingy.created_resource).to eq ARecipeDSLSpecNamespace::Thingy5
end
end
end
context "when Thingy3 has resource_name :thingy3" do
- before(:context) {
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy3 < BaseThingy
resource_name :thingy3
end
- }
+ end
it "thingy3 works in a recipe" do
- expect_recipe {
- thingy3 "blah" do; end
- }.to emit_no_warnings_or_errors
+ expect_recipe do
+ thingy3("blah") {}
+ end.to emit_no_warnings_or_errors
expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
end
context "and Thingy4 has resource_name :thingy3" do
- before(:context) {
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy4 < BaseThingy
resource_name :thingy3
end
- }
+ end
- it "thingy3 works in a recipe and yields Thingy3 (the alphabetical one)" do
- recipe = converge {
- thingy3 "blah" do; end
- }
- expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
+ it "thingy3 works in a recipe and yields Thingy4 (the last one)" do
+ recipe = converge do
+ thingy3("blah") {}
+ end
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy4
end
it "thingy4 does not work in a recipe" do
- expect_converge {
- thingy4 "blah" do; end
- }.to raise_error(NoMethodError)
+ expect_converge do
+ thingy4("blah") {}
+ end.to raise_error(NoMethodError)
end
it "resource_matching_short_name returns Thingy4" do
- expect(Chef::Resource.resource_matching_short_name(:thingy3)).to eq RecipeDSLSpecNamespace::Thingy3
+ expect(Chef::Resource.resource_matching_short_name(:thingy3)).to eq RecipeDSLSpecNamespace::Thingy4
end
end
context "and Thingy4 has resource_name :thingy3" do
- before(:context) {
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy4 < BaseThingy
resource_name :thingy3
end
- }
+ end
- it "thingy3 works in a recipe and yields Thingy3 (the alphabetical one)" do
- recipe = converge {
- thingy3 "blah" do; end
- }
- expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
+ it "thingy3 works in a recipe and yields Thingy4 (the last one)" do
+ recipe = converge do
+ thingy3("blah") {}
+ end
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy4
end
it "thingy4 does not work in a recipe" do
- expect_converge {
- thingy4 "blah" do; end
- }.to raise_error(NoMethodError)
+ expect_converge do
+ thingy4("blah") {}
+ end.to raise_error(NoMethodError)
end
it "resource_matching_short_name returns Thingy4" do
- expect(Chef::Resource.resource_matching_short_name(:thingy3)).to eq RecipeDSLSpecNamespace::Thingy3
+ expect(Chef::Resource.resource_matching_short_name(:thingy3)).to eq RecipeDSLSpecNamespace::Thingy4
end
end
end
@@ -679,36 +642,36 @@ describe "Recipe DSL methods" do
end
context "when Thingy7 provides :thingy8" do
- before(:context) {
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy7 < BaseThingy
resource_name :thingy7
provides :thingy8
end
- }
+ end
context "and Thingy8 has resource_name :thingy8" do
- before(:context) {
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy8 < BaseThingy
resource_name :thingy8
end
- }
+ end
it "thingy7 works in a recipe and yields Thingy7" do
- recipe = converge {
- thingy7 "blah" do; end
- }
+ recipe = converge do
+ thingy7("blah") {}
+ end
expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy7
end
- it "thingy8 works in a recipe and yields Thingy7 (alphabetical)" do
- recipe = converge {
- thingy8 "blah" do; end
- }
- expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy7
+ it "thingy8 works in a recipe and yields Thingy7 (last)" do
+ recipe = converge do
+ thingy8("blah") {}
+ end
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy8
end
it "resource_matching_short_name returns Thingy8" do
@@ -718,7 +681,7 @@ describe "Recipe DSL methods" do
end
context "when Thingy12 provides :thingy12, :twizzle and :twizzle2" do
- before(:context) {
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy12 < BaseThingy
resource_name :thingy12
@@ -726,32 +689,32 @@ describe "Recipe DSL methods" do
provides :twizzle2
end
- }
+ end
it "thingy12 works in a recipe and yields Thingy12" do
- expect_recipe {
- thingy12 "blah" do; end
- }.to emit_no_warnings_or_errors
+ expect_recipe do
+ thingy12("blah") {}
+ end.to emit_no_warnings_or_errors
expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy12
end
it "twizzle works in a recipe and yields Thingy12" do
- expect_recipe {
- twizzle "blah" do; end
- }.to emit_no_warnings_or_errors
+ expect_recipe do
+ twizzle("blah") {}
+ end.to emit_no_warnings_or_errors
expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy12
end
it "twizzle2 works in a recipe and yields Thingy12" do
- expect_recipe {
- twizzle2 "blah" do; end
- }.to emit_no_warnings_or_errors
+ expect_recipe do
+ twizzle2("blah") {}
+ end.to emit_no_warnings_or_errors
expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy12
end
end
context "with platform-specific resources 'my_super_thingy_foo' and 'my_super_thingy_bar'" do
- before(:context) {
+ before(:each) do
class MySuperThingyFoo < BaseThingy
resource_name :my_super_thingy_foo
provides :my_super_thingy, platform: "foo"
@@ -761,14 +724,14 @@ describe "Recipe DSL methods" do
resource_name :my_super_thingy_bar
provides :my_super_thingy, platform: "bar"
end
- }
+ end
it "A run with platform 'foo' uses MySuperThingyFoo" do
r = Cheffish::ChefRun.new(chef_config)
r.client.run_context.node.automatic["platform"] = "foo"
- r.compile_recipe {
- my_super_thingy "blah" do; end
- }
+ r.compile_recipe do
+ my_super_thingy("blah") {}
+ end
r.converge
expect(r).to emit_no_warnings_or_errors
expect(BaseThingy.created_resource).to eq MySuperThingyFoo
@@ -777,9 +740,9 @@ describe "Recipe DSL methods" do
it "A run with platform 'bar' uses MySuperThingyBar" do
r = Cheffish::ChefRun.new(chef_config)
r.client.run_context.node.automatic["platform"] = "bar"
- r.compile_recipe {
- my_super_thingy "blah" do; end
- }
+ r.compile_recipe do
+ my_super_thingy("blah") {}
+ end
r.converge
expect(r).to emit_no_warnings_or_errors
expect(BaseThingy.created_resource).to eq MySuperThingyBar
@@ -788,20 +751,20 @@ describe "Recipe DSL methods" do
it "A run with platform 'x' reports that my_super_thingy is not supported" do
r = Cheffish::ChefRun.new(chef_config)
r.client.run_context.node.automatic["platform"] = "x"
- expect {
- r.compile_recipe {
- my_super_thingy "blah" do; end
- }
- }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ expect do
+ r.compile_recipe do
+ my_super_thingy("blah") {}
+ end
+ end.to raise_error(Chef::Exceptions::NoSuchResourceType)
end
end
context "when Thingy10 provides :thingy10" do
- before(:context) {
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy10 < BaseThingy
resource_name :thingy10
end
- }
+ end
it "declaring a resource providing the same :thingy10 with override: true does not produce a warning" do
expect(Chef::Log).not_to receive(:warn)
@@ -812,11 +775,11 @@ describe "Recipe DSL methods" do
end
context "when Thingy11 provides :thingy11" do
- before(:context) {
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy11 < BaseThingy
resource_name :thingy10
end
- }
+ end
it "declaring a resource providing the same :thingy11 with os: 'linux' does not produce a warning" do
expect(Chef::Log).not_to receive(:warn)
@@ -829,7 +792,7 @@ describe "Recipe DSL methods" do
context "with a resource named 'B' with resource name :two_classes_one_dsl" do
let(:two_classes_one_dsl) { :"two_classes_one_dsl#{Namer.current_index}" }
- let(:resource_class) {
+ let(:resource_class) do
result = Class.new(BaseThingy) do
def self.name
"B"
@@ -841,11 +804,11 @@ describe "Recipe DSL methods" do
end
result.resource_name two_classes_one_dsl
result
- }
+ end
before { resource_class } # pull on it so it gets defined before the recipe runs
context "and another resource named 'A' with resource_name :two_classes_one_dsl" do
- let(:resource_class_a) {
+ let(:resource_class_a) do
result = Class.new(BaseThingy) do
def self.name
"A"
@@ -857,14 +820,14 @@ describe "Recipe DSL methods" do
end
result.resource_name two_classes_one_dsl
result
- }
+ end
before { resource_class_a } # pull on it so it gets defined before the recipe runs
it "two_classes_one_dsl resolves to A (alphabetically earliest)" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- instance_eval("#{two_classes_one_dsl} 'blah'")
- }
+ temp_two_classes_one_dsl = two_classes_one_dsl
+ recipe = converge do
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq resource_class_a
end
@@ -875,7 +838,7 @@ describe "Recipe DSL methods" do
end
context "and another resource named 'Z' with resource_name :two_classes_one_dsl" do
- let(:resource_class_z) {
+ let(:resource_class_z) do
result = Class.new(BaseThingy) do
def self.name
"Z"
@@ -887,20 +850,20 @@ describe "Recipe DSL methods" do
end
result.resource_name two_classes_one_dsl
result
- }
+ end
before { resource_class_z } # pull on it so it gets defined before the recipe runs
- it "two_classes_one_dsl resolves to B (alphabetically earliest)" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- instance_eval("#{two_classes_one_dsl} 'blah'")
- }
+ it "two_classes_one_dsl resolves to Z (last)" do
+ temp_two_classes_one_dsl = two_classes_one_dsl
+ recipe = converge do
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
+ end
expect(recipe.logged_warnings).to eq ""
- expect(BaseThingy.created_resource).to eq resource_class
+ expect(BaseThingy.created_resource).to eq resource_class_z
end
- it "resource_matching_short_name returns B" do
- expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ it "resource_matching_short_name returns Z" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class_z
end
context "and a priority array [ Z, B ]" do
@@ -909,16 +872,16 @@ describe "Recipe DSL methods" do
end
it "two_classes_one_dsl resolves to Z (respects the priority array)" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- instance_eval("#{two_classes_one_dsl} 'blah'")
- }
+ temp_two_classes_one_dsl = two_classes_one_dsl
+ recipe = converge do
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq resource_class_z
end
- it "resource_matching_short_name returns B" do
- expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ it "resource_matching_short_name returns Z" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class_z
end
context "when Z provides(:two_classes_one_dsl) { false }" do
@@ -927,10 +890,10 @@ describe "Recipe DSL methods" do
end
it "two_classes_one_dsl resolves to B (picks the next thing in the priority array)" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- instance_eval("#{two_classes_one_dsl} 'blah'")
- }
+ temp_two_classes_one_dsl = two_classes_one_dsl
+ recipe = converge do
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq resource_class
end
@@ -948,16 +911,16 @@ describe "Recipe DSL methods" do
end
it "two_classes_one_dsl resolves to Z (respects the most recent priority array)" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- instance_eval("#{two_classes_one_dsl} 'blah'")
- }
+ temp_two_classes_one_dsl = two_classes_one_dsl
+ recipe = converge do
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq resource_class_z
end
- it "resource_matching_short_name returns B" do
- expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ it "resource_matching_short_name returns Z" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class_z
end
context "when Z provides(:two_classes_one_dsl) { false }" do
@@ -966,10 +929,10 @@ describe "Recipe DSL methods" do
end
it "two_classes_one_dsl resolves to B (picks the first match from the other priority array)" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- instance_eval("#{two_classes_one_dsl} 'blah'")
- }
+ temp_two_classes_one_dsl = two_classes_one_dsl
+ recipe = converge do
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq resource_class
end
@@ -991,10 +954,10 @@ describe "Recipe DSL methods" do
end
it "two_classes_one_dsl resolves to B (picks the first match outside the priority array)" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- instance_eval("#{two_classes_one_dsl} 'blah'")
- }
+ temp_two_classes_one_dsl = two_classes_one_dsl
+ recipe = converge do
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq resource_class
end
@@ -1012,7 +975,7 @@ describe "Recipe DSL methods" do
resource_class.send(:define_method, :provider) { nil }
end
- let(:provider_class) {
+ let(:provider_class) do
result = Class.new(BaseThingy::Provider) do
def self.name
"B"
@@ -1024,11 +987,11 @@ describe "Recipe DSL methods" do
end
result.provides two_classes_one_dsl
result
- }
+ end
before { provider_class } # pull on it so it gets defined before the recipe runs
context "and another provider named 'A'" do
- let(:provider_class_a) {
+ let(:provider_class_a) do
result = Class.new(BaseThingy::Provider) do
def self.name
"A"
@@ -1039,15 +1002,15 @@ describe "Recipe DSL methods" do
def self.inspect; name.inspect; end
end
result
- }
+ end
context "which provides :two_classes_one_dsl" do
before { provider_class_a.provides two_classes_one_dsl }
it "two_classes_one_dsl resolves to A (alphabetically earliest)" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- instance_eval("#{two_classes_one_dsl} 'blah'")
- }
+ temp_two_classes_one_dsl = two_classes_one_dsl
+ recipe = converge do
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class_a
end
@@ -1056,10 +1019,10 @@ describe "Recipe DSL methods" do
before { provider_class_a.provides(two_classes_one_dsl) { false } }
it "two_classes_one_dsl resolves to B (since A declined)" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- instance_eval("#{two_classes_one_dsl} 'blah'")
- }
+ temp_two_classes_one_dsl = two_classes_one_dsl
+ recipe = converge do
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class
end
@@ -1067,7 +1030,7 @@ describe "Recipe DSL methods" do
end
context "and another provider named 'Z'" do
- let(:provider_class_z) {
+ let(:provider_class_z) do
result = Class.new(BaseThingy::Provider) do
def self.name
"Z"
@@ -1078,29 +1041,29 @@ describe "Recipe DSL methods" do
def self.inspect; name.inspect; end
end
result
- }
+ end
before { provider_class_z } # pull on it so it gets defined before the recipe runs
context "which provides :two_classes_one_dsl" do
before { provider_class_z.provides two_classes_one_dsl }
- it "two_classes_one_dsl resolves to B (alphabetically earliest)" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- instance_eval("#{two_classes_one_dsl} 'blah'")
- }
+ it "two_classes_one_dsl resolves to Z (last)" do
+ temp_two_classes_one_dsl = two_classes_one_dsl
+ recipe = converge do
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
+ end
expect(recipe.logged_warnings).to eq ""
- expect(BaseThingy.created_provider).to eq provider_class
+ expect(BaseThingy.created_provider).to eq provider_class_z
end
context "with a priority array [ Z, B ]" do
before { Chef.set_provider_priority_array two_classes_one_dsl, [ provider_class_z, provider_class ] }
it "two_classes_one_dsl resolves to Z (respects the priority map)" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- instance_eval("#{two_classes_one_dsl} 'blah'")
- }
+ temp_two_classes_one_dsl = two_classes_one_dsl
+ recipe = converge do
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class_z
end
@@ -1114,10 +1077,10 @@ describe "Recipe DSL methods" do
before { Chef.set_provider_priority_array two_classes_one_dsl, [ provider_class_z, provider_class ] }
it "two_classes_one_dsl resolves to B (the next one in the priority map)" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- instance_eval("#{two_classes_one_dsl} 'blah'")
- }
+ temp_two_classes_one_dsl = two_classes_one_dsl
+ recipe = converge do
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class
end
@@ -1128,10 +1091,10 @@ describe "Recipe DSL methods" do
before { Chef.set_provider_priority_array two_classes_one_dsl, [ provider_class ] }
it "two_classes_one_dsl resolves to B (the one in the next priority map)" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
- instance_eval("#{two_classes_one_dsl} 'blah'")
- }
+ temp_two_classes_one_dsl = two_classes_one_dsl
+ recipe = converge do
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class
end
@@ -1141,7 +1104,7 @@ describe "Recipe DSL methods" do
end
context "and another resource Blarghle with provides :two_classes_one_dsl, os: 'blarghle'" do
- let(:resource_class_blarghle) {
+ let(:resource_class_blarghle) do
result = Class.new(BaseThingy) do
def self.name
"Blarghle"
@@ -1154,27 +1117,27 @@ describe "Recipe DSL methods" do
result.resource_name two_classes_one_dsl
result.provides two_classes_one_dsl, os: "blarghle"
result
- }
+ end
before { resource_class_blarghle } # pull on it so it gets defined before the recipe runs
it "on os = blarghle, two_classes_one_dsl resolves to Blarghle" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
+ temp_two_classes_one_dsl = two_classes_one_dsl
+ recipe = converge do
# this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "blarghle"
- instance_eval("#{two_classes_one_dsl} 'blah' do; end")
- }
+ instance_eval("#{temp_two_classes_one_dsl} 'blah' do; end")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq resource_class_blarghle
end
it "on os = linux, two_classes_one_dsl resolves to B" do
- two_classes_one_dsl = self.two_classes_one_dsl
- recipe = converge {
+ temp_two_classes_one_dsl = two_classes_one_dsl
+ recipe = converge do
# this is an ugly way to test, make Cheffish expose node attrs
run_context.node.automatic[:os] = "linux"
- instance_eval("#{two_classes_one_dsl} 'blah' do; end")
- }
+ instance_eval("#{temp_two_classes_one_dsl} 'blah' do; end")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq resource_class
end
@@ -1182,67 +1145,39 @@ describe "Recipe DSL methods" do
end
context "with a resource MyResource" do
- let(:resource_class) { Class.new(BaseThingy) do
- def self.called_provides
- @called_provides
- end
+ let(:resource_class) do
+ Class.new(BaseThingy) do
+ def self.called_provides
+ @called_provides
+ end
- def to_s
- "MyResource"
- end
- end }
+ def to_s
+ "MyResource"
+ end
+ end end
let(:my_resource) { :"my_resource#{Namer.current_index}" }
let(:blarghle_blarghle_little_star) { :"blarghle_blarghle_little_star#{Namer.current_index}" }
context "with resource_name :my_resource" do
- before {
+ before do
resource_class.resource_name my_resource
- }
-
- context "with provides? returning true to my_resource" do
- before {
- my_resource = self.my_resource
- resource_class.define_singleton_method(:provides?) do |node, resource_name|
- @called_provides = true
- resource_name == my_resource
- end
- }
-
- it "my_resource returns the resource and calls provides?, but does not emit a warning" do
- dsl_name = self.my_resource
- recipe = converge {
- instance_eval("#{dsl_name} 'foo'")
- }
- expect(recipe.logged_warnings).to eq ""
- expect(BaseThingy.created_resource).to eq resource_class
- expect(resource_class.called_provides).to be_truthy
- end
end
- context "with provides? returning true to blarghle_blarghle_little_star and not resource_name" do
+ context "with provides? returning true to my_resource" do
before do
- blarghle_blarghle_little_star = self.blarghle_blarghle_little_star
+ temp_my_resource = my_resource
resource_class.define_singleton_method(:provides?) do |node, resource_name|
@called_provides = true
- resource_name == blarghle_blarghle_little_star
+ resource_name == temp_my_resource
end
end
- it "my_resource does not return the resource" do
- dsl_name = self.my_resource
- expect_converge {
- instance_eval("#{dsl_name} 'foo'")
- }.to raise_error(Chef::Exceptions::NoSuchResourceType)
- expect(resource_class.called_provides).to be_truthy
- end
-
- it "blarghle_blarghle_little_star 'foo' returns the resource and emits a warning" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- dsl_name = self.blarghle_blarghle_little_star
- recipe = converge {
+ it "my_resource returns the resource and calls provides?, but does not emit a warning" do
+ dsl_name = my_resource
+ recipe = converge do
instance_eval("#{dsl_name} 'foo'")
- }
- expect(recipe.logged_warnings).to include "WARN: #{resource_class}.provides? returned true when asked if it provides DSL #{dsl_name}, but provides :#{dsl_name} was never called!"
+ end
+ expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq resource_class
expect(resource_class.called_provides).to be_truthy
end
@@ -1280,10 +1215,10 @@ describe "Recipe DSL methods" do
end
it "my_resource runs the provider and does not emit a warning" do
- my_resource = self.my_resource
- recipe = converge {
- instance_eval("#{my_resource} 'foo'")
- }
+ temp_my_resource = my_resource
+ recipe = converge do
+ instance_eval("#{temp_my_resource} 'foo'")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class
end
@@ -1310,10 +1245,10 @@ describe "Recipe DSL methods" do
end
it "my_resource runs the first provider" do
- my_resource = self.my_resource
- recipe = converge {
- instance_eval("#{my_resource} 'foo'")
- }
+ temp_my_resource = my_resource
+ recipe = converge do
+ instance_eval("#{temp_my_resource} 'foo'")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class
end
@@ -1327,17 +1262,17 @@ describe "Recipe DSL methods" do
# TODO no warning? ick
it "my_resource runs the provider anyway" do
- my_resource = self.my_resource
- recipe = converge {
- instance_eval("#{my_resource} 'foo'")
- }
+ temp_my_resource = my_resource
+ recipe = converge do
+ instance_eval("#{temp_my_resource} 'foo'")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class
end
context "and another provider supporting :my_resource with supports? true" do
let(:provider_class2) do
- my_resource = self.my_resource
+ temp_my_resource = my_resource
Class.new(BaseThingy::Provider) do
def self.name
"MyProvider2"
@@ -1350,7 +1285,7 @@ describe "Recipe DSL methods" do
def self.called_provides
@called_provides
end
- provides my_resource
+ provides temp_my_resource
def self.supports?(resource, action)
true
end
@@ -1359,90 +1294,34 @@ describe "Recipe DSL methods" do
before { provider_class2 } # make sure the provider class shows up
it "my_resource runs the other provider" do
- my_resource = self.my_resource
- recipe = converge {
- instance_eval("#{my_resource} 'foo'")
- }
+ temp_my_resource = my_resource
+ recipe = converge do
+ instance_eval("#{temp_my_resource} 'foo'")
+ end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class2
end
end
end
end
+ end
+ end
+ end
- context "with provides? returning true" do
- before {
- my_resource = self.my_resource
- provider_class.define_singleton_method(:provides?) do |node, resource|
- @called_provides = true
- resource.declared_type == my_resource
- end
- }
-
- context "that provides :my_resource" do
- before {
- provider_class.provides my_resource
- }
-
- it "my_resource calls the provider (and calls provides?), but does not emit a warning" do
- my_resource = self.my_resource
- recipe = converge {
- instance_eval("#{my_resource} 'foo'")
- }
- expect(recipe.logged_warnings).to eq ""
- expect(BaseThingy.created_provider).to eq provider_class
- expect(provider_class.called_provides).to be_truthy
- end
- end
-
- context "that does not call provides :my_resource" do
- it "my_resource calls the provider (and calls provides?), and emits a warning" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- my_resource = self.my_resource
- recipe = converge {
- instance_eval("#{my_resource} 'foo'")
- }
- expect(recipe.logged_warnings).to include("WARN: #{provider_class}.provides? returned true when asked if it provides DSL #{my_resource}, but provides :#{my_resource} was never called!")
- expect(BaseThingy.created_provider).to eq provider_class
- expect(provider_class.called_provides).to be_truthy
- end
- end
- end
-
- context "with provides? returning false to my_resource" do
- before {
- my_resource = self.my_resource
- provider_class.define_singleton_method(:provides?) do |node, resource|
- @called_provides = true
- false
- end
- }
-
- context "that provides :my_resource" do
- before {
- provider_class.provides my_resource
- }
-
- it "my_resource fails to find a provider (and calls provides)" do
- my_resource = self.my_resource
- expect_converge {
- instance_eval("#{my_resource} 'foo'")
- }.to raise_error(Chef::Exceptions::ProviderNotFound)
- expect(provider_class.called_provides).to be_truthy
- end
- end
+ context "with UTF-8 provides" do
+ before(:each) do
+ class UTF8Thingy < BaseThingy
+ resource_name :Straße
+ provides :Straße
+ end
+ end
- context "that does not provide :my_resource" do
- it "my_resource fails to find a provider (and calls provides)" do
- my_resource = self.my_resource
- expect_converge {
- instance_eval("#{my_resource} 'foo'")
- }.to raise_error(Chef::Exceptions::ProviderNotFound)
- expect(provider_class.called_provides).to be_truthy
- end
- end
- end
+ it "utf-8 dsl names work" do
+ recipe = converge do
+ Straße("blah") {}
end
+ expect(recipe.logged_warnings).to eq ""
+ expect(BaseThingy.created_resource).to eq(UTF8Thingy)
end
end
end
@@ -1451,15 +1330,15 @@ describe "Recipe DSL methods" do
before { Namer.current_index += 1 }
context "with an LWRP that declares actions" do
- let(:resource_class) {
+ let(:resource_class) do
Class.new(Chef::Resource::LWRPBase) do
provides :"recipe_dsl_spec#{Namer.current_index}"
actions :create
end
- }
- let(:resource) {
+ end
+ let(:resource) do
resource_class.new("blah", run_context)
- }
+ end
it "The actions are part of actions along with :nothing" do
expect(resource_class.actions).to eq [ :nothing, :create ]
end
@@ -1468,15 +1347,15 @@ describe "Recipe DSL methods" do
end
context "and a subclass that declares more actions" do
- let(:subresource_class) {
+ let(:subresource_class) do
Class.new(Chef::Resource::LWRPBase) do
provides :"recipe_dsl_spec_sub#{Namer.current_index}"
actions :delete
end
- }
- let(:subresource) {
+ end
+ let(:subresource) do
subresource_class.new("subblah", run_context)
- }
+ end
it "The parent class actions are not part of actions" do
expect(subresource_class.actions).to eq [ :nothing, :delete ]
@@ -1491,29 +1370,4 @@ describe "Recipe DSL methods" do
end
end
- context "with a dynamically defined resource and regular provider" do
- before(:context) do
- Class.new(Chef::Resource) do
- resource_name :lw_resource_with_hw_provider_test_case
- default_action :create
- attr_accessor :created_provider
- end
- class Chef::Provider::LwResourceWithHwProviderTestCase < Chef::Provider
- def load_current_resource
- end
-
- def action_create
- new_resource.created_provider = self.class
- end
- end
- end
-
- it "looks up the provider in Chef::Provider converting the resource name from snake case to camel case" do
- resource = nil
- recipe = converge {
- resource = lw_resource_with_hw_provider_test_case "blah" do; end
- }
- expect(resource.created_provider).to eq(Chef::Provider::LwResourceWithHwProviderTestCase)
- end
- end
end
diff --git a/spec/integration/recipes/resource_action_spec.rb b/spec/integration/recipes/resource_action_spec.rb
index 8f6f4b7f46..149b17fcad 100644
--- a/spec/integration/recipes/resource_action_spec.rb
+++ b/spec/integration/recipes/resource_action_spec.rb
@@ -1,5 +1,30 @@
require "support/shared/integration/integration_helper"
+class NoActionJackson < Chef::Resource
+ use_automatic_resource_name
+
+ def foo(value = nil)
+ @foo = value if value
+ @foo
+ end
+
+ class <<self
+ attr_accessor :action_was
+ end
+end
+
+class WeirdActionJackson < Chef::Resource
+ use_automatic_resource_name
+
+ class <<self
+ attr_accessor :action_was
+ end
+
+ action :Straße do
+ WeirdActionJackson.action_was = action
+ end
+end
+
# Houses any classes we declare
module ResourceActionSpec
@@ -9,94 +34,110 @@ module ResourceActionSpec
shared_context "ActionJackson" do
it "the default action is the first declared action" do
converge <<-EOM, __FILE__, __LINE__ + 1
- #{resource_dsl} "hi" do
- foo "foo!"
- end
- EOM
+ #{resource_dsl} "hi" do
+ foo "foo!"
+ end
+ EOM
expect(ActionJackson.ran_action).to eq :access_recipe_dsl
expect(ActionJackson.succeeded).to eq true
end
+ context "when running in whyrun mode" do
+ before do
+ Chef::Config[:why_run] = true
+ end
+
+ it "the default action runs" do
+ converge <<-EOM, __FILE__, __LINE__ + 1
+ #{resource_dsl} "hi" do
+ foo "foo!"
+ end
+ EOM
+ expect(ActionJackson.ran_action).to eq :access_recipe_dsl
+ expect(ActionJackson.succeeded).to eq true
+ end
+ end
+
it "the action can access recipe DSL" do
converge <<-EOM, __FILE__, __LINE__ + 1
- #{resource_dsl} "hi" do
- foo "foo!"
- action :access_recipe_dsl
- end
- EOM
+ #{resource_dsl} "hi" do
+ foo "foo!"
+ action :access_recipe_dsl
+ end
+ EOM
expect(ActionJackson.ran_action).to eq :access_recipe_dsl
expect(ActionJackson.succeeded).to eq true
end
it "the action can access attributes" do
converge <<-EOM, __FILE__, __LINE__ + 1
- #{resource_dsl} "hi" do
- foo "foo!"
- action :access_attribute
- end
- EOM
+ #{resource_dsl} "hi" do
+ foo "foo!"
+ action :access_attribute
+ end
+ EOM
expect(ActionJackson.ran_action).to eq :access_attribute
expect(ActionJackson.succeeded).to eq "foo!"
end
it "the action can access public methods" do
converge <<-EOM, __FILE__, __LINE__ + 1
- #{resource_dsl} "hi" do
- foo "foo!"
- action :access_method
- end
- EOM
+ #{resource_dsl} "hi" do
+ foo "foo!"
+ action :access_method
+ end
+ EOM
expect(ActionJackson.ran_action).to eq :access_method
expect(ActionJackson.succeeded).to eq "foo_public!"
end
it "the action can access protected methods" do
converge <<-EOM, __FILE__, __LINE__ + 1
- #{resource_dsl} "hi" do
- foo "foo!"
- action :access_protected_method
- end
- EOM
+ #{resource_dsl} "hi" do
+ foo "foo!"
+ action :access_protected_method
+ end
+ EOM
expect(ActionJackson.ran_action).to eq :access_protected_method
expect(ActionJackson.succeeded).to eq "foo_protected!"
end
it "the action cannot access private methods" do
- expect {
+ expect do
converge(<<-EOM, __FILE__, __LINE__ + 1)
- #{resource_dsl} "hi" do
- foo "foo!"
- action :access_private_method
- end
- EOM
- }.to raise_error(NameError)
+ #{resource_dsl} "hi" do
+ foo "foo!"
+ action :access_private_method
+ end
+ EOM
+ end.to raise_error(NameError)
expect(ActionJackson.ran_action).to eq :access_private_method
end
it "the action cannot access resource instance variables" do
converge <<-EOM, __FILE__, __LINE__ + 1
- #{resource_dsl} "hi" do
- foo "foo!"
- action :access_instance_variable
- end
- EOM
+ #{resource_dsl} "hi" do
+ foo "foo!"
+ action :access_instance_variable
+ end
+ EOM
expect(ActionJackson.ran_action).to eq :access_instance_variable
expect(ActionJackson.succeeded).to be_nil
end
it "the action does not compile until the prior resource has converged" do
converge <<-EOM, __FILE__, __LINE__ + 1
- ruby_block "wow" do
- block do
- ResourceActionSpec::ActionJackson.ruby_block_converged = "ruby_block_converged!"
+ ruby_block "wow" do
+ block do
+ ResourceActionSpec::ActionJackson.ruby_block_converged = "ruby_block_converged!"
+ end
end
- end
- #{resource_dsl} "hi" do
- foo "foo!"
- action :access_class_method
- end
- EOM
+ #{resource_dsl} "hi" do
+ foo "foo!"
+ action :access_class_method
+ end
+ EOM
expect(ActionJackson.ran_action).to eq :access_class_method
expect(ActionJackson.succeeded).to eq "ruby_block_converged!"
end
@@ -139,29 +180,9 @@ module ResourceActionSpec
attr_accessor :ruby_block_converged
end
- public
-
- def foo_public
- "foo_public!"
- end
-
- protected
-
- def foo_protected
- "foo_protected!"
- end
-
- private
-
- def foo_private
- "foo_private!"
- end
-
- public
-
action :access_recipe_dsl do
ActionJackson.ran_action = :access_recipe_dsl
- ruby_block "hi there" do
+ whyrun_safe_ruby_block "hi there" do
block do
ActionJackson.succeeded = true
end
@@ -199,13 +220,29 @@ module ResourceActionSpec
ActionJackson.ran_action = :access_class_method
ActionJackson.succeeded = ActionJackson.ruby_block_converged
end
+
+ def foo_public
+ "foo_public!"
+ end
+
+ protected
+
+ def foo_protected
+ "foo_protected!"
+ end
+
+ private
+
+ def foo_private
+ "foo_private!"
+ end
end
- before(:each) {
+ before(:each) do
ActionJackson.ran_action = :error
ActionJackson.succeeded = :error
ActionJackson.ruby_block_converged = :error
- }
+ end
it_behaves_like "ActionJackson" do
let(:resource_dsl) { :action_jackson }
@@ -217,11 +254,11 @@ module ResourceActionSpec
end
context "And 'action_jackgrandson' inheriting from ActionJackson and changing nothing" do
- before(:context) {
+ before(:each) do
class ActionJackgrandson < ActionJackson
use_automatic_resource_name
end
- }
+ end
it_behaves_like "ActionJackson" do
let(:resource_dsl) { :action_jackgrandson }
@@ -267,38 +304,38 @@ module ResourceActionSpec
end
it "the default action remains the same even though new actions were specified first" do
- converge {
+ converge do
action_jackalope "hi" do
foo "foo!"
bar "bar!"
end
- }
+ end
expect(ActionJackson.ran_action).to eq :access_recipe_dsl
expect(ActionJackson.succeeded).to eq true
end
it "new actions run, and can access overridden, new, and overridden attributes" do
- converge {
+ converge do
action_jackalope "hi" do
foo "foo!"
bar "bar!"
blarghle "blarghle!"
action :access_jackalope
end
- }
+ end
expect(ActionJackalope.jackalope_ran).to eq :access_jackalope
expect(ActionJackalope.succeeded).to eq "foo!alope blarghle! bar!alope"
end
it "overridden actions run, call super, and can access overridden, new, and overridden attributes" do
- converge {
+ converge do
action_jackalope "hi" do
foo "foo!"
bar "bar!"
blarghle "blarghle!"
action :access_attribute
end
- }
+ end
expect(ActionJackson.ran_action).to eq :access_attribute
expect(ActionJackson.succeeded).to eq "foo!alope blarghle! bar!alope"
expect(ActionJackalope.jackalope_ran).to eq :access_attribute
@@ -306,14 +343,14 @@ module ResourceActionSpec
end
it "non-overridden actions run and can access overridden and non-overridden variables (but not necessarily new ones)" do
- converge {
+ converge do
action_jackalope "hi" do
foo "foo!"
bar "bar!"
blarghle "blarghle!"
action :access_attribute2
end
- }
+ end
expect(ActionJackson.ran_action).to eq :access_attribute2
expect(ActionJackson.succeeded).to eq("foo!alope blarghle! bar!alope").or(eq("foo!alope blarghle!"))
end
@@ -321,129 +358,26 @@ module ResourceActionSpec
end
context "With a resource with no actions" do
- class NoActionJackson < Chef::Resource
- use_automatic_resource_name
-
- def foo(value = nil)
- @foo = value if value
- @foo
- end
-
- class <<self
- attr_accessor :action_was
- end
- end
-
it "the default action is :nothing" do
- converge {
+ converge do
no_action_jackson "hi" do
foo "foo!"
NoActionJackson.action_was = action
end
- }
+ end
expect(NoActionJackson.action_was).to eq [:nothing]
end
end
- context "With a resource with action a-b-c d" do
- class WeirdActionJackson < Chef::Resource
- use_automatic_resource_name
-
- class <<self
- attr_accessor :action_was
- end
-
- action "a-b-c d" do
- WeirdActionJackson.action_was = action
- end
- end
-
+ context "With a resource with a UTF-8 action" do
it "Running the action works" do
- expect_recipe {
+ expect_recipe do
weird_action_jackson "hi"
- }.to be_up_to_date
- expect(WeirdActionJackson.action_was).to eq :"a-b-c d"
+ end.to be_up_to_date
+ expect(WeirdActionJackson.action_was).to eq :Straße
end
end
- context "With a resource with property x" do
- class ResourceActionSpecWithX < Chef::Resource
- resource_name :resource_action_spec_with_x
- property :x, default: 20
- action :set do
- # Access x during converge to ensure that we emit no warnings there
- x
- end
- end
-
- context "And another resource with a property x and an action that sets property x to its value" do
- class ResourceActionSpecAlsoWithX < Chef::Resource
- resource_name :resource_action_spec_also_with_x
- property :x
- action :set_x_to_x do
- resource_action_spec_with_x "hi" do
- x x
- end
- end
- def self.x_warning_line
- __LINE__ - 4
- end
- action :set_x_to_x_in_non_initializer do
- r = resource_action_spec_with_x "hi" do
- x 10
- end
- x_times_2 = r.x * 2
- end
- action :set_x_to_10 do
- resource_action_spec_with_x "hi" do
- x 10
- end
- end
- end
-
- attr_reader :x_warning_line
-
- it "Using the enclosing resource to set x to x emits a warning that you're using the wrong x" do
- recipe = converge {
- resource_action_spec_also_with_x "hi" do
- x 1
- action :set_x_to_x
- end
- }
- warnings = recipe.logs.lines.select { |l| l =~ /warn/i }
- expect(warnings.size).to eq 1
- expect(warnings[0]).to match(/property x is declared in both resource_action_spec_with_x\[hi\] and resource_action_spec_also_with_x\[hi\] action :set_x_to_x. Use new_resource.x instead. At #{__FILE__}:#{ResourceActionSpecAlsoWithX.x_warning_line}/)
- end
-
- it "Using the enclosing resource to set x to x outside the initializer emits no warning" do
- expect_recipe {
- resource_action_spec_also_with_x "hi" do
- x 1
- action :set_x_to_x_in_non_initializer
- end
- }.to emit_no_warnings_or_errors
- end
-
- it "Using the enclosing resource to set x to 10 emits no warning" do
- expect_recipe {
- resource_action_spec_also_with_x "hi" do
- x 1
- action :set_x_to_10
- end
- }.to emit_no_warnings_or_errors
- end
-
- it "Using the enclosing resource to set x to 10 emits no warning" do
- expect_recipe {
- r = resource_action_spec_also_with_x "hi"
- r.x 1
- r.action :set_x_to_10
- }.to emit_no_warnings_or_errors
- end
- end
-
- end
-
context "With a resource with a set_or_return property named group (same name as a resource)" do
class ResourceActionSpecWithGroupAction < Chef::Resource
resource_name :resource_action_spec_set_group_to_nil
@@ -464,11 +398,11 @@ module ResourceActionSpec
end
it "Setting group to nil in an action does not emit a warning about it being defined in two places" do
- expect_recipe {
+ expect_recipe do
resource_action_spec_set_group_to_nil "hi" do
action :set_group_to_nil
end
- }.to emit_no_warnings_or_errors
+ end.to emit_no_warnings_or_errors
end
end
@@ -482,66 +416,45 @@ module ResourceActionSpec
end
end
end
-
- it "Raises an error when attempting to use a template in the action" do
- expect_converge {
- has_property_named_template "hi"
- }.to raise_error(/Property template of has_property_named_template\[hi\] cannot be passed a block! If you meant to create a resource named template instead, you'll need to first rename the property./)
- end
end
- context "When a resource declares methods in action_class and declare_action_class" do
+ context "When a resource declares methods in action_class" do
class DeclaresActionClassMethods < Chef::Resource
use_automatic_resource_name
property :x
- action :create do
- new_resource.x = a + b + c + d
- end
action_class do
def a
1
end
end
- declare_action_class do
- def b
- 2
- end
- end
- action_class do
+ action_class.class_eval <<-EOM
def c
3
end
- end
- declare_action_class do
- def d
- 4
- end
+ EOM
+ action :create do
+ new_resource.x = a + c
end
end
it "the methods are not available on the resource" do
expect { DeclaresActionClassMethods.new("hi").a }.to raise_error(NameError)
- expect { DeclaresActionClassMethods.new("hi").b }.to raise_error(NameError)
expect { DeclaresActionClassMethods.new("hi").c }.to raise_error(NameError)
- expect { DeclaresActionClassMethods.new("hi").d }.to raise_error(NameError)
end
it "the methods are available to the action" do
r = nil
- expect_recipe {
+ expect_recipe do
r = declares_action_class_methods "hi"
- }.to emit_no_warnings_or_errors
- expect(r.x).to eq(10)
+ end.to emit_no_warnings_or_errors
+ expect(r.x).to eq(4)
end
- context "And a subclass also creates a method" do
+ context "And a subclass overrides a method with an action_class block" do
class DeclaresActionClassMethodsToo < DeclaresActionClassMethods
use_automatic_resource_name
- action :create do
- new_resource.x a + b + c + d + e
- end
action_class do
- def e
+ def a
5
end
end
@@ -549,18 +462,40 @@ module ResourceActionSpec
it "the methods are not available on the resource" do
expect { DeclaresActionClassMethods.new("hi").a }.to raise_error(NameError)
- expect { DeclaresActionClassMethods.new("hi").b }.to raise_error(NameError)
expect { DeclaresActionClassMethods.new("hi").c }.to raise_error(NameError)
- expect { DeclaresActionClassMethods.new("hi").d }.to raise_error(NameError)
- expect { DeclaresActionClassMethods.new("hi").e }.to raise_error(NameError)
end
it "the methods are available to the action" do
r = nil
- expect_recipe {
+ expect_recipe do
+ r = declares_action_class_methods_too "hi"
+ end.to emit_no_warnings_or_errors
+ expect(r.x).to eq(8)
+ end
+ end
+
+ context "And a subclass overrides a method with class_eval" do
+ # this tests inheritance with *only* an action_class accessor that does not declare a block
+ class DeclaresActionClassMethodsToo < DeclaresActionClassMethods
+ use_automatic_resource_name
+ action_class.class_eval <<-EOM
+ def a
+ 5
+ end
+ EOM
+ end
+
+ it "the methods are not available on the resource" do
+ expect { DeclaresActionClassMethods.new("hi").a }.to raise_error(NameError)
+ expect { DeclaresActionClassMethods.new("hi").c }.to raise_error(NameError)
+ end
+
+ it "the methods are available to the action" do
+ r = nil
+ expect_recipe do
r = declares_action_class_methods_too "hi"
- }.to emit_no_warnings_or_errors
- expect(r.x).to eq(15)
+ end.to emit_no_warnings_or_errors
+ expect(r.x).to eq(8)
end
end
end
diff --git a/spec/integration/recipes/resource_converge_if_changed_spec.rb b/spec/integration/recipes/resource_converge_if_changed_spec.rb
index 4e5fd526fc..f0ba4822a7 100644
--- a/spec/integration/recipes/resource_converge_if_changed_spec.rb
+++ b/spec/integration/recipes/resource_converge_if_changed_spec.rb
@@ -17,9 +17,9 @@ describe "Resource::ActionClass#converge_if_changed" do
before { Namer.current_index += 1 }
before { Namer.incrementing_value = 0 }
- context "when the resource has identity, state and control properties" do
+ context "when the resource has identity, state, control, and sensitive properties" do
let(:resource_name) { :"converge_if_changed_dsl#{Namer.current_index}" }
- let(:resource_class) {
+ let(:resource_class) do
result = Class.new(Chef::Resource) do
def self.to_s; resource_name.to_s; end
@@ -28,6 +28,7 @@ describe "Resource::ActionClass#converge_if_changed" do
property :control1, desired_state: false, default: "default_control1"
property :state1, default: "default_state1"
property :state2, default: "default_state2"
+ property :sensitive1, default: "default_dontprintme", sensitive: true
attr_accessor :converged
def initialize(*args)
super
@@ -36,7 +37,7 @@ describe "Resource::ActionClass#converge_if_changed" do
end
result.resource_name resource_name
result
- }
+ end
let(:converged_recipe) { converge(converge_recipe) }
let(:resource) { converged_recipe.resources.first }
@@ -54,6 +55,7 @@ describe "Resource::ActionClass#converge_if_changed" do
resource_class.load_current_value do
state1 "current_state1"
state2 "current_state2"
+ sensitive1 "current_dontprintme"
end
end
@@ -70,13 +72,13 @@ describe "Resource::ActionClass#converge_if_changed" do
end
context "and state1 is set to a new value" do
- let(:converge_recipe) {
+ let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
state1 'new_state1'
end
EOM
- }
+ end
it "the resource updates state1" do
expect(resource.converged).to eq 1
@@ -90,14 +92,14 @@ describe "Resource::ActionClass#converge_if_changed" do
end
context "and state1 and state2 are set to new values" do
- let(:converge_recipe) {
+ let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
state1 'new_state1'
state2 'new_state2'
end
EOM
- }
+ end
it "the resource updates state1 and state2" do
expect(resource.converged).to eq 1
@@ -112,7 +114,7 @@ EOM
end
context "and state1 and state2 are set to new sensitive values" do
- let(:converge_recipe) {
+ let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
sensitive true
@@ -120,7 +122,7 @@ EOM
state2 'new_state2'
end
EOM
- }
+ end
it "the resource updates state1 and state2" do
expect(resource.converged).to eq 1
@@ -134,15 +136,35 @@ EOM
end
end
+ context "and sensitive1 is set to a new value" do
+ let(:converge_recipe) do
+ <<-EOM
+ #{resource_name} 'blah' do
+ sensitive1 'new_dontprintme'
+ end
+ EOM
+ end
+
+ it "the resource updates sensitive1" do
+ expect(resource.converged).to eq 1
+ expect(resource.updated?).to be_truthy
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create
+ - update default_identity1
+ - set sensitive1 to (suppressed sensitive property)
+EOM
+ end
+ end
+
context "and state1 is set to its current value but state2 is set to a new value" do
- let(:converge_recipe) {
+ let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
state1 'current_state1'
state2 'new_state2'
end
EOM
- }
+ end
it "the resource updates state2" do
expect(resource.converged).to eq 1
@@ -156,14 +178,14 @@ EOM
end
context "and state1 and state2 are set to their current values" do
- let(:converge_recipe) {
+ let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
state1 'current_state1'
state2 'current_state2'
end
EOM
- }
+ end
it "the resource updates nothing" do
expect(resource.converged).to eq 0
@@ -175,14 +197,14 @@ EOM
end
context "and identity1 and control1 are set to new values" do
- let(:converge_recipe) {
+ let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
identity1 'new_identity1'
control1 'new_control1'
end
EOM
- }
+ end
# Because the identity value is copied over to the new resource, by
# default they do not register as "changed"
@@ -205,14 +227,14 @@ EOM
end
context "and identity1 and control1 are set to new values" do
- let(:converge_recipe) {
+ let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
identity1 'new_identity1'
control1 'new_control1'
end
EOM
- }
+ end
# Control values are not desired state and are therefore not considered
# a reason for converging.
@@ -244,22 +266,24 @@ EOM
expect(converged_recipe.stdout).to eq <<-EOM
* #{resource_name}[blah] action create
- create default_identity1
- - set identity1 to "default_identity1" (default value)
- - set state1 to "default_state1" (default value)
- - set state2 to "default_state2" (default value)
+ - set identity1 to "default_identity1" (default value)
+ - set state1 to "default_state1" (default value)
+ - set state2 to "default_state2" (default value)
+ - set sensitive1 to (suppressed sensitive property) (default value)
EOM
end
end
- context "and state1 and state2 are set" do
- let(:converge_recipe) {
+ context "and state1, state2, and sensitive1 are set" do
+ let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
state1 'new_state1'
state2 'new_state2'
+ sensitive1 'new_dontprintme'
end
EOM
- }
+ end
it "the resource is created" do
expect(resource.converged).to eq 1
@@ -267,15 +291,16 @@ EOM
expect(converged_recipe.stdout).to eq <<-EOM
* #{resource_name}[blah] action create
- create default_identity1
- - set identity1 to "default_identity1" (default value)
- - set state1 to "new_state1"
- - set state2 to "new_state2"
+ - set identity1 to "default_identity1" (default value)
+ - set state1 to "new_state1"
+ - set state2 to "new_state2"
+ - set sensitive1 to (suppressed sensitive property)
EOM
end
end
context "and state1 and state2 are set with sensitive property" do
- let(:converge_recipe) {
+ let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
sensitive true
@@ -283,7 +308,7 @@ EOM
state2 'new_state2'
end
EOM
- }
+ end
it "the resource is created" do
expect(resource.converged).to eq 1
@@ -291,9 +316,10 @@ EOM
expect(converged_recipe.stdout).to eq <<-EOM
* #{resource_name}[blah] action create
- create default_identity1
- - set identity1 to (suppressed sensitive property) (default value)
- - set state1 to (suppressed sensitive property)
- - set state2 to (suppressed sensitive property)
+ - set identity1 to (suppressed sensitive property) (default value)
+ - set state1 to (suppressed sensitive property)
+ - set state2 to (suppressed sensitive property)
+ - set sensitive1 to (suppressed sensitive property) (default value)
EOM
end
end
@@ -309,6 +335,9 @@ EOM
converge_if_changed :state2 do
new_resource.converged += 1
end
+ converge_if_changed :sensitive1 do
+ new_resource.converged += 1
+ end
end
end
@@ -334,13 +363,13 @@ EOM
context "and state1 is set to a new value" do
- let(:converge_recipe) {
+ let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
state1 'new_state1'
end
EOM
- }
+ end
it "the resource updates state1" do
expect(resource.converged).to eq 1
@@ -354,14 +383,14 @@ EOM
end
context "and state1 and state2 are set to new values" do
- let(:converge_recipe) {
+ let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
state1 'new_state1'
state2 'new_state2'
end
EOM
- }
+ end
it "the resource updates state1 and state2" do
expect(resource.converged).to eq 2
@@ -377,14 +406,14 @@ EOM
end
context "and state1 is set to its current value but state2 is set to a new value" do
- let(:converge_recipe) {
+ let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
state1 'current_state1'
state2 'new_state2'
end
EOM
- }
+ end
it "the resource updates state2" do
expect(resource.converged).to eq 1
@@ -398,14 +427,14 @@ EOM
end
context "and state1 and state2 are set to their current values" do
- let(:converge_recipe) {
+ let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
state1 'current_state1'
state2 'current_state2'
end
EOM
- }
+ end
it "the resource updates nothing" do
expect(resource.converged).to eq 0
@@ -415,6 +444,26 @@ EOM
EOM
end
end
+
+ context "and sensitive1 is set to a new value" do
+ let(:converge_recipe) do
+ <<-EOM
+ #{resource_name} 'blah' do
+ sensitive1 'new_dontprintme'
+ end
+ EOM
+ end
+
+ it "the resource updates sensitive1" do
+ expect(resource.converged).to eq 1
+ expect(resource.updated?).to be_truthy
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create
+ - update default_identity1
+ - set sensitive1 to (suppressed sensitive property)
+EOM
+ end
+ end
end
context "and no current_resource" do
@@ -425,12 +474,12 @@ EOM
end
context "and nothing is set" do
- let(:converge_recipe) {
+ let(:converge_recipe) do
"#{resource_name} 'blah'"
- }
+ end
it "the resource is created" do
- expect(resource.converged).to eq 2
+ expect(resource.converged).to eq 3
expect(resource.updated?).to be_truthy
expect(converged_recipe.stdout).to eq <<-EOM
* #{resource_name}[blah] action create
@@ -438,22 +487,25 @@ EOM
- set state1 to "default_state1" (default value)
- create default_identity1
- set state2 to "default_state2" (default value)
+ - create default_identity1
+ - set sensitive1 to (suppressed sensitive property) (default value)
EOM
end
end
- context "and state1 and state2 are set to new values" do
- let(:converge_recipe) {
+ context "and state1, state2, and sensitive1 are set to new values" do
+ let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
state1 'new_state1'
state2 'new_state2'
+ sensitive1 'new_dontprintme'
end
EOM
- }
+ end
it "the resource is created" do
- expect(resource.converged).to eq 2
+ expect(resource.converged).to eq 3
expect(resource.updated?).to be_truthy
expect(converged_recipe.stdout).to eq <<-EOM
* #{resource_name}[blah] action create
@@ -461,12 +513,14 @@ EOM
- set state1 to "new_state1"
- create default_identity1
- set state2 to "new_state2"
+ - create default_identity1
+ - set sensitive1 to (suppressed sensitive property)
EOM
end
end
context "and state1 and state2 are set to new sensitive values" do
- let(:converge_recipe) {
+ let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
sensitive true
@@ -474,10 +528,10 @@ EOM
state2 'new_state2'
end
EOM
- }
+ end
it "the resource is created" do
- expect(resource.converged).to eq 2
+ expect(resource.converged).to eq 3
expect(resource.updated?).to be_truthy
expect(converged_recipe.stdout).to eq <<-EOM
* #{resource_name}[blah] action create
@@ -485,6 +539,8 @@ EOM
- set state1 to (suppressed sensitive property)
- create default_identity1
- set state2 to (suppressed sensitive property)
+ - create default_identity1
+ - set sensitive1 to (suppressed sensitive property) (default value)
EOM
end
end
diff --git a/spec/integration/recipes/resource_load_spec.rb b/spec/integration/recipes/resource_load_spec.rb
index 53ce1c91b3..4fc14c0687 100644
--- a/spec/integration/recipes/resource_load_spec.rb
+++ b/spec/integration/recipes/resource_load_spec.rb
@@ -18,7 +18,7 @@ describe "Resource.load_current_value" do
before { Namer.incrementing_value = 0 }
let(:resource_name) { :"load_current_value_dsl#{Namer.current_index}" }
- let(:resource_class) {
+ let(:resource_class) do
result = Class.new(Chef::Resource) do
def self.to_s; resource_name.to_s; end
@@ -32,12 +32,12 @@ describe "Resource.load_current_value" do
@created
end
action :create do
- new_resource.class.created_x = x
+ new_resource.class.created_x = new_resource.x
end
end
result.resource_name resource_name
result
- }
+ end
# Pull on resource_class to initialize it
before { resource_class }
@@ -45,10 +45,10 @@ describe "Resource.load_current_value" do
context "with a resource with load_current_value" do
before :each do
resource_class.load_current_value do
- x "loaded #{Namer.incrementing_value} (#{self.class.properties.sort_by { |name, p| name }.
- select { |name, p| p.is_set?(self) }.
- map { |name, p| "#{name}=#{p.get(self)}" }.
- join(", ") })"
+ x "loaded #{Namer.incrementing_value} (#{self.class.properties.sort_by { |name, p| name }
+ .select { |name, p| p.is_set?(self) }
+ .map { |name, p| "#{name}=#{p.get(self)}" }
+ .join(", ")})"
end
end
@@ -56,11 +56,11 @@ describe "Resource.load_current_value" do
let(:resource) do
e = self
r = nil
- converge {
+ converge do
r = public_send(e.resource_name, "blah") do
x "desired"
end
- }
+ end
r
end
@@ -83,17 +83,17 @@ describe "Resource.load_current_value" do
end
context "and identity: :i and :d with desired_state: false" do
- before {
+ before do
resource_class.class_eval do
property :i, identity: true
property :d, desired_state: false
end
- }
+ end
- before {
+ before do
resource.i "desired_i"
resource.d "desired_d"
- }
+ end
it "i, name and d are passed to load_current_value, but not x" do
expect(resource.current_value.x).to eq "loaded 2 (d=desired_d, i=desired_i, name=blah)"
@@ -101,17 +101,17 @@ describe "Resource.load_current_value" do
end
context "and name_property: :i and :d with desired_state: false" do
- before {
+ before do
resource_class.class_eval do
property :i, name_property: true
property :d, desired_state: false
end
- }
+ end
- before {
+ before do
resource.i "desired_i"
resource.d "desired_d"
- }
+ end
it "i, name and d are passed to load_current_value, but not x" do
expect(resource.current_value.x).to eq "loaded 2 (d=desired_d, i=desired_i, name=blah)"
@@ -119,32 +119,16 @@ describe "Resource.load_current_value" do
end
end
- context "and a resource with no values set" do
- let(:resource) do
- e = self
- r = nil
- converge {
- r = public_send(e.resource_name, "blah") do
- end
- }
- r
- end
-
- it "the provider accesses values from load_current_value" do
- expect(resource.class.created_x).to eq "loaded 1 (name=blah)"
- end
- end
-
- let (:subresource_name) {
+ let (:subresource_name) do
:"load_current_value_subresource_dsl#{Namer.current_index}"
- }
- let (:subresource_class) {
+ end
+ let (:subresource_class) do
r = Class.new(resource_class) do
property :y, default: lazy { "default_y #{Namer.incrementing_value}" }
end
r.resource_name subresource_name
r
- }
+ end
# Pull on subresource_class to initialize it
before { subresource_class }
@@ -152,11 +136,11 @@ describe "Resource.load_current_value" do
let(:subresource) do
e = self
r = nil
- converge {
+ converge do
r = public_send(e.subresource_name, "blah") do
x "desired"
end
- }
+ end
r
end
@@ -170,14 +154,14 @@ describe "Resource.load_current_value" do
end
context "And a child resource class with load_current_value" do
- before {
+ before do
subresource_class.load_current_value do
y "loaded_y #{Namer.incrementing_value} (#{self.class.properties.sort_by { |name, p| name }.
select { |name, p| p.is_set?(self) }.
map { |name, p| "#{name}=#{p.get(self)}" }.
- join(", ") })"
+ join(", ")})"
end
- }
+ end
it "the overridden load_current_value is used" do
current_resource = subresource.current_value
@@ -187,15 +171,15 @@ describe "Resource.load_current_value" do
end
context "and a child resource class with load_current_value calling super()" do
- before {
+ before do
subresource_class.load_current_value do
super()
y "loaded_y #{Namer.incrementing_value} (#{self.class.properties.sort_by { |name, p| name }.
select { |name, p| p.is_set?(self) }.
map { |name, p| "#{name}=#{p.get(self)}" }.
- join(", ") })"
+ join(", ")})"
end
- }
+ end
it "the original load_current_value is called as well as the child one" do
current_resource = subresource.current_value
diff --git a/spec/integration/solo/solo_spec.rb b/spec/integration/solo/solo_spec.rb
index 5add4849e9..63525f0081 100644
--- a/spec/integration/solo/solo_spec.rb
+++ b/spec/integration/solo/solo_spec.rb
@@ -4,6 +4,7 @@ require "chef/run_lock"
require "chef/config"
require "timeout"
require "fileutils"
+require "chef/win32/security" if Chef::Platform.windows?
describe "chef-solo" do
include IntegrationSupport
@@ -15,7 +16,56 @@ describe "chef-solo" do
let(:cookbook_ancient_100_metadata_rb) { cb_metadata("ancient", "1.0.0") }
- let(:chef_solo) { "ruby bin/chef-solo --minimal-ohai" }
+ let(:chef_solo) { "ruby bin/chef-solo --legacy-mode --minimal-ohai" }
+
+ when_the_repository "creates nodes" do
+ let(:nodes_dir) { File.join(@repository_dir, "nodes") }
+ let(:node_file) { Dir[File.join(nodes_dir, "*.json")][0] }
+
+ before do
+ file "config/solo.rb", <<EOM
+chef_repo_path "#{@repository_dir}"
+EOM
+ result = shell_out("ruby bin/chef-solo -c \"#{path_to('config/solo.rb')}\" -l debug", :cwd => chef_dir)
+ result.error!
+ end
+
+ describe "on unix", :unix_only do
+ describe "the nodes directory" do
+ it "has the correct permissions" do
+ expect(File.stat(nodes_dir).mode.to_s(8)[-3..-1]).to eq("700")
+ end
+ end
+
+ describe "the node file" do
+ it "has the correct permissions" do
+ expect(File.stat(node_file).mode.to_s(8)[-4..-1]).to eq("0600")
+ end
+ end
+ end
+
+ describe "on windows", :windows_only do
+ let(:read_mask) { Chef::ReservedNames::Win32::API::Security::GENERIC_READ }
+ let(:write_mask) { Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE }
+ let(:execute_mask) { Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE }
+
+ describe "the nodes directory" do
+ it "has the correct permissions" do
+ expect(Chef::ReservedNames::Win32::File.file_access_check(nodes_dir, read_mask)).to be(true)
+ expect(Chef::ReservedNames::Win32::File.file_access_check(nodes_dir, write_mask)).to be(true)
+ expect(Chef::ReservedNames::Win32::File.file_access_check(nodes_dir, execute_mask)).to be(true)
+ end
+ end
+
+ describe "the node file" do
+ it "has the correct permissions" do
+ expect(Chef::ReservedNames::Win32::File.file_access_check(node_file, read_mask)).to be(true)
+ expect(Chef::ReservedNames::Win32::File.file_access_check(node_file, write_mask)).to be(true)
+ expect(Chef::ReservedNames::Win32::File.file_access_check(node_file, execute_mask)).to be(false)
+ end
+ end
+ end
+ end
when_the_repository "has a cookbook with a basic recipe" do
before do
@@ -112,7 +162,11 @@ EOM
file "cookbooks/x/recipes/default.rb", <<EOM
ruby_block "sleeping" do
block do
- sleep 10
+ retries = 200
+ while IO.read(Chef::Config[:log_location]) !~ /Chef client .* is running, will wait for it to finish and then run./
+ sleep 0.1
+ raise "we ran out of retries" if ( retries -= 1 ) <= 0
+ end
end
end
EOM
@@ -125,51 +179,38 @@ file_cache_path "#{path_to('config/cache')}"
EOM
# We have a timeout protection here so that if due to some bug
# run_lock gets stuck we can discover it.
- expect {
+ expect do
Timeout.timeout(120) do
chef_dir = File.join(File.dirname(__FILE__), "..", "..", "..")
- # Instantiate the first chef-solo run
- s1 = Process.spawn("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default' \
--l debug -L #{path_to('logs/runs.log')}", :chdir => chef_dir)
+ threads = []
- # Give it some time to progress
- sleep 5
+ # Instantiate the first chef-solo run
+ threads << Thread.new do
+ s1 = Process.spawn("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default' -l debug -L #{path_to('logs/runs.log')}", :chdir => chef_dir)
+ Process.waitpid(s1)
+ end
# Instantiate the second chef-solo run
- s2 = Process.spawn("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default' \
--l debug -L #{path_to('logs/runs.log')}", :chdir => chef_dir)
+ threads << Thread.new do
+ s2 = Process.spawn("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default' -l debug -L #{path_to('logs/runs.log')}", :chdir => chef_dir)
+ Process.waitpid(s2)
+ end
- Process.waitpid(s1)
- Process.waitpid(s2)
+ threads.each(&:join)
end
- }.not_to raise_error
+ end.not_to raise_error
# Unfortunately file / directory helpers in integration tests
# are implemented using before(:each) so we need to do all below
# checks in one example.
run_log = File.read(path_to("logs/runs.log"))
+ # second run should have a message which indicates it's waiting for the first run
+ expect(run_log).to match(/Chef client .* is running, will wait for it to finish and then run./)
+
# both of the runs should succeed
expect(run_log.lines.reject { |l| !l.include? "INFO: Chef Run complete in" }.length).to eq(2)
-
- # second run should have a message which indicates it's waiting for the first run
- pid_lines = run_log.lines.reject { |l| !l.include? "Chef-client pid:" }
- expect(pid_lines.length).to eq(2)
- pids = pid_lines.map { |l| l.split(" ").last }
- expect(run_log).to include("Chef client #{pids[0]} is running, will wait for it to finish and then run.")
-
- # second run should start after first run ends
- starts = [ ]
- ends = [ ]
- run_log.lines.each_with_index do |line, index|
- if line.include? "Chef-client pid:"
- starts << index
- elsif line.include? "INFO: Chef Run complete in"
- ends << index
- end
- end
- expect(starts[1]).to be > ends[0]
end
end
diff --git a/spec/scripts/ssl-serve.rb b/spec/scripts/ssl-serve.rb
index c05aa2c285..3f4e343926 100644
--- a/spec/scripts/ssl-serve.rb
+++ b/spec/scripts/ssl-serve.rb
@@ -42,6 +42,6 @@ webrick_opts = DEFAULT_OPTIONS.merge(server_opts)
pp :webrick_opts => webrick_opts
server = WEBrick::HTTPServer.new(webrick_opts)
-trap "INT" do server.shutdown end
+trap("INT") { server.shutdown }
server.start
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 2f6747c9af..b4d337eecd 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,6 +30,8 @@ $:.unshift File.expand_path("../..", __FILE__)
require "rubygems"
require "rspec/mocks"
+require "webmock/rspec"
+
$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
$:.unshift(File.expand_path("../lib", __FILE__))
$:.unshift(File.dirname(__FILE__))
@@ -66,6 +68,13 @@ require "chef/util/file_edit"
require "chef/config"
+require "chef/chef_fs/file_system_cache"
+
+require "chef/api_client_v1"
+
+require "chef/mixin/versioned_api"
+require "chef/server_api_versions"
+
if ENV["CHEF_FIPS"] == "1"
Chef::Config.init_openssl
end
@@ -102,6 +111,11 @@ TEST_PLATFORM = TEST_NODE["platform"]
TEST_PLATFORM_VERSION = TEST_NODE["platform_version"]
TEST_PLATFORM_FAMILY = TEST_NODE["platform_family"]
+provider_priority_map ||= nil
+resource_priority_map ||= nil
+provider_handler_map ||= nil
+resource_handler_map ||= nil
+
RSpec.configure do |config|
config.include(Matchers)
config.include(MockShellout::RSpec)
@@ -132,24 +146,31 @@ RSpec.configure do |config|
config.filter_run_excluding :not_supported_on_mac_osx_106 => true if mac_osx_106?
config.filter_run_excluding :not_supported_on_mac_osx => true if mac_osx?
config.filter_run_excluding :mac_osx_only => true if !mac_osx?
- config.filter_run_excluding :not_supported_on_win2k3 => true if windows_win2k3?
+ config.filter_run_excluding :not_supported_on_aix => true if aix?
config.filter_run_excluding :not_supported_on_solaris => true if solaris?
config.filter_run_excluding :not_supported_on_gce => true if gce?
config.filter_run_excluding :not_supported_on_nano => true if windows_nano_server?
- config.filter_run_excluding :win2k3_only => true unless windows_win2k3?
+ config.filter_run_excluding :win2012r2_only => true unless windows_2012r2?
config.filter_run_excluding :windows_2008r2_or_later => true unless windows_2008r2_or_later?
config.filter_run_excluding :windows64_only => true unless windows64?
config.filter_run_excluding :windows32_only => true unless windows32?
config.filter_run_excluding :windows_nano_only => true unless windows_nano_server?
+ config.filter_run_excluding :windows_gte_10 => true unless windows_gte_10?
+ config.filter_run_excluding :windows_lt_10 => true if windows_gte_10?
config.filter_run_excluding :ruby64_only => true unless ruby_64bit?
config.filter_run_excluding :ruby32_only => true unless ruby_32bit?
config.filter_run_excluding :windows_powershell_dsc_only => true unless windows_powershell_dsc?
config.filter_run_excluding :windows_powershell_no_dsc_only => true unless ! windows_powershell_dsc?
config.filter_run_excluding :windows_domain_joined_only => true unless windows_domain_joined?
config.filter_run_excluding :windows_not_domain_joined_only => true if windows_domain_joined?
+ # We think this line was causing rspec tests to not run on the Jenkins windows
+ # testers. If we ever fix it we should restore it.
+ # config.filter_run_excluding :windows_service_requires_assign_token => true if !STDOUT.isatty && !windows_user_right?("SeAssignPrimaryTokenPrivilege")
+ config.filter_run_excluding :windows_service_requires_assign_token => true
config.filter_run_excluding :solaris_only => true unless solaris?
config.filter_run_excluding :system_windows_service_gem_only => true unless system_windows_service_gem?
config.filter_run_excluding :unix_only => true unless unix?
+ config.filter_run_excluding :linux_only => true unless linux?
config.filter_run_excluding :aix_only => true unless aix?
config.filter_run_excluding :debian_family_only => true unless debian_family?
config.filter_run_excluding :supports_cloexec => true unless supports_cloexec?
@@ -164,20 +185,32 @@ RSpec.configure do |config|
config.filter_run_excluding :broken => true
config.filter_run_excluding :not_wpar => true unless wpar?
config.filter_run_excluding :not_supported_under_fips => true if fips?
+ config.filter_run_excluding :rhel => true unless rhel?
+ config.filter_run_excluding :rhel5 => true unless rhel5?
+ config.filter_run_excluding :rhel6 => true unless rhel6?
+ config.filter_run_excluding :rhel7 => true unless rhel7?
+ config.filter_run_excluding :intel_64bit => true unless intel_64bit?
+ config.filter_run_excluding :not_rhel => true if rhel?
+ config.filter_run_excluding :not_rhel5 => true if rhel5?
+ config.filter_run_excluding :not_rhel6 => true if rhel6?
+ config.filter_run_excluding :not_rhel7 => true if rhel7?
+ config.filter_run_excluding :not_intel_64bit => true if intel_64bit?
# these let us use chef: ">= 13" or ruby: "~> 2.0.0" or any other Gem::Dependency-style constraint
config.filter_run_excluding chef: DependencyProc.with(Chef::VERSION)
config.filter_run_excluding ruby: DependencyProc.with(RUBY_VERSION)
+ config.filter_run_excluding :choco_installed => true unless choco_installed?
+
running_platform_arch = `uname -m`.strip unless windows?
- config.filter_run_excluding :arch => lambda {|target_arch|
+ config.filter_run_excluding :arch => lambda { |target_arch|
running_platform_arch != target_arch
}
# Functional Resource tests that are provider-specific:
# context "on platforms that use useradd", :provider => {:user => Chef::Provider::User::Useradd}} do #...
- config.filter_run_excluding :provider => lambda {|criteria|
+ config.filter_run_excluding :provider => lambda { |criteria|
type, target_provider = criteria.first
node = TEST_NODE.dup
@@ -198,8 +231,15 @@ RSpec.configure do |config|
config.run_all_when_everything_filtered = true
config.before(:each) do
+ # it'd be nice to run this with connections blocked or only to localhost, but we do make lots
+ # of real connections, so cannot. we reset it to allow connections every time to avoid
+ # tests setting connections to be disabled and that state leaking into other tests.
+ WebMock.allow_net_connect!
+
Chef.reset!
+ Chef::ChefFS::FileSystemCache.instance.reset!
+
Chef::Config.reset
# By default, treat deprecation warnings as errors in tests.
@@ -207,12 +247,46 @@ RSpec.configure do |config|
# Set environment variable so the setting persists in child processes
ENV["CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS"] = "1"
+
+ # we don't perfectly reset the priority/handler maps here, but by dup'ing the top level hash we
+ # throw away all the garbage resources and providers that we setup. if we mutate something like
+ # :package then that'll carry over from test-to-test, but the solution would be to deep-dup on every
+ # single test we run which is much more expensive. by throwing away the garbage top level keys we
+ # significantly speed up test runs.
+ provider_handler_map ||= Chef.provider_handler_map.send(:map).dup
+ resource_handler_map ||= Chef.resource_handler_map.send(:map).dup
+ provider_priority_map ||= Chef.provider_priority_map.send(:map).dup
+ resource_priority_map ||= Chef.resource_priority_map.send(:map).dup
+ Chef.provider_handler_map.instance_variable_set(:@map, provider_handler_map.dup)
+ Chef.resource_handler_map.instance_variable_set(:@map, resource_handler_map.dup)
+ Chef.provider_priority_map.instance_variable_set(:@map, provider_priority_map.dup)
+ Chef.resource_priority_map.instance_variable_set(:@map, resource_priority_map.dup)
+ end
+
+ # This bit of jankiness guards against specs which accidentally drop privs when running as
+ # root -- which are nearly impossible to debug and so we bail out very hard if this
+ # condition ever happens. If a spec stubs Process.[e]uid this can throw a false positive
+ # which the spec must work around by unmocking Process.[e]uid to and_call_original in its
+ # after block.
+ if Process.euid == 0 && Process.uid == 0
+ config.after(:each) do
+ if Process.uid != 0
+ RSpec.configure { |c| c.fail_fast = true }
+ raise "rspec was invoked as root, but the last test dropped real uid to #{Process.uid}"
+ end
+ if Process.euid != 0
+ RSpec.configure { |c| c.fail_fast = true }
+ raise "rspec was invoked as root, but the last test dropped effective uid to #{Process.euid}"
+ end
+ end
end
# raise if anyone commits any test to CI with :focus set on it
- config.before(:example, :focus) do
- raise "This example was committed with `:focus` and should not have been"
- end if ENV["CI"]
+ if ENV["CI"]
+ config.before(:example, :focus) do
+ raise "This example was committed with `:focus` and should not have been"
+ end
+ end
config.before(:suite) do
ARGV.clear
@@ -220,6 +294,7 @@ RSpec.configure do |config|
end
require "webrick/utils"
+require "thread"
# Webrick uses a centralized/synchronized timeout manager. It works by
# starting a thread to check for timeouts on an interval. The timeout
@@ -238,7 +313,12 @@ module WEBrick
module Utils
class TimeoutHandler
def initialize
- @timeout_info = Hash.new
+ end
+
+ def register(*args)
+ end
+
+ def cancel(*args)
end
end
end
diff --git a/spec/stress/win32/security_spec.rb b/spec/stress/win32/security_spec.rb
index 58cf7b3357..3c03a657b2 100644
--- a/spec/stress/win32/security_spec.rb
+++ b/spec/stress/win32/security_spec.rb
@@ -49,21 +49,21 @@ describe "Chef::ReservedNames::Win32::Security", :windows_only do
end
it "should not leak when retrieving and reading the ACE from a file", :volatile do
- expect {
+ expect do
sids = Chef::ReservedNames::Win32::Security::SecurableObject.new(@monkeyfoo).security_descriptor.dacl.select { |ace| ace.sid }
GC.start
- }.not_to leak_memory(:warmup => 50, :iterations => 100)
+ end.not_to leak_memory(:warmup => 50, :iterations => 100)
end
it "should not leak when creating a new ACL and setting it on a file", :volatile do
securable_object = Security::SecurableObject.new(@monkeyfoo)
- expect {
+ expect do
securable_object.dacl = Chef::ReservedNames::Win32::Security::ACL.create([
Chef::ReservedNames::Win32::Security::ACE.access_allowed(Chef::ReservedNames::Win32::Security::SID.Everyone, Chef::ReservedNames::Win32::API::Security::GENERIC_READ),
Chef::ReservedNames::Win32::Security::ACE.access_denied(Chef::ReservedNames::Win32::Security::SID.from_account("Users"), Chef::ReservedNames::Win32::API::Security::GENERIC_ALL),
])
GC.start
- }.not_to leak_memory(:warmup => 50, :iterations => 100)
+ end.not_to leak_memory(:warmup => 50, :iterations => 100)
end
end
diff --git a/spec/support/chef_helpers.rb b/spec/support/chef_helpers.rb
index f64c14da4d..444e5a30c8 100644
--- a/spec/support/chef_helpers.rb
+++ b/spec/support/chef_helpers.rb
@@ -53,13 +53,11 @@ end
# This is a temporary fix to get tests passing on systems that have no `diff`
# until we can replace shelling out to `diff` with ruby diff-lcs
def has_diff?
- begin
- diff_cmd = Mixlib::ShellOut.new("diff -v")
- diff_cmd.run_command
- true
- rescue Errno::ENOENT
- false
- end
+ diff_cmd = Mixlib::ShellOut.new("diff -v")
+ diff_cmd.run_command
+ true
+rescue Errno::ENOENT
+ false
end
# This is a helper to determine if the ruby in the PATH contains
@@ -84,6 +82,28 @@ def canonicalize_path(path)
windows? ? path.tr("/", '\\') : path
end
+# Makes a temp directory with a canonical path on any platform.
+# Only really needed to work around an issue on Windows where
+# Ruby's temp library generates paths with short names.
+def make_canonical_temp_directory
+ temp_directory = Dir.mktmpdir
+ if windows?
+ # On Windows, temporary file / directory path names may have shortened
+ # subdirectory names due to reliance on the TMP and TEMP environment variables
+ # in some Windows APIs and duplicated logic in Ruby's temp file implementation.
+ # To work around this in the unit test context, we obtain the long (canonical)
+ # path name via a Windows system call so that this path name can be used
+ # in expectations that assume the ability to canonically name paths in comparisons.
+ # Note that this was not an issue prior to Ruby 2.2 -- with Ruby 2.2,
+ # some Chef code started to use long file names, while Ruby's temp file implementation
+ # continued to return the shortened names -- this would cause these particular tests to
+ # fail if the username happened to be longer than 8 characters.
+ Chef::ReservedNames::Win32::File.get_long_path_name(temp_directory)
+ else
+ temp_directory
+ end
+end
+
# Check if a cmd exists on the PATH
def which(cmd)
paths = ENV["PATH"].split(File::PATH_SEPARATOR) + [ "/bin", "/usr/bin", "/sbin", "/usr/sbin" ]
diff --git a/spec/support/lib/chef/provider/snakeoil.rb b/spec/support/lib/chef/provider/snakeoil.rb
index a42f889e74..f7769ebfc8 100644
--- a/spec/support/lib/chef/provider/snakeoil.rb
+++ b/spec/support/lib/chef/provider/snakeoil.rb
@@ -19,6 +19,7 @@
class Chef
class Provider
class SnakeOil < Chef::Provider
+ provides :cat
def load_current_resource
true
diff --git a/spec/support/lib/chef/resource/cat.rb b/spec/support/lib/chef/resource/cat.rb
index f62db4d2cd..38cbd60f0b 100644
--- a/spec/support/lib/chef/resource/cat.rb
+++ b/spec/support/lib/chef/resource/cat.rb
@@ -19,6 +19,7 @@
class Chef
class Resource
class Cat < Chef::Resource
+ provides :cat
attr_accessor :action
diff --git a/spec/support/lib/chef/resource/one_two_three_four.rb b/spec/support/lib/chef/resource/one_two_three_four.rb
index e46bede0fa..4dcbf1e60b 100644
--- a/spec/support/lib/chef/resource/one_two_three_four.rb
+++ b/spec/support/lib/chef/resource/one_two_three_four.rb
@@ -19,8 +19,7 @@
class Chef
class Resource
class OneTwoThreeFour < Chef::Resource
-
- attr_reader :i_can_count
+ provides :one_two_three_four
def i_can_count(tf)
@i_can_count = tf
diff --git a/spec/support/lib/chef/resource/openldap_includer.rb b/spec/support/lib/chef/resource/openldap_includer.rb
index 5a7651b10d..356d622e38 100644
--- a/spec/support/lib/chef/resource/openldap_includer.rb
+++ b/spec/support/lib/chef/resource/openldap_includer.rb
@@ -19,6 +19,8 @@
class Chef
class Resource
class OpenldapIncluder < Chef::Resource::LWRPBase
+ provides :openldap_includer
+
allowed_actions :run
default_action :run
end
diff --git a/spec/support/lib/chef/resource/with_state.rb b/spec/support/lib/chef/resource/with_state.rb
index f256bf4984..444191da62 100644
--- a/spec/support/lib/chef/resource/with_state.rb
+++ b/spec/support/lib/chef/resource/with_state.rb
@@ -22,6 +22,8 @@ require "chef/json_compat"
class Chef
class Resource
class WithState < Chef::Resource
+ provides :with_state
+
attr_accessor :state
end
end
diff --git a/spec/support/lib/chef/resource/zen_master.rb b/spec/support/lib/chef/resource/zen_master.rb
index f5137c18ec..9d6e5d46f7 100644
--- a/spec/support/lib/chef/resource/zen_master.rb
+++ b/spec/support/lib/chef/resource/zen_master.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,10 +22,9 @@ require "chef/json_compat"
class Chef
class Resource
class ZenMaster < Chef::Resource
+ provides :zen_master
allowed_actions :win, :score
- attr_reader :peace
-
def peace(tf)
@peace = tf
end
diff --git a/spec/support/mock/constant.rb b/spec/support/mock/constant.rb
index 3a23b4d8d8..7acd02e9d7 100644
--- a/spec/support/mock/constant.rb
+++ b/spec/support/mock/constant.rb
@@ -15,7 +15,7 @@ def mock_constants(constants)
begin
yield
ensure
- constants.each do |constant, val|
+ constants.each_key do |constant|
source_object, const_name = parse_constant(constant)
with_warnings(nil) { source_object.const_set(const_name, saved_constants[constant]) }
end
diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb
index 9ba56a15e3..8569523e05 100644
--- a/spec/support/platform_helpers.rb
+++ b/spec/support/platform_helpers.rb
@@ -1,9 +1,12 @@
require "fcntl"
require "chef/mixin/shell_out"
+require "ohai/mixin/http_helper"
require "ohai/mixin/gce_metadata"
+require "chef/mixin/powershell_out"
class ShellHelpers
extend Chef::Mixin::ShellOut
+ extend Chef::Mixin::PowershellOut
end
# magic stolen from bundler/spec/support/less_than_proc.rb
@@ -48,25 +51,33 @@ def windows_domain_joined?
computer_system["partofdomain"]
end
-def windows_win2k3?
- return false unless windows?
- wmi = WmiLite::Wmi.new
- host = wmi.first_of("Win32_OperatingSystem")
- (host["version"] && host["version"].start_with?("5.2"))
-end
-
def windows_2008r2_or_later?
return false unless windows?
- wmi = WmiLite::Wmi.new
- host = wmi.first_of("Win32_OperatingSystem")
- version = host["version"]
- return false unless version
- components = version.split(".").map do |component|
+ return false unless host_version
+ components = host_version.split(".").map do |component|
component.to_i
end
components.length >= 2 && components[0] >= 6 && components[1] >= 1
end
+def windows_2012r2?
+ return false unless windows?
+ (host_version && host_version.start_with?("6.3"))
+end
+
+def windows_gte_10?
+ return false unless windows?
+ Gem::Requirement.new(">= 10").satisfied_by?(Gem::Version.new(host_version))
+end
+
+def host_version
+ @host_version ||= begin
+ wmi = WmiLite::Wmi.new
+ host = wmi.first_of("Win32_OperatingSystem")
+ host["version"]
+ end
+end
+
def windows_powershell_dsc?
return false unless windows?
supports_dsc = false
@@ -84,6 +95,12 @@ def windows_nano_server?
Chef::Platform.windows_nano_server?
end
+def windows_user_right?(right)
+ return false unless windows?
+ require "chef/win32/security"
+ Chef::ReservedNames::Win32::Security.get_account_right(ENV["USERNAME"]).include?(right)
+end
+
def mac_osx_106?
if File.exists? "/usr/bin/sw_vers"
result = ShellHelpers.shell_out("/usr/bin/sw_vers")
@@ -126,6 +143,10 @@ def unix?
!windows?
end
+def linux?
+ !!(RUBY_PLATFORM =~ /linux/)
+end
+
def os_x?
!!(RUBY_PLATFORM =~ /darwin/)
end
@@ -138,6 +159,26 @@ def freebsd?
!!(RUBY_PLATFORM =~ /freebsd/)
end
+def intel_64bit?
+ !!(ohai[:kernel][:machine] == "x86_64")
+end
+
+def rhel?
+ !!(ohai[:platform_family] == "rhel")
+end
+
+def rhel5?
+ rhel? && !!(ohai[:platform_version].to_i == 5)
+end
+
+def rhel6?
+ rhel? && !!(ohai[:platform_version].to_i == 6)
+end
+
+def rhel7?
+ rhel? && !!(ohai[:platform_version].to_i == 7)
+end
+
def debian_family?
!!(ohai[:platform_family] == "debian")
end
@@ -169,7 +210,7 @@ def selinux_enabled?
when 0
return true
else
- raise RuntimeError, "Unknown exit code from command #{selinuxenabled_path}: #{cmd.exitstatus}"
+ raise "Unknown exit code from command #{selinuxenabled_path}: #{cmd.exitstatus}"
end
else
# We assume selinux is not enabled if selinux utils are not
@@ -203,12 +244,20 @@ def fips?
ENV["CHEF_FIPS"] == "1"
end
-class GCEDetector
- extend Ohai::Mixin::GCEMetadata
+class HttpHelper
+ extend Ohai::Mixin::HttpHelper
+ def self.logger
+ Chef::Log
+ end
end
def gce?
- GCEDetector.can_metadata_connect?(Ohai::Mixin::GCEMetadata::GCE_METADATA_ADDR, 80)
+ HttpHelper.can_socket_connect?(Ohai::Mixin::GCEMetadata::GCE_METADATA_ADDR, 80)
rescue SocketError
false
end
+
+def choco_installed?
+ result = ShellHelpers.powershell_out("choco --version")
+ result.stderr.empty? ? true : false
+end
diff --git a/spec/support/platforms/prof/gc.rb b/spec/support/platforms/prof/gc.rb
index 2e1b45c294..b494ff9646 100644
--- a/spec/support/platforms/prof/gc.rb
+++ b/spec/support/platforms/prof/gc.rb
@@ -35,12 +35,10 @@ module RSpec
end
def working_set_size
- begin
- ::GC.start
- ::GC::Profiler.result.scan(LINE_PATTERN)[-1][2].to_i if ::GC::Profiler.enabled?
- ensure
- ::GC::Profiler.clear
- end
+ ::GC.start
+ ::GC::Profiler.result.scan(LINE_PATTERN)[-1][2].to_i if ::GC::Profiler.enabled?
+ ensure
+ ::GC::Profiler.clear
end
def handle_count
diff --git a/spec/support/shared/context/client.rb b/spec/support/shared/context/client.rb
index d8676ef168..4b90fcaedd 100644
--- a/spec/support/shared/context/client.rb
+++ b/spec/support/shared/context/client.rb
@@ -20,7 +20,7 @@ shared_context "client" do
end
let(:ohai_system) do
- ohai = instance_double("Ohai::System", :all_plugins => true, :data => ohai_data)
+ ohai = instance_double("Ohai::System", :all_plugins => true, :data => ohai_data, logger: logger)
allow(ohai).to receive(:[]) do |k|
ohai_data[k]
end
@@ -37,19 +37,24 @@ shared_context "client" do
let(:json_attribs) { nil }
let(:client_opts) { {} }
+ let(:stdout) { STDOUT }
+ let(:stderr) { STDERR }
+
let(:client) do
Chef::Config[:event_loggers] = []
- Chef::Client.new(json_attribs, client_opts).tap do |c|
+ allow(Ohai::System).to receive(:new).and_return(ohai_system)
+ opts = client_opts.merge({ logger: logger })
+ Chef::Client.new(json_attribs, opts).tap do |c|
c.node = node
end
end
- before do
- Chef::Log.logger = Logger.new(StringIO.new)
+ let(:logger) { instance_double("Mixlib::Log::Child", trace: nil, debug: nil, warn: nil, info: nil, error: nil, fatal: nil) }
- # Node/Ohai data
- #Chef::Config[:node_name] = fqdn
- allow(Ohai::System).to receive(:new).and_return(ohai_system)
+ before do
+ stub_const("Chef::Client::STDOUT_FD", stdout)
+ stub_const("Chef::Client::STDERR_FD", stderr)
+ allow(client).to receive(:logger).and_return(logger)
end
end
@@ -68,9 +73,10 @@ shared_context "a client run" do
let(:api_client_exists?) { false }
let(:enable_fork) { false }
- let(:http_cookbook_sync) { double("Chef::ServerAPI (cookbook sync)") }
- let(:http_node_load) { double("Chef::ServerAPI (node)") }
- let(:http_node_save) { double("Chef::ServerAPI (node save)") }
+ let(:http_data_collector) { double("Chef::ServerAPI (data collector)") }
+ let(:http_cookbook_sync) { double("Chef::ServerAPI (cookbook sync)") }
+ let(:http_node_load) { double("Chef::ServerAPI (node)") }
+ let(:http_node_save) { double("Chef::ServerAPI (node save)") }
let(:reporting_rest_client) { double("Chef::ServerAPI (reporting client)") }
let(:runner) { instance_double("Chef::Runner") }
@@ -92,6 +98,13 @@ shared_context "a client run" do
end
end
+ def stub_for_data_collector_init
+ expect(Chef::ServerAPI).to receive(:new).
+ with(Chef::Config[:data_collector][:server_url], validate_utf8: false).
+ exactly(:once).
+ and_return(http_data_collector)
+ end
+
def stub_for_node_load
# Client.register will then turn around create another
# Chef::ServerAPI object, this time with the client key it got from the
@@ -121,12 +134,18 @@ shared_context "a client run" do
# ---Client#sync_cookbooks -- downloads the list of cookbooks to sync
#
expect_any_instance_of(Chef::CookbookSynchronizer).to receive(:sync_cookbooks)
- expect(Chef::ServerAPI).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_cookbook_sync)
+ expect(Chef::ServerAPI).to receive(:new).with(Chef::Config[:chef_server_url], version_class: Chef::CookbookManifestVersions).and_return(http_cookbook_sync)
expect(http_cookbook_sync).to receive(:post).
with("environments/_default/cookbook_versions", { :run_list => [] }).
and_return({})
end
+ def stub_for_required_recipe
+ response = Net::HTTPNotFound.new("1.1", "404", "Not Found")
+ exception = Net::HTTPServerException.new('404 "Not Found"', response)
+ expect(http_node_load).to receive(:get).with("required_recipe").and_raise(exception)
+ end
+
def stub_for_converge
# define me
end
@@ -148,14 +167,14 @@ shared_context "a client run" do
Chef::Config[:cache_path] = windows? ? 'C:\chef' : "/var/chef"
Chef::Config[:why_run] = false
Chef::Config[:audit_mode] = :enabled
-
- stub_const("Chef::Client::STDOUT_FD", stdout)
- stub_const("Chef::Client::STDERR_FD", stderr)
+ Chef::Config[:chef_guid] = "default-guid"
stub_rest_clean
stub_for_register
+ stub_for_data_collector_init
stub_for_node_load
stub_for_sync_cookbooks
+ stub_for_required_recipe
stub_for_converge
stub_for_audit
stub_for_node_save
@@ -230,8 +249,9 @@ shared_context "audit phase failed with error" do
end
shared_context "audit phase completed with failed controls" do
- let(:audit_runner) { instance_double("Chef::Audit::Runner", :failed? => true,
- :num_failed => 1, :num_total => 3) }
+ let(:audit_runner) do
+ instance_double("Chef::Audit::Runner", :failed? => true,
+ :num_failed => 1, :num_total => 3) end
let(:audit_error) do
err = Chef::Exceptions::AuditsFailed.new(audit_runner.num_failed, audit_runner.num_total)
diff --git a/spec/support/shared/examples/client.rb b/spec/support/shared/examples/client.rb
index 3c13cd767e..6479c9d582 100644
--- a/spec/support/shared/examples/client.rb
+++ b/spec/support/shared/examples/client.rb
@@ -14,6 +14,57 @@ shared_examples "a completed run" do
expect(node.automatic_attrs[:platform]).to eq(platform)
expect(node.automatic_attrs[:platform_version]).to eq(platform_version)
end
+
+ describe "setting node GUID" do
+ let(:chef_guid_path) { "/tmp/chef_guid" }
+ let(:chef_guid) { "test-test-test" }
+ let(:metadata_file) { "data_collector_metadata.json" }
+ let(:metadata_path) { Pathname.new(File.join(Chef::Config[:file_cache_path], metadata_file)).cleanpath.to_s }
+ let(:file) { instance_double(File) }
+
+ before do
+ allow(File).to receive(:read).and_call_original
+ Chef::Config[:chef_guid_path] = chef_guid_path
+ Chef::Config[:chef_guid] = nil
+ end
+
+ it "loads from the config" do
+ expect(File).to receive(:exists?).with(chef_guid_path).and_return(true)
+ expect(File).to receive(:read).with(chef_guid_path).and_return(chef_guid)
+ client.run
+ expect(Chef::Config[:chef_guid]).to eql(chef_guid)
+ expect(node.automatic_attrs[:chef_guid]).to eql(chef_guid)
+ end
+
+ it "loads from the data collector config" do
+ expect(File).to receive(:exists?).with(chef_guid_path).and_return(false)
+ expect(Chef::FileCache).to receive(:load).with(metadata_file).and_return("{\"node_uuid\": \"#{chef_guid}\"}")
+
+ expect(File).to receive(:open).with(chef_guid_path, "w+").and_yield(file)
+ expect(file).to receive(:write).with(chef_guid)
+
+ client.run
+ expect(Chef::Config[:chef_guid]).to eql(chef_guid)
+ expect(node.automatic_attrs[:chef_guid]).to eql(chef_guid)
+ end
+
+ it "creates a new one" do
+ expect(File).to receive(:exists?).with(chef_guid_path).and_return(false)
+ expect(File).to receive(:exists?).with(metadata_path).and_return(false)
+
+ expect(SecureRandom).to receive(:uuid).and_return(chef_guid).at_least(:once)
+
+ # we'll try and write the generated UUID to the data collector too, and that's ok
+ allow(File).to receive(:open).with(metadata_path, "w", 420)
+
+ expect(File).to receive(:open).with(chef_guid_path, "w+").and_yield(file)
+ expect(file).to receive(:write).with(chef_guid)
+
+ client.run
+ expect(Chef::Config[:chef_guid]).to eql(chef_guid)
+ expect(node.automatic_attrs[:chef_guid]).to eql(chef_guid)
+ end
+ end
end
shared_examples "a completed run with audit failure" do
diff --git a/spec/support/shared/functional/execute_resource.rb b/spec/support/shared/functional/execute_resource.rb
new file mode 100644
index 0000000000..1a14bb38c3
--- /dev/null
+++ b/spec/support/shared/functional/execute_resource.rb
@@ -0,0 +1,150 @@
+#
+# Author:: Adam Edwards (<adamed@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+shared_context "a non-admin Windows user" do
+ include Chef::Mixin::ShellOut
+
+ let(:windows_nonadmin_user_domain) { ENV["COMPUTERNAME"] }
+ let(:windows_nonadmin_user_qualified) { "#{windows_nonadmin_user_domain}\\#{windows_nonadmin_user}" }
+ let(:temp_profile_path) { "#{ENV['USERPROFILE']}\\..\\cheftesttempuser" }
+ before do
+ shell_out!("net.exe user /delete #{windows_nonadmin_user}", returns: [0, 2])
+
+ # Supply a profile path when creating a user to avoid an apparent Windows bug where deleting
+ # the user actually creates the profile when it did not immediately exist before executing
+ # net user /delete! For some reason, specifying an explicit path ensures that the path
+ # profile doesn't get created at deletion.
+ shell_out!("net.exe user /add #{windows_nonadmin_user} \"#{windows_nonadmin_user_password}\" /profilepath:#{temp_profile_path}")
+ end
+
+ after do
+ shell_out!("net.exe user /delete #{windows_nonadmin_user}", returns: [0, 2])
+ end
+end
+
+shared_context "alternate user identity" do
+ let(:windows_alternate_user) { "chef%02d%02d%02d" % [Time.now.year % 100, Time.now.month, Time.now.day] }
+ let(:windows_alternate_user_password) { "lj28;fx3T!x,2" }
+ let(:windows_alternate_user_qualified) { "#{ENV['COMPUTERNAME']}\\#{windows_alternate_user}" }
+
+ let(:windows_nonadmin_user) { windows_alternate_user }
+ let(:windows_nonadmin_user_password) { windows_alternate_user_password }
+
+ include_context "a non-admin Windows user"
+end
+
+shared_context "a command that can be executed as an alternate user" do
+ include_context "alternate user identity"
+
+ let(:script_output_dir) { Dir.mktmpdir }
+ let(:script_output_path) { File.join(script_output_dir, make_tmpname("chef_execute_identity_test")) }
+ let(:script_output) { File.read(script_output_path) }
+
+ include Chef::Mixin::ShellOut
+
+ before do
+ shell_out!("icacls \"#{script_output_dir.gsub(/\//, '\\')}\" /grant \"authenticated users:(F)\"")
+ end
+
+ after do
+ File.delete(script_output_path) if File.exists?(script_output_path)
+ Dir.rmdir(script_output_dir) if Dir.exist?(script_output_dir)
+ end
+end
+
+shared_examples_for "an execute resource that supports alternate user identity" do
+ context "when running on Windows", :windows_only, :windows_service_requires_assign_token do
+
+ include_context "a command that can be executed as an alternate user"
+
+ let(:windows_current_user) { ENV["USERNAME"] }
+ let(:windows_current_user_qualified) { "#{ENV['USERDOMAIN'] || ENV['COMPUTERNAME']}\\#{windows_current_user}" }
+ let(:resource_identity_command) { "powershell.exe -noprofile -command \"import-module microsoft.powershell.utility;([Security.Principal.WindowsPrincipal]([Security.Principal.WindowsIdentity]::GetCurrent())).identity.name | out-file -encoding ASCII '#{script_output_path}'\"" }
+
+ let(:execute_resource) do
+ resource.user(windows_alternate_user)
+ resource.password(windows_alternate_user_password)
+ resource.send(resource_command_property, resource_identity_command)
+ resource
+ end
+
+ it "executes the process as an alternate user" do
+ expect(windows_current_user.length).to be > 0
+ expect { execute_resource.run_action(:run) }.not_to raise_error
+ expect(script_output.chomp.length).to be > 0
+ expect(script_output.chomp.downcase).to eq(windows_alternate_user_qualified.downcase)
+ expect(script_output.chomp.downcase).not_to eq(windows_current_user.downcase)
+ expect(script_output.chomp.downcase).not_to eq(windows_current_user_qualified.downcase)
+ end
+
+ let(:windows_alternate_user_password_invalid) { "#{windows_alternate_user_password}x" }
+
+ it "raises an exception if the user's password is invalid" do
+ execute_resource.password(windows_alternate_user_password_invalid)
+ expect { execute_resource.run_action(:run) }.to raise_error(SystemCallError)
+ end
+ end
+end
+
+shared_examples_for "a resource with a guard specifying an alternate user identity" do
+ context "when running on Windows", :windows_only, :windows_service_requires_assign_token do
+ include_context "alternate user identity"
+
+ let(:resource_command_property) { :command }
+
+ let(:powershell_equal_to_alternate_user) { "-eq" }
+ let(:powershell_not_equal_to_alternate_user) { "-ne" }
+ let(:guard_identity_command) { "powershell.exe -noprofile -command \"import-module microsoft.powershell.utility;exit @(392,0)[[int32](([Security.Principal.WindowsPrincipal]([Security.Principal.WindowsIdentity]::GetCurrent())).Identity.Name #{comparison_to_alternate_user} '#{windows_alternate_user_qualified}')]\"" }
+
+ before do
+ resource.guard_interpreter(guard_interpreter_resource)
+ end
+
+ context "when the guard expression is true if the user is alternate and false otherwise" do
+ let(:comparison_to_alternate_user) { powershell_equal_to_alternate_user }
+
+ it "causes the resource to be updated for only_if" do
+ resource.only_if(guard_identity_command, { user: windows_alternate_user, password: windows_alternate_user_password })
+ resource.run_action(:run)
+ expect(resource).to be_updated_by_last_action
+ end
+
+ it "causes the resource to not be updated for not_if" do
+ resource.not_if(guard_identity_command, { user: windows_alternate_user, password: windows_alternate_user_password })
+ resource.run_action(:run)
+ expect(resource).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when the guard expression is false if the user is alternate and true otherwise" do
+ let(:comparison_to_alternate_user) { powershell_not_equal_to_alternate_user }
+
+ it "causes the resource not to be updated for only_if" do
+ resource.only_if(guard_identity_command, { user: windows_alternate_user, password: windows_alternate_user_password })
+ resource.run_action(:run)
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ it "causes the resource to be updated for not_if" do
+ resource.not_if(guard_identity_command, { user: windows_alternate_user, password: windows_alternate_user_password })
+ resource.run_action(:run)
+ expect(resource).to be_updated_by_last_action
+ end
+ end
+ end
+end
diff --git a/spec/support/shared/functional/file_resource.rb b/spec/support/shared/functional/file_resource.rb
index bb8db772be..d025a0806d 100644
--- a/spec/support/shared/functional/file_resource.rb
+++ b/spec/support/shared/functional/file_resource.rb
@@ -399,10 +399,10 @@ shared_examples_for "a configured file resource" do
content
end
- context "when the target file is a symlink", :not_supported_on_win2k3 do
- let(:symlink_target) {
+ context "when the target file is a symlink" do
+ let(:symlink_target) do
File.join(CHEF_SPEC_DATA, "file-test-target")
- }
+ end
describe "when configured not to manage symlink's target" do
before(:each) do
@@ -820,9 +820,9 @@ shared_examples_for "a configured file resource" do
end
describe "when path is specified with windows separator", :windows_only do
- let(:path) {
+ let(:path) do
File.join(test_file_dir, make_tmpname(file_base)).gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR)
- }
+ end
before do
@notified_resource = Chef::Resource.new("punk", resource.run_context)
@@ -1035,6 +1035,7 @@ shared_context Chef::Resource::File do
end
before do
+ FileUtils.rm_rf(test_file_dir)
FileUtils.mkdir_p(test_file_dir)
end
diff --git a/spec/support/shared/functional/http.rb b/spec/support/shared/functional/http.rb
index 7e9e3f34c5..76faaaed47 100644
--- a/spec/support/shared/functional/http.rb
+++ b/spec/support/shared/functional/http.rb
@@ -52,27 +52,27 @@ module ChefHTTPShared
# just a normal file
# (expected_content should be uncompressed)
- @api.get("/nyan_cat.png", 200) {
+ @api.get("/nyan_cat.png", 200) do
File.open(nyan_uncompressed_filename, "rb") do |f|
f.read
end
- }
+ end
# this ends in .gz, we do not uncompress it and drop it on the filesystem as a .gz file (the internet often lies)
# (expected_content should be compressed)
- @api.get("/nyan_cat.png.gz", 200, nil, { "Content-Type" => "application/gzip", "Content-Encoding" => "gzip" } ) {
+ @api.get("/nyan_cat.png.gz", 200, nil, { "Content-Type" => "application/gzip", "Content-Encoding" => "gzip" } ) do
File.open(nyan_compressed_filename, "rb") do |f|
f.read
end
- }
+ end
# this is an uncompressed file that was compressed by some mod_gzip-ish webserver thingy, so we will expand it
# (expected_content should be uncompressed)
- @api.get("/nyan_cat_compressed.png", 200, nil, { "Content-Type" => "application/gzip", "Content-Encoding" => "gzip" } ) {
+ @api.get("/nyan_cat_compressed.png", 200, nil, { "Content-Type" => "application/gzip", "Content-Encoding" => "gzip" } ) do
File.open(nyan_compressed_filename, "rb") do |f|
f.read
end
- }
+ end
#
# endpoints that set Content-Length correctly
@@ -83,11 +83,11 @@ module ChefHTTPShared
{
"Content-Length" => nyan_uncompressed_size.to_s,
}
- ) {
+ ) do
File.open(nyan_uncompressed_filename, "rb") do |f|
f.read
end
- }
+ end
# (expected_content should be uncompressed)
@api.get("/nyan_cat_content_length_compressed.png", 200, nil,
@@ -96,11 +96,11 @@ module ChefHTTPShared
"Content-Type" => "application/gzip",
"Content-Encoding" => "gzip",
}
- ) {
+ ) do
File.open(nyan_compressed_filename, "rb") do |f|
f.read
end
- }
+ end
#
# endpoints that simulate truncated downloads (bad content-length header)
@@ -111,11 +111,11 @@ module ChefHTTPShared
{
"Content-Length" => (nyan_uncompressed_size + 1).to_s,
}
- ) {
+ ) do
File.open(nyan_uncompressed_filename, "rb") do |f|
f.read
end
- }
+ end
# (expected_content should be uncompressed)
@api.get("/nyan_cat_truncated_compressed.png", 200, nil,
@@ -124,11 +124,11 @@ module ChefHTTPShared
"Content-Type" => "application/gzip",
"Content-Encoding" => "gzip",
}
- ) {
+ ) do
File.open(nyan_compressed_filename, "rb") do |f|
f.read
end
- }
+ end
#
# in the presence of a transfer-encoding header, we must ignore the content-length (this bad content-length should work)
@@ -140,11 +140,11 @@ module ChefHTTPShared
"Content-Length" => (nyan_uncompressed_size + 1).to_s,
"Transfer-Encoding" => "anything",
}
- ) {
+ ) do
File.open(nyan_uncompressed_filename, "rb") do |f|
f.read
end
- }
+ end
#
# 403 with a Content-Length
diff --git a/spec/support/shared/functional/securable_resource.rb b/spec/support/shared/functional/securable_resource.rb
index 506b96736c..2ca93bdcd0 100644
--- a/spec/support/shared/functional/securable_resource.rb
+++ b/spec/support/shared/functional/securable_resource.rb
@@ -81,7 +81,7 @@ shared_context "use Windows permissions", :windows_only do
SID ||= Chef::ReservedNames::Win32::Security::SID
ACE ||= Chef::ReservedNames::Win32::Security::ACE
ACL ||= Chef::ReservedNames::Win32::Security::ACL
- SecurableObject ||= Chef::ReservedNames::Win32::Security::SecurableObject
+ SecurableObject ||= Chef::ReservedNames::Win32::Security::SecurableObject # rubocop:disable Naming/ConstantName
end
def get_security_descriptor(path)
@@ -138,9 +138,9 @@ shared_context "use Windows permissions", :windows_only do
RSpec::Matchers.define :have_expected_properties do |mask, type, flags|
match do |ace|
- ace.mask == mask
- ace.type == type
- ace.flags == flags
+ ace.mask == mask &&
+ ace.type == type &&
+ ace.flags == flags
end
end
@@ -439,7 +439,7 @@ shared_examples_for "a securable resource without existing target" do
context "with a mode attribute" do
if windows?
- Security ||= Chef::ReservedNames::Win32::API::Security
+ Security ||= Chef::ReservedNames::Win32::API::Security # rubocop:disable Naming/ConstantName
end
it "respects mode in string form as an octal number" do
diff --git a/spec/support/shared/functional/win32_service.rb b/spec/support/shared/functional/win32_service.rb
index 0f9072bdef..3199caa34f 100644
--- a/spec/support/shared/functional/win32_service.rb
+++ b/spec/support/shared/functional/win32_service.rb
@@ -23,9 +23,7 @@ shared_context "using Win32::Service" do
# We can only uninstall when the service is stopped.
if test_service_state != "stopped"
::Win32::Service.send("stop", "spec-service")
- while test_service_state != "stopped"
- sleep 1
- end
+ sleep 1 while test_service_state != "stopped"
end
::Win32::Service.delete("spec-service")
@@ -39,7 +37,7 @@ shared_context "using Win32::Service" do
# Definition for the test-service
- let(:test_service) {
+ let(:test_service) do
{
:service_name => "spec-service",
:service_display_name => "Spec Test Service",
@@ -47,13 +45,13 @@ shared_context "using Win32::Service" do
:service_file_path => File.expand_path(File.join(File.dirname(__FILE__), "../../platforms/win32/spec_service.rb")),
:delayed_start => true,
}
- }
+ end
# Test service creates a file for us to verify that it is running.
# Since our test service is running as Local System we should look
# for the file it creates under SYSTEM temp directory
- let(:test_service_file) {
+ let(:test_service_file) do
"#{ENV['SystemDrive']}\\windows\\temp\\spec_service_file"
- }
+ end
end
diff --git a/spec/support/shared/functional/windows_script.rb b/spec/support/shared/functional/windows_script.rb
index 908198add4..e5ac0741bd 100644
--- a/spec/support/shared/functional/windows_script.rb
+++ b/spec/support/shared/functional/windows_script.rb
@@ -46,10 +46,6 @@ shared_context Chef::Resource::WindowsScript do
File.delete(script_output_path) if File.exists?(script_output_path)
end
- let!(:resource) do
- Chef::Resource::WindowsScript::Batch.new("Batch resource functional test", @run_context)
- end
-
shared_examples_for "a script resource with architecture attribute" do
context "with the given architecture attribute value" do
let(:expected_architecture) do
@@ -125,6 +121,55 @@ shared_context Chef::Resource::WindowsScript do
end
shared_examples_for "a Windows script running on Windows" do
+ shared_examples_for "a script that cannot be accessed by other users if they are not administrators" do
+ include Chef::Mixin::ShellOut
+
+ let(:script_provider) { resource.provider_for_action(:run) }
+ let(:script_file) { script_provider.script_file }
+ let(:script_file_path) { script_file.to_path }
+
+ let(:read_access_denied_command) { "::File.read('#{script_file_path}')" }
+ let(:modify_access_denied_command) { "::File.write('#{script_file_path}', 'stuff')" }
+ let(:delete_access_denied_command) { "::File.delete('#{script_file_path}')" }
+ let(:access_denied_sentinel) { 7334 }
+ let(:access_allowed_sentinel) { 1586 }
+ let(:access_command_invalid) { 0 }
+
+ let(:ruby_interpreter_path) { RbConfig.ruby }
+ let(:ruby_command_template) { "require 'FileUtils';status = 0;begin; #{ruby_access_command};rescue Exception => e; puts e; status = e.class == Errno::EACCES ? #{access_denied_sentinel} : #{access_allowed_sentinel};end;exit status" }
+ let(:command_template) { "set BUNDLE_GEMFILE=&#{ruby_interpreter_path} -e \"#{ruby_command_template}\"" }
+ let(:access_command) { command_template }
+
+ before do
+ expect(script_provider).to receive(:unlink_script_file)
+ resource.code("echo hi")
+ script_provider.action_run
+ end
+
+ after do
+ script_file.close! if script_file
+ ::File.delete(script_file.to_path) if script_file && ::File.exists?(script_file.to_path)
+ end
+
+ include_context "alternate user identity"
+
+ shared_examples_for "a script whose file system location cannot be accessed by other non-admin users" do
+ let(:ruby_access_command) { file_access_command }
+ it "generates a script in the local file system that prevents read access to other non-admin users" do
+ shell_out!(access_command, { user: windows_nonadmin_user, password: windows_nonadmin_user_password, returns: [access_denied_sentinel] })
+ end
+ end
+
+ context "when a different non-admin user attempts write (modify) to access the script" do
+ let(:file_access_command) { modify_access_denied_command }
+ it_behaves_like "a script whose file system location cannot be accessed by other non-admin users"
+ end
+
+ context "when a different non-admin user attempts write (delete) to access the script" do
+ let(:file_access_command) { delete_access_denied_command }
+ it_behaves_like "a script whose file system location cannot be accessed by other non-admin users"
+ end
+ end
describe "when the run action is invoked on Windows" do
it "executes the script code" do
@@ -132,6 +177,21 @@ shared_context Chef::Resource::WindowsScript do
resource.returns(0)
resource.run_action(:run)
end
+
+ context "the script is executed with the identity of the current user", :windows_service_requires_assign_token do
+ it_behaves_like "a script that cannot be accessed by other users if they are not administrators"
+ end
+
+ context "the script is executed with an alternate non-admin identity", :windows_service_requires_assign_token do
+ include_context "alternate user identity"
+
+ before do
+ resource.user(windows_alternate_user)
+ resource.password(windows_alternate_user_password)
+ end
+
+ it_behaves_like "a script that cannot be accessed by other users if they are not administrators"
+ end
end
context "when $env:TMP has a space" do
@@ -165,6 +225,11 @@ shared_context Chef::Resource::WindowsScript do
expect(resource.class).to receive(:new).and_call_original
expect(resource.should_skip?(:run)).to be_falsey
end
+
+ context "when this resource is used as a guard and it is specified with an alternate user identity" do
+ let(:guard_interpreter_resource) { resource.resource_name }
+ it_behaves_like "a resource with a guard specifying an alternate user identity"
+ end
end
context "when the architecture attribute is not set" do
@@ -181,6 +246,11 @@ shared_context Chef::Resource::WindowsScript do
let(:resource_architecture) { :x86_64 }
it_behaves_like "a script resource with architecture attribute"
end
+
+ describe "when running with an alternate user identity" do
+ let(:resource_command_property) { :code }
+ it_behaves_like "an execute resource that supports alternate user identity"
+ end
end
def get_windows_script_output(suffix = "")
diff --git a/spec/support/shared/integration/app_server_support.rb b/spec/support/shared/integration/app_server_support.rb
index 9bc48dbd93..e2bb3812ea 100644
--- a/spec/support/shared/integration/app_server_support.rb
+++ b/spec/support/shared/integration/app_server_support.rb
@@ -1,7 +1,7 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
# Author:: Ho-Sheng Hsiao (<hosh@chef.io>)
-# Copyright:: Copyright 2012-2016, 2013-2015 Chef Software, Inc.
+# Copyright:: Copyright 2012-2016 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,9 +33,7 @@ module AppServerSupport
end
end
Timeout.timeout(30) do
- until server && server.status == :Running
- sleep(0.01)
- end
+ sleep(0.01) until server && server.status == :Running
end
[server, thread]
end
diff --git a/spec/support/shared/integration/knife_support.rb b/spec/support/shared/integration/knife_support.rb
index 1e81cd115c..86764cad66 100644
--- a/spec/support/shared/integration/knife_support.rb
+++ b/spec/support/shared/integration/knife_support.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright 2013-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,10 +20,11 @@ require "chef/knife"
require "chef/application/knife"
require "logger"
require "chef/log"
+require "chef/chef_fs/file_system_cache"
module KnifeSupport
DEBUG = ENV["DEBUG"]
- def knife(*args)
+ def knife(*args, input: nil)
# Allow knife('role from file roles/blah.json') rather than requiring the
# arguments to be split like knife('role', 'from', 'file', 'roles/blah.json')
# If any argument will have actual spaces in it, the long form is required.
@@ -37,7 +38,7 @@ module KnifeSupport
Chef::Config[:concurrency] = 1
# Work on machines where we can't access /var
- checksums_cache_dir = Dir.mktmpdir("checksums") do |checksums_cache_dir|
+ Dir.mktmpdir("checksums") do |checksums_cache_dir|
Chef::Config[:cache_options] = {
:path => checksums_cache_dir,
:skip_expires => true,
@@ -47,6 +48,13 @@ module KnifeSupport
# ourselves, thank you very much
stdout = StringIO.new
stderr = StringIO.new
+
+ stdin = if input
+ StringIO.new(input)
+ else
+ STDIN
+ end
+
old_loggers = Chef::Log.loggers
old_log_level = Chef::Log.level
begin
@@ -56,19 +64,30 @@ module KnifeSupport
subcommand_class.load_deps
instance = subcommand_class.new(args)
+ # Load configs
+ instance.merge_configs
+
# Capture stdout/stderr
- instance.ui = Chef::Knife::UI.new(stdout, stderr, STDIN, {})
+ instance.ui = Chef::Knife::UI.new(stdout, stderr, stdin, instance.config.merge(disable_editing: true))
# Don't print stuff
Chef::Config[:verbosity] = ( DEBUG ? 2 : 0 )
instance.config[:config_file] = File.join(CHEF_SPEC_DATA, "null_config.rb")
+ # Ensure the ChefFS cache is empty
+ Chef::ChefFS::FileSystemCache.instance.reset!
+
# Configure chef with a (mostly) blank knife.rb
# We set a global and then mutate it in our stub knife.rb so we can be
# extra sure that we're not loading someone's real knife.rb and then
# running test scenarios against a real chef server. If things don't
# smell right, abort.
+ # To ensure that we don't pick up a user's credentials file we lie through our teeth about
+ # it's existence.
+ allow(File).to receive(:file?).and_call_original
+ allow(File).to receive(:file?).with(File.expand_path("~/.chef/credentials")).and_return(false)
+
$__KNIFE_INTEGRATION_FAILSAFE_CHECK = "ole"
instance.configure_chef
@@ -100,8 +119,6 @@ module KnifeSupport
end
end
- private
-
class KnifeResult
include ::RSpec::Matchers
diff --git a/spec/support/shared/shared_examples.rb b/spec/support/shared/shared_examples.rb
index 550fa2eb68..0c031bbafd 100644
--- a/spec/support/shared/shared_examples.rb
+++ b/spec/support/shared/shared_examples.rb
@@ -3,9 +3,9 @@
# Any object which defines a .to_json should import this test
shared_examples "to_json equivalent to Chef::JSONCompat.to_json" do
- let(:jsonable) {
+ let(:jsonable) do
raise "You must define the subject when including this test"
- }
+ end
it "should allow consumers to call #to_json or Chef::JSONCompat.to_json" do
expect(jsonable.to_json).to eq(Chef::JSONCompat.to_json(jsonable))
diff --git a/spec/support/shared/unit/api_versioning.rb b/spec/support/shared/unit/api_versioning.rb
index b61469a2d0..28141b73b1 100644
--- a/spec/support/shared/unit/api_versioning.rb
+++ b/spec/support/shared/unit/api_versioning.rb
@@ -43,13 +43,13 @@ shared_examples_for "user and client reregister" do
let(:generic_exception) { Exception.new }
let(:min_version) { "2" }
let(:max_version) { "5" }
- let(:return_hash_406) {
+ let(:return_hash_406) do
{
"min_version" => min_version,
"max_version" => max_version,
"request_version" => "30",
}
- }
+ end
context "when V0 is not supported by the server" do
context "when the exception is 406 and returns x-ops-server-api-version header" do
diff --git a/spec/support/shared/unit/application_dot_d.rb b/spec/support/shared/unit/application_dot_d.rb
index a8769d6d03..b94fa7c184 100644
--- a/spec/support/shared/unit/application_dot_d.rb
+++ b/spec/support/shared/unit/application_dot_d.rb
@@ -31,22 +31,26 @@ shared_examples_for "an application that loads a dot d" do
context "when client_d_dir is set to a directory with configuration" do
# We're not going to mock out globbing the directory. We want to
# make sure that we are correctly globbing.
- let(:client_d_dir) { Chef::Util::PathHelper.cleanpath(
- File.join(File.dirname(__FILE__), "../../../data/client.d_00")) }
+ let(:client_d_dir) do
+ Chef::Util::PathHelper.cleanpath(
+ File.join(File.dirname(__FILE__), "../../../data/client.d_00")) end
it "loads the configuration in order" do
expect(IO).to receive(:read).with(Pathname.new("#{client_d_dir}/00-foo.rb").cleanpath.to_s).and_return("foo 0")
expect(IO).to receive(:read).with(Pathname.new("#{client_d_dir}/01-bar.rb").cleanpath.to_s).and_return("bar 0")
+ expect(IO).to receive(:read).with(Pathname.new("#{client_d_dir}/02-strings.rb").cleanpath.to_s).and_return("strings 0")
allow(app).to receive(:apply_config).with(anything(), Chef::Config.platform_specific_path("/etc/chef/client.rb")).and_call_original.ordered
expect(app).to receive(:apply_config).with("foo 0", Pathname.new("#{client_d_dir}/00-foo.rb").cleanpath.to_s).and_call_original.ordered
expect(app).to receive(:apply_config).with("bar 0", Pathname.new("#{client_d_dir}/01-bar.rb").cleanpath.to_s).and_call_original.ordered
+ expect(app).to receive(:apply_config).with("strings 0", Pathname.new("#{client_d_dir}/02-strings.rb").cleanpath.to_s).and_call_original.ordered
app.reconfigure
end
end
context "when client_d_dir is set to a directory without configuration" do
- let(:client_d_dir) { Chef::Util::PathHelper.cleanpath(
- File.join(File.dirname(__FILE__), "../../data/client.d_01")) }
+ let(:client_d_dir) do
+ Chef::Util::PathHelper.cleanpath(
+ File.join(File.dirname(__FILE__), "../../data/client.d_01")) end
# client.d_01 has a nested folder with a rb file that if
# executed, would raise an exception. If it is executed,
@@ -60,8 +64,9 @@ shared_examples_for "an application that loads a dot d" do
context "when client_d_dir is set to a directory containing a directory named foo.rb" do
# foo.rb as a directory should be ignored
- let(:client_d_dir) { Chef::Util::PathHelper.cleanpath(
- File.join(File.dirname(__FILE__), "../../data/client.d_02")) }
+ let(:client_d_dir) do
+ Chef::Util::PathHelper.cleanpath(
+ File.join(File.dirname(__FILE__), "../../data/client.d_02")) end
it "does not raise an exception" do
expect { app.reconfigure }.not_to raise_error
diff --git a/spec/support/shared/unit/execute_resource.rb b/spec/support/shared/unit/execute_resource.rb
index e33247da42..c15b02612e 100644
--- a/spec/support/shared/unit/execute_resource.rb
+++ b/spec/support/shared/unit/execute_resource.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -76,14 +76,8 @@ shared_examples_for "an execute resource" do
expect(@resource.group).to eql(1)
end
- it "should accept an array for the execution path in Chef-12 and log deprecation message", chef: "< 13" do
- expect(Chef::Log).to receive(:warn).at_least(:once)
- @resource.path ["woot"]
- expect(@resource.path).to eql(["woot"])
- end
-
- it "should raise an exception in chef-13", chef: ">= 13" do
- expect(@resource.path [ "woot" ]).to raise_error
+ it "the old path property (that never worked) is not supported in chef >= 13" do
+ expect { @resource.path [ "woot" ] }.to raise_error
end
it "should accept an integer for the return code" do
@@ -106,6 +100,16 @@ shared_examples_for "an execute resource" do
expect(@resource.user).to eql(1)
end
+ it "should accept a string for the domain" do
+ @resource.domain "mothership"
+ expect(@resource.domain).to eql("mothership")
+ end
+
+ it "should accept a string for the password" do
+ @resource.password "we.funk!"
+ expect(@resource.password).to eql("we.funk!")
+ end
+
it "should accept a string for creates" do
@resource.creates "something"
expect(@resource.creates).to eql("something")
@@ -116,6 +120,37 @@ shared_examples_for "an execute resource" do
expect(@resource.live_stream).to be true
end
+ describe "the resource's sensitive attribute" do
+ it "should be false by default" do
+ expect(@resource.sensitive).to eq(false)
+ end
+
+ it "should be true if set to true" do
+ expect(@resource.sensitive).to eq(false)
+ @resource.sensitive true
+ expect(@resource.sensitive).to eq(true)
+ end
+
+ it "should be true if the password is non-nil" do
+ @resource.password("we.funk!")
+ expect(@resource.sensitive).to eq(true)
+ end
+
+ it "should be true if the password is non-nil but the value is explicitly set to false" do
+ @resource.password("we.funk!")
+ @resource.sensitive false
+ expect(@resource.sensitive).to eq(false)
+ end
+
+ # added this test to ensure setting of password property after or before sensitive does not matter
+ it "should be false if the sensitive is set before password property" do
+ @resource.sensitive false
+ @resource.password("we.funk!")
+ expect(@resource.sensitive).to eq(false)
+ end
+
+ end
+
describe "when it has cwd, environment, group, path, return value, and a user" do
before do
@resource.command("grep")
diff --git a/spec/support/shared/unit/provider/file.rb b/spec/support/shared/unit/provider/file.rb
index cb539ffbc3..d508e76b24 100644
--- a/spec/support/shared/unit/provider/file.rb
+++ b/spec/support/shared/unit/provider/file.rb
@@ -459,11 +459,11 @@ shared_examples_for Chef::Provider::File do
context "do_validate_content" do
before { setup_normal_file }
- let(:tempfile) {
+ let(:tempfile) do
t = double("Tempfile", :path => "/tmp/foo-bar-baz", :closed? => true)
allow(content).to receive(:tempfile).and_return(t)
t
- }
+ end
context "with user-supplied verifications" do
it "calls #verify on each verification with tempfile path" do
@@ -476,7 +476,15 @@ shared_examples_for Chef::Provider::File do
allow(File).to receive(:directory?).with("C:\\Windows\\system32/cmd.exe").and_return(false)
provider.new_resource.verify windows? ? "REM" : "true"
provider.new_resource.verify windows? ? "cmd.exe /c exit 1" : "false"
- expect { provider.send(:do_validate_content) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { provider.send(:do_validate_content) }.to raise_error(Chef::Exceptions::ValidationFailed, "Proposed content for #{provider.new_resource.path} failed verification #{windows? ? "cmd.exe /c exit 1" : "false"}")
+ end
+
+ it "does not show verification for sensitive resources" do
+ allow(File).to receive(:directory?).with("C:\\Windows\\system32/cmd.exe").and_return(false)
+ provider.new_resource.verify windows? ? "REM" : "true"
+ provider.new_resource.verify windows? ? "cmd.exe /c exit 1" : "false"
+ provider.new_resource.sensitive true
+ expect { provider.send(:do_validate_content) }.to raise_error(Chef::Exceptions::ValidationFailed, "Proposed content for #{provider.new_resource.path} failed verification [sensitive]")
end
end
end
@@ -683,6 +691,16 @@ shared_examples_for Chef::Provider::File do
end
end
+ context "in why run mode" do
+ before { Chef::Config[:why_run] = true }
+ after { Chef::Config[:why_run] = false }
+
+ it "does not modify new_resource" do
+ setup_missing_file
+ expect(provider).not_to receive(:load_resource_attributes_from_file).with(provider.new_resource)
+ provider.run_action(:create)
+ end
+ end
end
context "action delete" do
diff --git a/spec/support/shared/unit/provider/useradd_based_user_provider.rb b/spec/support/shared/unit/provider/useradd_based_user_provider.rb
index 6677a069ea..cc2d22f64f 100644
--- a/spec/support/shared/unit/provider/useradd_based_user_provider.rb
+++ b/spec/support/shared/unit/provider/useradd_based_user_provider.rb
@@ -18,13 +18,18 @@
# limitations under the License.
#
+# XXX: this used to be shared by solaris and linux classes, but at some
+# point became linux-specific. it is now a misnomer to call these 'shared'
+# examples and they should either realy get turned into shared examples or
+# should be copypasta'd back directly into the linux tests.
+
shared_examples_for "a useradd-based user provider" do |supported_useradd_options|
before(:each) do
@node = Chef::Node.new
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::User.new("adam", @run_context)
+ @new_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
@new_resource.comment "Adam Jacob"
@new_resource.uid 1000
@new_resource.gid 1000
@@ -35,7 +40,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
@new_resource.manage_home false
@new_resource.force false
@new_resource.non_unique false
- @current_resource = Chef::Resource::User.new("adam", @run_context)
+ @current_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
@current_resource.comment "Adam Jacob"
@current_resource.uid 1000
@current_resource.gid 1000
@@ -46,7 +51,6 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
@current_resource.manage_home false
@current_resource.force false
@current_resource.non_unique false
- @current_resource.supports({ :manage_home => false, :non_unique => false })
end
describe "when setting option" do
@@ -71,9 +75,9 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
end
it "should set the option for #{attribute} if the new resources #{attribute} is not nil, without homedir management (using real attributes)" do
- allow(@new_resource).to receive(:manage_home).and_return(false)
- allow(@new_resource).to receive(:non_unique).and_return(false)
- allow(@new_resource).to receive(:non_unique).and_return(false)
+ @new_resource.manage_home(false)
+ @new_resource.non_unique(false)
+ @new_resource.non_unique(false)
allow(@new_resource).to receive(attribute).and_return("hola")
expect(provider.universal_options).to eql([option, "hola"])
end
@@ -81,7 +85,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
it "should combine all the possible options" do
combined_opts = []
- supported_useradd_options.sort { |a, b| a[0] <=> b[0] }.each do |attribute, option|
+ supported_useradd_options.sort_by { |a| a[0] }.each do |attribute, option|
allow(@new_resource).to receive(attribute).and_return("hola")
combined_opts << option << "hola"
end
@@ -96,62 +100,49 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
it "should set useradd -r" do
@new_resource.system(true)
- expect(provider.useradd_options).to eq([ "-r" ])
+ expect(provider.useradd_options).to eq([ "-r", "-m" ])
end
end
describe "when the resource has a different home directory and supports home directory management" do
before do
- allow(@new_resource).to receive(:home).and_return("/wowaweea")
- allow(@new_resource).to receive(:supports).and_return({ :manage_home => true,
- :non_unique => false })
+ @new_resource.home "/wowaweea"
+ @new_resource.manage_home true
end
it "should set -m -d /homedir" do
- expect(provider.universal_options).to eq(%w{-d /wowaweea -m})
- expect(provider.useradd_options).to eq([])
+ expect(provider.universal_options).to eq(%w{-d /wowaweea})
+ expect(provider.usermod_options).to eq(%w{-m})
end
end
describe "when the resource has a different home directory and supports home directory management (using real attributes)" do
before do
- allow(@new_resource).to receive(:home).and_return("/wowaweea")
- allow(@new_resource).to receive(:manage_home).and_return(true)
- allow(@new_resource).to receive(:non_unique).and_return(false)
+ @new_resource.home("/wowaweea")
+ @new_resource.manage_home true
+ @new_resource.non_unique false
end
it "should set -m -d /homedir" do
- expect(provider.universal_options).to eql(%w{-d /wowaweea -m})
- expect(provider.useradd_options).to eq([])
+ expect(provider.universal_options).to eq(%w{-d /wowaweea})
+ expect(provider.usermod_options).to eq(%w{-m})
end
end
- describe "when the resource supports non_unique ids" do
- before do
- allow(@new_resource).to receive(:supports).and_return({ :manage_home => false,
- :non_unique => true })
- end
-
- it "should set -m -o" do
- expect(provider.universal_options).to eql([ "-o" ])
- end
+ it "when non_unique is false should not set -m" do
+ @new_resource.non_unique false
+ expect(provider.universal_options).to eql([ ])
end
- describe "when the resource supports non_unique ids (using real attributes)" do
- before do
- allow(@new_resource).to receive(:manage_home).and_return(false)
- allow(@new_resource).to receive(:non_unique).and_return(true)
- end
-
- it "should set -m -o" do
- expect(provider.universal_options).to eql([ "-o" ])
- end
+ it "when non_unique is true should set -o" do
+ @new_resource.non_unique true
+ expect(provider.universal_options).to eql([ "-o" ])
end
end
describe "when creating a user" do
before(:each) do
- @current_resource = Chef::Resource::User.new(@new_resource.name, @run_context)
+ @current_resource = Chef::Resource::User::LinuxUser.new(@new_resource.name, @run_context)
@current_resource.username(@new_resource.username)
provider.current_resource = @current_resource
provider.new_resource.manage_home true
@@ -188,7 +179,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
command.concat(["-p", "abracadabra"]) if supported_useradd_options.key?("password")
command.concat([ "-s", "/usr/bin/zsh",
"-u", "1000",
- "-r",
+ "-r", "-m",
"adam" ])
expect(provider).to receive(:shell_out!).with(*command).and_return(true)
provider.create_user
@@ -229,7 +220,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
end
it "CHEF-3429: does not set -m if we aren't changing the home directory" do
- expect(provider).to receive(:updating_home?).and_return(false)
+ expect(provider).to receive(:updating_home?).at_least(:once).and_return(false)
command = ["usermod",
"-g", "23",
"adam" ]
@@ -246,15 +237,12 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
end
it "should run userdel with the new resources user name and -r if manage_home is true" do
- @new_resource.supports({ :manage_home => true,
- :non_unique => false })
+ @new_resource.manage_home true
expect(provider).to receive(:shell_out!).with("userdel", "-r", @new_resource.username).and_return(true)
provider.remove_user
end
it "should run userdel with the new resources user name if non_unique is true" do
- @new_resource.supports({ :manage_home => false,
- :non_unique => true })
expect(provider).to receive(:shell_out!).with("userdel", @new_resource.username).and_return(true)
provider.remove_user
end
@@ -269,7 +257,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
describe "when checking the lock" do
# lazy initialize so we can modify stdout and stderr strings
let(:passwd_s_status) do
- double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => @stdout, :stderr => @stderr)
+ double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => @stdout, :stderr => @stderr, :error! => nil)
end
before(:each) do
@@ -284,7 +272,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
end
it "should return false if status begins with P" do
- expect(provider).to receive(:shell_out!).
+ expect(provider).to receive(:shell_out).
with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
and_return(passwd_s_status)
expect(provider.check_lock).to eql(false)
@@ -292,7 +280,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
it "should return false if status begins with N" do
@stdout = "root N"
- expect(provider).to receive(:shell_out!).
+ expect(provider).to receive(:shell_out).
with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
and_return(passwd_s_status)
expect(provider.check_lock).to eql(false)
@@ -300,57 +288,38 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
it "should return true if status begins with L" do
@stdout = "root L"
- expect(provider).to receive(:shell_out!).
+ expect(provider).to receive(:shell_out).
with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
and_return(passwd_s_status)
expect(provider.check_lock).to eql(true)
end
- it "should raise a Chef::Exceptions::User if passwd -S fails on anything other than redhat/centos" do
- @node.automatic_attrs[:platform] = "ubuntu"
- expect(provider).to receive(:shell_out!).
+ it "should raise a ShellCommandFailed exception if passwd -S exits with something other than 0 or 1" do
+ expect(passwd_s_status).to receive(:error!).and_raise(Mixlib::ShellOut::ShellCommandFailed)
+ expect(provider).to receive(:shell_out).
with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
and_return(passwd_s_status)
- expect(passwd_s_status).to receive(:exitstatus).and_return(1)
- expect { provider.check_lock }.to raise_error(Chef::Exceptions::User)
+ expect { provider.check_lock }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
end
- %w{redhat centos}.each do |os|
- it "should not raise a Chef::Exceptions::User if passwd -S exits with 1 on #{os} and the passwd package is version 0.73-1" do
- @node.automatic_attrs[:platform] = os
- expect(passwd_s_status).to receive(:exitstatus).and_return(1)
- expect(provider).to receive(:shell_out!).
- with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
- and_return(passwd_s_status)
- rpm_status = double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => "passwd-0.73-1\n", :stderr => "")
- expect(provider).to receive(:shell_out!).with("rpm -q passwd").and_return(rpm_status)
- expect { provider.check_lock }.not_to raise_error
- end
-
- it "should raise a Chef::Exceptions::User if passwd -S exits with 1 on #{os} and the passwd package is not version 0.73-1" do
- @node.automatic_attrs[:platform] = os
- expect(passwd_s_status).to receive(:exitstatus).and_return(1)
- expect(provider).to receive(:shell_out!).
- with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
- and_return(passwd_s_status)
- rpm_status = double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => "passwd-0.73-2\n", :stderr => "")
- expect(provider).to receive(:shell_out!).with("rpm -q passwd").and_return(rpm_status)
- expect { provider.check_lock }.to raise_error(Chef::Exceptions::User)
- end
-
- it "should raise a ShellCommandFailed exception if passwd -S exits with something other than 0 or 1 on #{os}" do
- @node.automatic_attrs[:platform] = os
- expect(provider).to receive(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed)
- expect { provider.check_lock }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
- end
+ it "should raise an error if the output isn't parsable" do
+ expect(passwd_s_status).to receive(:stdout).and_return("")
+ expect(passwd_s_status).to receive(:stderr).and_return("")
+ expect(provider).to receive(:shell_out).
+ with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
+ and_return(passwd_s_status)
+ expect { provider.check_lock }.to raise_error(Chef::Exceptions::User)
end
context "when in why run mode" do
before do
passwd_status = double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => "", :stderr => "passwd: user 'chef-test' does not exist\n")
- expect(provider).to receive(:shell_out!).
+ expect(provider).to receive(:shell_out).
with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
and_return(passwd_status)
+ # ubuntu returns 252 on user-does-not-exist so will raise if #error! is called or if
+ # shell_out! is used
+ allow(passwd_status).to receive(:error!).and_raise(Mixlib::ShellOut::ShellCommandFailed)
Chef::Config[:why_run] = true
end
@@ -420,9 +389,9 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
end
end
it "should return true if the current home does not exist but a home is specified by the new resource" do
- @new_resource = Chef::Resource::User.new("adam", @run_context)
- @current_resource = Chef::Resource::User.new("adam", @run_context)
- provider = Chef::Provider::User::Useradd.new(@new_resource, @run_context)
+ @new_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
+ @current_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
+ provider = Chef::Provider::User::Linux.new(@new_resource, @run_context)
provider.current_resource = @current_resource
@current_resource.home nil
@new_resource.home "/home/kitten"
diff --git a/spec/support/shared/unit/resource/static_provider_resolution.rb b/spec/support/shared/unit/resource/static_provider_resolution.rb
index 4ae5d4576f..e68b805d7d 100644
--- a/spec/support/shared/unit/resource/static_provider_resolution.rb
+++ b/spec/support/shared/unit/resource/static_provider_resolution.rb
@@ -33,13 +33,13 @@ def static_provider_resolution(opts = {})
platform_version = opts[:platform_version]
describe resource_class, "static provider initialization" do
- let(:node) {
+ let(:node) do
node = Chef::Node.new
node.automatic_attrs[:os] = os
node.automatic_attrs[:platform_family] = platform_family
node.automatic_attrs[:platform_version] = platform_version
node
- }
+ end
let(:events) { Chef::EventDispatch::Dispatcher.new }
let(:run_context) { Chef::RunContext.new(node, {}, events) }
let(:resource) { resource_class.new("foo", run_context) }
diff --git a/spec/support/shared/unit/script_resource.rb b/spec/support/shared/unit/script_resource.rb
index 732b07d4ef..a04da4b63e 100644
--- a/spec/support/shared/unit/script_resource.rb
+++ b/spec/support/shared/unit/script_resource.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,10 +34,6 @@ shared_examples_for "a script resource" do
expect(script_resource.command).to be nil
end
- it "should set command to the name on the resource", chef: "< 13" do
- expect(script_resource.command).to eql script_resource.name
- end
-
it "should accept a string for the code" do
script_resource.code "hey jude"
expect(script_resource.code).to eql("hey jude")
@@ -52,23 +48,19 @@ shared_examples_for "a script resource" do
expect { script_resource.command("foo") }.to raise_error(Chef::Exceptions::Script)
end
- it "should not raise an exception if users set command on the resource", chef: "< 13" do
- expect { script_resource.command("foo") }.not_to raise_error
- end
-
describe "when executing guards" do
- let(:resource) {
+ let(:resource) do
resource = script_resource
resource.run_context = run_context
resource.code "echo hi"
resource
- }
- let(:node) {
+ end
+ let(:node) do
node = Chef::Node.new
node.automatic[:platform] = "debian"
node.automatic[:platform_version] = "6.0"
node
- }
+ end
let(:events) { Chef::EventDispatch::Dispatcher.new }
let(:run_context) { Chef::RunContext.new(node, {}, events) }
diff --git a/spec/support/shared/unit/user_and_client_shared.rb b/spec/support/shared/unit/user_and_client_shared.rb
index e3481dfeca..6c31ca22d1 100644
--- a/spec/support/shared/unit/user_and_client_shared.rb
+++ b/spec/support/shared/unit/user_and_client_shared.rb
@@ -55,13 +55,13 @@ shared_examples_for "user or client create" do
end
context "when chef_key is returned by the server" do
- let(:chef_key) {
+ let(:chef_key) do
{
"chef_key" => {
"public_key" => "some_public_key",
},
}
- }
+ end
it "puts the public key into the objectr returned by create" do
expect(rest_v1).to receive(:post).with(url, payload).and_return(payload.merge(chef_key))
@@ -70,14 +70,14 @@ shared_examples_for "user or client create" do
end
context "when private_key is returned in chef_key" do
- let(:chef_key) {
+ let(:chef_key) do
{
"chef_key" => {
"public_key" => "some_public_key",
"private_key" => "some_private_key",
},
}
- }
+ end
it "puts the private key into the object returned by create" do
expect(rest_v1).to receive(:post).with(url, payload).and_return(payload.merge(chef_key))
diff --git a/spec/tiny_server.rb b/spec/tiny_server.rb
index a3711e4dbd..83c5bf4a42 100644
--- a/spec/tiny_server.rb
+++ b/spec/tiny_server.rb
@@ -20,97 +20,82 @@ require "rubygems"
require "webrick"
require "webrick/https"
require "rack"
-#require 'thin'
+require "thread"
require "singleton"
require "open-uri"
require "chef/config"
module TinyServer
- class Server < Rack::Server
-
- attr_writer :app
-
- def self.setup(options = nil, &block)
- tiny_app = new(options)
- app_code = Rack::Builder.new(&block).to_app
- tiny_app.app = app_code
- tiny_app
- end
-
- def shutdown
- server.shutdown
- end
- end
-
class Manager
# 5 == debug, 3 == warning
LOGGER = WEBrick::Log.new(STDOUT, 3)
DEFAULT_OPTIONS = {
- :server => "webrick",
- :Port => 9000,
- :Host => "localhost",
- :environment => :none,
- :Logger => LOGGER,
- :AccessLog => [] # Remove this option to enable the access log when debugging.
+ Port: 9000,
+ Host: "localhost",
+ Logger: LOGGER,
+ # SSLEnable: options[:ssl],
+ # SSLCertName: [ [ 'CN', WEBrick::Utils::getservername ] ],
+ AccessLog: [], # Remove this option to enable the access log when debugging.
}
- def initialize(options = nil)
- @options = options ? DEFAULT_OPTIONS.merge(options) : DEFAULT_OPTIONS
+ def initialize(**options)
+ @options = DEFAULT_OPTIONS.merge(options)
@creator = caller.first
end
- def start
+ attr_reader :options
+ attr_reader :creator
+ attr_reader :server
+
+ def start(timeout = 5)
+ raise "Server already started!" if server
+
+ # Create the server (but don't start yet)
+ start_queue = Queue.new
+ @server = create_server(StartCallback: proc { start_queue << true })
+
@server_thread = Thread.new do
- @server = Server.setup(@options) do
- run API.instance
- end
- @old_handler = trap(:INT, "EXIT")
- @server.start
+ # Ensure any exceptions will cause the main rspec thread to fail too
+ Thread.current.abort_on_exception = true
+ server.start
end
- block_until_started
- trap(:INT, @old_handler)
- end
- def url
- "http://localhost:#{@options[:Port]}"
+ # Wait for the StartCallback to tell us we've started
+ Timeout.timeout(timeout) do
+ start_queue.pop
+ end
end
- def block_until_started
- 200.times do
- if started? && !@server.nil?
- return true
+ def stop(timeout = 5)
+ if server
+ server.shutdown
+ @server = nil
+ end
+
+ if server_thread
+ begin
+ # Wait for a normal shutdown
+ server_thread.join(timeout)
+ rescue
+ # If it wouldn't shut down normally, kill it.
+ server_thread.kill
+ server_thread.join(timeout)
end
+ @server_thread = nil
end
- raise "ivar weirdness" if started? && @server.nil?
- raise "TinyServer failed to boot :/"
- end
-
- def started?
- open(url)
- true
- rescue OpenURI::HTTPError
- true
- rescue Errno::ECONNREFUSED, EOFError, Errno::ECONNRESET => e
- sleep 0.1
- true
- # If the host has ":::1 localhost" in its hosts file and if IPv6
- # is not enabled we can get NetworkUnreachable exception...
- rescue Errno::ENETUNREACH, Net::ReadTimeout, IO::EAGAINWaitReadable,
- Errno::EHOSTUNREACH => e
- sleep 0.1
- false
- end
-
- def stop
- # yes, this is terrible.
- @server.shutdown
- @server_thread.kill
- @server_thread.join
- @server_thread = nil
end
+ private
+
+ attr_reader :server_thread
+
+ def create_server(**extra_options)
+ server = WEBrick::HTTPServer.new(**options, **extra_options)
+ server.mount("/", Rack::Handler::WEBrick, API.instance)
+ server
+ end
end
class API
diff --git a/spec/unit/api_client_v1_spec.rb b/spec/unit/api_client_v1_spec.rb
index 8c90d5ad38..9c643fa492 100644
--- a/spec/unit/api_client_v1_spec.rb
+++ b/spec/unit/api_client_v1_spec.rb
@@ -326,13 +326,13 @@ describe Chef::ApiClientV1 do
describe "Versioned API Interactions" do
let(:response_406) { OpenStruct.new(:code => "406") }
let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
- let(:payload) {
+ let(:payload) do
{
:name => "some_name",
:validator => true,
:admin => true,
}
- }
+ end
before do
@client = Chef::ApiClientV1.new
diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb
index d223d55d2b..0af3916134 100644
--- a/spec/unit/application/apply_spec.rb
+++ b/spec/unit/application/apply_spec.rb
@@ -24,13 +24,13 @@ describe Chef::Application::Apply do
allow(@app).to receive(:configure_logging).and_return(true)
allow(Chef::Log).to receive(:debug).with("FIPS mode is enabled.")
@recipe_text = "package 'nyancat'"
- Chef::Config[:solo] = true
+ Chef::Config[:solo_legacy_mode] = true
end
describe "configuring the application" do
it "should set solo mode to true" do
@app.reconfigure
- expect(Chef::Config[:solo]).to be_truthy
+ expect(Chef::Config[:solo_legacy_mode]).to be_truthy
end
end
describe "read_recipe_file" do
@@ -52,7 +52,8 @@ describe Chef::Application::Apply do
describe "when recipe is nil" do
it "should raise a fatal with the missing filename message" do
- expect(Chef::Application).to receive(:fatal!).with("No recipe file was provided", 1)
+ expect(Chef::Application).to receive(:fatal!).with("No recipe file was provided",
+ Chef::Exceptions::RecipeNotFound.new)
@app.read_recipe_file(nil)
end
end
@@ -61,7 +62,8 @@ describe Chef::Application::Apply do
allow(File).to receive(:exist?).with(@recipe_path).and_return(false)
end
it "should raise a fatal with the file doesn't exist message" do
- expect(Chef::Application).to receive(:fatal!).with(/^No file exists at/, 1)
+ expect(Chef::Application).to receive(:fatal!).with(/^No file exists at/,
+ Chef::Exceptions::RecipeNotFound.new)
@app.read_recipe_file(@recipe_file_name)
end
end
diff --git a/spec/unit/application/client_spec.rb b/spec/unit/application/client_spec.rb
index 6765ca93ae..de6972b625 100644
--- a/spec/unit/application/client_spec.rb
+++ b/spec/unit/application/client_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@junglist.gen.nz>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -74,6 +74,8 @@ describe Chef::Application::Client, "reconfigure" do
end
before do
+ Chef::Config.reset
+
allow(Kernel).to receive(:trap).and_return(:ok)
allow(::File).to receive(:read).and_call_original
allow(::File).to receive(:read).with(Chef::Config.platform_specific_path("/etc/chef/client.rb")).and_return("")
@@ -105,6 +107,7 @@ describe Chef::Application::Client, "reconfigure" do
shared_examples "sets the configuration" do |cli_arguments, expected_config|
describe cli_arguments do
before do
+ cli_arguments ||= ""
ARGV.replace(cli_arguments.split)
app.reconfigure
end
@@ -141,6 +144,69 @@ describe Chef::Application::Client, "reconfigure" do
:daemonize => true
end
end
+
+ describe "--[no]-fork" do
+ before do
+ Chef::Config[:interval] = nil # FIXME: we're overriding the before block setting this
+ end
+
+ context "by default" do
+ it_behaves_like "sets the configuration", "", client_fork: false
+ end
+
+ context "with --fork" do
+ it_behaves_like "sets the configuration", "--fork", client_fork: true
+ end
+
+ context "with --no-fork" do
+ it_behaves_like "sets the configuration", "--no-fork", client_fork: false
+ end
+
+ context "with an interval" do
+ it_behaves_like "sets the configuration", "--interval 1800", client_fork: true
+ end
+
+ context "with once" do
+ it_behaves_like "sets the configuration", "--once", client_fork: false
+ end
+
+ context "with daemonize", :unix_only do
+ it_behaves_like "sets the configuration", "--daemonize", client_fork: true
+ end
+ end
+
+ describe "--config-option" do
+ context "with a single value" do
+ it_behaves_like "sets the configuration", "--config-option chef_server_url=http://example",
+ :chef_server_url => "http://example"
+ end
+
+ context "with two values" do
+ it_behaves_like "sets the configuration", "--config-option chef_server_url=http://example --config-option policy_name=web",
+ :chef_server_url => "http://example", :policy_name => "web"
+ end
+
+ context "with a boolean value" do
+ it_behaves_like "sets the configuration", "--config-option minimal_ohai=true",
+ :minimal_ohai => true
+ end
+
+ context "with an empty value" do
+ it "should terminate with message" do
+ expect(Chef::Application).to receive(:fatal!).with('Unparsable config option ""').and_raise("so ded")
+ ARGV.replace(["--config-option", ""])
+ expect { app.reconfigure }.to raise_error "so ded"
+ end
+ end
+
+ context "with an invalid value" do
+ it "should terminate with message" do
+ expect(Chef::Application).to receive(:fatal!).with('Unparsable config option "asdf"').and_raise("so ded")
+ ARGV.replace(["--config-option", "asdf"])
+ expect { app.reconfigure }.to raise_error "so ded"
+ end
+ end
+ end
end
describe "when configured to not fork the client process" do
@@ -151,21 +217,16 @@ describe Chef::Application::Client, "reconfigure" do
Chef::Config[:splay] = nil
end
- context "when interval is given" do
- before do
- Chef::Config[:interval] = 600
- allow(ChefConfig).to receive(:windows?).and_return(false)
- end
-
- it "should terminate with message" do
- expect(Chef::Application).to receive(:fatal!).with(
-"Unforked chef-client interval runs are disabled in Chef 12.
+ it "should terminal with message when interval is given" do
+ Chef::Config[:interval] = 600
+ allow(ChefConfig).to receive(:windows?).and_return(false)
+ expect(Chef::Application).to receive(:fatal!).with(
+ "Unforked chef-client interval runs are disabled in Chef 12.
Configuration settings:
interval = 600 seconds
Enable chef-client interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options."
- )
- app.reconfigure
- end
+ )
+ app.reconfigure
end
context "when interval is given on windows" do
@@ -222,6 +283,7 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
before do
allow(@app).to receive(:interval_sleep).with(wait_secs).and_return true
allow(@app).to receive(:interval_sleep).with(0).and_call_original
+ allow(@app).to receive(:time_to_sleep).and_return(1)
end
it "sleeps for the amount of time passed" do
@@ -385,6 +447,7 @@ describe Chef::Application::Client, "configure_chef" do
before do
@original_argv = ARGV.dup
ARGV.clear
+ allow(::File).to receive(:read).and_call_original
allow(::File).to receive(:read).with(Chef::Config.platform_specific_path("/etc/chef/client.rb")).and_return("")
app.configure_chef
end
@@ -484,6 +547,7 @@ describe Chef::Application::Client, "run_application", :unix_only do
end
it "shouldn't sleep when sent USR1" do
+ allow(@app).to receive(:interval_sleep).and_return true
allow(@app).to receive(:interval_sleep).with(0).and_call_original
pid = fork do
@app.run_application
diff --git a/spec/unit/application/exit_code_spec.rb b/spec/unit/application/exit_code_spec.rb
new file mode 100644
index 0000000000..7783cf3ed7
--- /dev/null
+++ b/spec/unit/application/exit_code_spec.rb
@@ -0,0 +1,144 @@
+#
+# Author:: Steven Murawski (<smurawski@chef.io>)
+# Copyright:: Copyright 2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef"
+require "spec_helper"
+
+require "chef/application/exit_code"
+
+describe Chef::Application::ExitCode do
+
+ let(:exit_codes) { Chef::Application::ExitCode }
+
+ let(:valid_rfc_exit_codes) { Chef::Application::ExitCode::VALID_RFC_062_EXIT_CODES.values }
+
+ context "Validates the return codes from RFC 062" do
+
+ before do
+ allow(Chef::Config).to receive(:[]).with(:exit_status).and_return(:enabled)
+ end
+
+ it "validates a SUCCESS return code of 0" do
+ expect(valid_rfc_exit_codes.include?(0)).to eq(true)
+ end
+
+ it "validates a GENERIC_FAILURE return code of 1" do
+ expect(valid_rfc_exit_codes.include?(1)).to eq(true)
+ end
+
+ it "validates a SIGINT_RECEIVED return code of 2" do
+ expect(valid_rfc_exit_codes.include?(2)).to eq(true)
+ end
+
+ it "validates a SIGTERM_RECEIVED return code of 3" do
+ expect(valid_rfc_exit_codes.include?(3)).to eq(true)
+ end
+
+ it "validates a AUDIT_MODE_FAILURE return code of 42" do
+ expect(valid_rfc_exit_codes.include?(42)).to eq(true)
+ end
+
+ it "validates a REBOOT_SCHEDULED return code of 35" do
+ expect(valid_rfc_exit_codes.include?(35)).to eq(true)
+ end
+
+ it "validates a REBOOT_NEEDED return code of 37" do
+ expect(valid_rfc_exit_codes.include?(37)).to eq(true)
+ end
+
+ it "validates a REBOOT_FAILED return code of 41" do
+ expect(valid_rfc_exit_codes.include?(41)).to eq(true)
+ end
+
+ it "validates a CLIENT_UPGRADED return code of 213" do
+ expect(valid_rfc_exit_codes.include?(213)).to eq(true)
+ end
+ end
+
+ context "when Chef validates exit codes" do
+
+ it "does write a warning on non-standard exit codes" do
+ expect(Chef::Log).to receive(:warn).with(
+ /^Chef attempted to exit with a non-standard exit code of 151/)
+ expect(exit_codes.normalize_exit_code(151)).to eq(1)
+ end
+
+ it "returns a GENERIC_FAILURE for non-RFC exit codes" do
+ expect(exit_codes.normalize_exit_code(151)).to eq(1)
+ end
+
+ it "returns GENERIC_FAILURE when no exit code is specified" do
+ expect(exit_codes.normalize_exit_code()).to eq(1)
+ end
+
+ it "returns SIGINT_RECEIVED when a SIGINT is received" do
+ expect(exit_codes.normalize_exit_code(Chef::Exceptions::SigInt.new("BOOM"))).to eq(2)
+ end
+
+ it "returns SIGTERM_RECEIVED when a SIGTERM is received" do
+ expect(exit_codes.normalize_exit_code(Chef::Exceptions::SigTerm.new("BOOM"))).to eq(3)
+ end
+
+ it "returns GENERIC_FAILURE when an exception is specified" do
+ expect(exit_codes.normalize_exit_code(Exception.new("BOOM"))).to eq(1)
+ end
+
+ it "returns AUDIT_MODE_FAILURE when there is an audit error" do
+ audit_error = Chef::Exceptions::AuditError.new("BOOM")
+ runtime_error = Chef::Exceptions::RunFailedWrappingError.new(audit_error)
+ expect(exit_codes.normalize_exit_code(runtime_error)).to eq(42)
+ end
+
+ it "returns REBOOT_SCHEDULED when there is an reboot requested" do
+ reboot_error = Chef::Exceptions::Reboot.new("BOOM")
+ runtime_error = Chef::Exceptions::RunFailedWrappingError.new(reboot_error)
+ expect(exit_codes.normalize_exit_code(runtime_error)).to eq(35)
+ end
+
+ it "returns REBOOT_FAILED when the reboot command fails" do
+ reboot_error = Chef::Exceptions::RebootFailed.new("BOOM")
+ runtime_error = Chef::Exceptions::RunFailedWrappingError.new(reboot_error)
+ expect(exit_codes.normalize_exit_code(runtime_error)).to eq(41)
+ end
+
+ it "returns REBOOT_NEEDED when a reboot is pending" do
+ reboot_error = Chef::Exceptions::RebootPending.new("BOOM")
+ runtime_error = Chef::Exceptions::RunFailedWrappingError.new(reboot_error)
+ expect(exit_codes.normalize_exit_code(runtime_error)).to eq(37)
+ end
+
+ it "returns CLIENT_UPGRADED when the client was upgraded during converge" do
+ client_upgraded_error = Chef::Exceptions::ClientUpgraded.new("BOOM")
+ runtime_error = Chef::Exceptions::RunFailedWrappingError.new(client_upgraded_error)
+ expect(exit_codes.normalize_exit_code(runtime_error)).to eq(213)
+ end
+
+ it "returns SIGINT_RECEIVED when a SIGINT is received." do
+ sigint_error = Chef::Exceptions::SigInt.new("BOOM")
+ runtime_error = Chef::Exceptions::RunFailedWrappingError.new(sigint_error)
+ expect(exit_codes.normalize_exit_code(runtime_error)).to eq(2)
+ end
+
+ it "returns SIGTERM_RECEIVED when a SIGTERM is received." do
+ sigterm_error = Chef::Exceptions::SigTerm.new("BOOM")
+ runtime_error = Chef::Exceptions::RunFailedWrappingError.new(sigterm_error)
+ expect(exit_codes.normalize_exit_code(runtime_error)).to eq(3)
+ end
+ end
+
+end
diff --git a/spec/unit/application/solo_spec.rb b/spec/unit/application/solo_spec.rb
index 85799d73db..5b9dbeaafd 100644
--- a/spec/unit/application/solo_spec.rb
+++ b/spec/unit/application/solo_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@junglist.gen.nz>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,141 +30,196 @@ describe Chef::Application::Solo do
Chef::Config[:json_attribs] = false
Chef::Config[:solo] = true
+ Chef::Config[:solo_legacy_mode] = true
# protect the unit tests against accidental --delete-entire-chef-repo from firing
# for real during tests. DO NOT delete this line.
expect(FileUtils).not_to receive(:rm_rf)
end
- describe "configuring the application" do
- it "should call set_specific_recipes" do
- expect(app).to receive(:set_specific_recipes)
- app.reconfigure
- end
-
- it "should set solo mode to true" do
- app.reconfigure
- expect(Chef::Config[:solo]).to be_truthy
- end
+ context "in legacy mode" do
+ describe "configuring the application" do
+ it "should call set_specific_recipes" do
+ expect(app).to receive(:set_specific_recipes)
+ app.reconfigure
+ end
- it "should set audit-mode to :disabled" do
- app.reconfigure
- expect(Chef::Config[:audit_mode]).to be :disabled
- end
+ it "should set solo mode to true" do
+ app.reconfigure
+ expect(Chef::Config[:solo]).to be_truthy
+ end
- describe "when configured to not fork the client process" do
- before do
- Chef::Config[:client_fork] = false
- Chef::Config[:daemonize] = false
- Chef::Config[:interval] = nil
- Chef::Config[:splay] = nil
+ it "should set audit-mode to :disabled" do
+ app.reconfigure
+ expect(Chef::Config[:audit_mode]).to be :disabled
end
- context "when interval is given" do
+ describe "when configured to not fork the client process" do
before do
- Chef::Config[:interval] = 600
+ Chef::Config[:client_fork] = false
+ Chef::Config[:daemonize] = false
+ Chef::Config[:interval] = nil
+ Chef::Config[:splay] = nil
end
- it "should terminate with message" do
- expect(Chef::Application).to receive(:fatal!).with(
-"Unforked chef-client interval runs are disabled in Chef 12.
+ context "when interval is given" do
+ before do
+ Chef::Config[:interval] = 600
+ end
+
+ it "should terminate with message" do
+ expect(Chef::Application).to receive(:fatal!).with(
+ "Unforked chef-client interval runs are disabled in Chef 12.
Configuration settings:
interval = 600 seconds
Enable chef-client interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options."
- )
+ )
+ app.reconfigure
+ end
+ end
+ end
+
+ describe "when in daemonized mode and no interval has been set" do
+ before do
+ Chef::Config[:daemonize] = true
+ end
+
+ it "should set the interval to 1800" do
+ Chef::Config[:interval] = nil
app.reconfigure
+ expect(Chef::Config[:interval]).to eq(1800)
end
end
- end
- describe "when in daemonized mode and no interval has been set" do
- before do
- Chef::Config[:daemonize] = true
+ describe "when the json_attribs configuration option is specified" do
+ let(:json_attribs) { { "a" => "b" } }
+ let(:config_fetcher) { double(Chef::ConfigFetcher, :fetch_json => json_attribs) }
+ let(:json_source) { "https://foo.com/foo.json" }
+
+ before do
+ Chef::Config[:json_attribs] = json_source
+ expect(Chef::ConfigFetcher).to receive(:new).with(json_source).
+ and_return(config_fetcher)
+ end
+
+ it "reads the JSON attributes from the specified source" do
+ app.reconfigure
+ expect(app.chef_client_json).to eq(json_attribs)
+ end
end
- it "should set the interval to 1800" do
- Chef::Config[:interval] = nil
+ it "downloads a tarball when the recipe_url configuration option is specified" do
+ Chef::Config[:cookbook_path] = "#{Dir.tmpdir}/chef-solo/cookbooks"
+ Chef::Config[:recipe_url] = "http://junglist.gen.nz/recipes.tgz"
+
+ expect(FileUtils).to receive(:mkdir_p).with("#{Dir.tmpdir}/chef-solo").and_return(true)
+
+ tarfile = StringIO.new("remote_tarball_content")
+ target_file = StringIO.new
+
+ expect(app).to receive(:open).with("http://junglist.gen.nz/recipes.tgz").and_yield(tarfile)
+ expect(File).to receive(:open).with("#{Dir.tmpdir}/chef-solo/recipes.tgz", "wb").and_yield(target_file)
+
+ archive = double(Mixlib::Archive)
+
+ expect(Mixlib::Archive).to receive(:new).with("#{Dir.tmpdir}/chef-solo/recipes.tgz").and_return(archive)
+ expect(archive).to receive(:extract).with("#{Dir.tmpdir}/chef-solo", { perms: false, ignore: /^\.$/ })
app.reconfigure
- expect(Chef::Config[:interval]).to eq(1800)
+ expect(target_file.string).to eq("remote_tarball_content")
end
- end
- describe "when the json_attribs configuration option is specified" do
- let(:json_attribs) { { "a" => "b" } }
- let(:config_fetcher) { double(Chef::ConfigFetcher, :fetch_json => json_attribs) }
- let(:json_source) { "https://foo.com/foo.json" }
+ it "fetches the recipe_url first when both json_attribs and recipe_url are specified" do
+ json_attribs = { "a" => "b" }
+ config_fetcher = instance_double("Chef::ConfigFetcher", :fetch_json => json_attribs)
- before do
- Chef::Config[:json_attribs] = json_source
- expect(Chef::ConfigFetcher).to receive(:new).with(json_source).
- and_return(config_fetcher)
- end
+ Chef::Config[:json_attribs] = "https://foo.com/foo.json"
+ Chef::Config[:recipe_url] = "http://icanhas.cheezburger.com/lolcats"
+ Chef::Config[:cookbook_path] = "#{Dir.tmpdir}/chef-solo/cookbooks"
+ expect(FileUtils).to receive(:mkdir_p).with("#{Dir.tmpdir}/chef-solo").and_return(true)
- it "reads the JSON attributes from the specified source" do
+ archive = double(Mixlib::Archive)
+
+ expect(Mixlib::Archive).to receive(:new).with("#{Dir.tmpdir}/chef-solo/recipes.tgz").and_return(archive)
+ expect(archive).to receive(:extract).with("#{Dir.tmpdir}/chef-solo", { perms: false, ignore: /^\.$/ })
+ expect(app).to receive(:fetch_recipe_tarball).ordered
+ expect(Chef::ConfigFetcher).to receive(:new).ordered.and_return(config_fetcher)
app.reconfigure
- expect(app.chef_client_json).to eq(json_attribs)
end
end
- it "downloads a tarball when the recipe_url configuration option is specified" do
- Chef::Config[:cookbook_path] = "#{Dir.tmpdir}/chef-solo/cookbooks"
- Chef::Config[:recipe_url] = "http://junglist.gen.nz/recipes.tgz"
-
- expect(FileUtils).to receive(:mkdir_p).with("#{Dir.tmpdir}/chef-solo").and_return(true)
+ describe "after the application has been configured" do
+ before do
+ Chef::Config[:solo] = true
+ Chef::Config[:solo_legacy_mode] = true
+
+ allow(Chef::Daemon).to receive(:change_privilege)
+ chef_client = double("Chef::Client")
+ allow(Chef::Client).to receive(:new).and_return(chef_client)
+ # this is all stuff the reconfigure method needs
+ allow(app).to receive(:configure_opt_parser).and_return(true)
+ allow(app).to receive(:configure_chef).and_return(true)
+ allow(app).to receive(:configure_logging).and_return(true)
+ end
- tarfile = StringIO.new("remote_tarball_content")
- target_file = StringIO.new
+ it "should change privileges" do
+ expect(Chef::Daemon).to receive(:change_privilege).and_return(true)
+ app.setup_application
+ end
+ end
- expect(app).to receive(:open).with("http://junglist.gen.nz/recipes.tgz").and_yield(tarfile)
- expect(File).to receive(:open).with("#{Dir.tmpdir}/chef-solo/recipes.tgz", "wb").and_yield(target_file)
+ it_behaves_like "an application that loads a dot d" do
+ let(:dot_d_config_name) { :solo_d_dir }
+ end
+ end
- shellout = instance_double("Mixlib::ShellOut", run_command: nil, error!: nil, stdout: "")
+ context "in local mode" do
+ before do
+ Chef::Config[:solo_legacy_mode] = false
+ end
- expect(app).to receive(:shell_out!).with("tar zxvf #{Dir.tmpdir}/chef-solo/recipes.tgz -C #{Dir.tmpdir}/chef-solo").and_return(shellout)
+ it "sets solo mode to true" do
app.reconfigure
- expect(target_file.string).to eq("remote_tarball_content")
+ expect(Chef::Config[:solo]).to be_truthy
end
- it "fetches the recipe_url first when both json_attribs and recipe_url are specified" do
- json_attribs = { "a" => "b" }
- config_fetcher = instance_double("Chef::ConfigFetcher", :fetch_json => json_attribs)
+ it "sets local mode to true" do
+ app.reconfigure
+ expect(Chef::Config[:local_mode]).to be_truthy
+ end
- Chef::Config[:json_attribs] = "https://foo.com/foo.json"
- Chef::Config[:recipe_url] = "http://icanhas.cheezburger.com/lolcats"
- Chef::Config[:cookbook_path] = "#{Dir.tmpdir}/chef-solo/cookbooks"
- expect(FileUtils).to receive(:mkdir_p).with("#{Dir.tmpdir}/chef-solo").and_return(true)
+ context "argv gets tidied up" do
+ before do
+ @original_argv = ARGV.dup
+ ARGV.clear
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ end
- allow(Chef::Mixin::Command).to receive(:run_command).and_return(true)
+ after do
+ ARGV.replace(@original_argv)
+ end
- shellout = instance_double("Mixlib::ShellOut", run_command: nil, error!: nil, stdout: "")
+ it "deletes --ez" do
+ ARGV << "--ez"
+ app.reconfigure
+ expect(ARGV.include?("--ez")).to be_falsey
+ end
+ end
- expect(app).to receive(:shell_out!).with("tar zxvf #{Dir.tmpdir}/chef-solo/recipes.tgz -C #{Dir.tmpdir}/chef-solo").and_return(shellout)
- expect(app).to receive(:fetch_recipe_tarball).ordered
- expect(Chef::ConfigFetcher).to receive(:new).ordered.and_return(config_fetcher)
+ it "sets the repo path" do
+ expect(Chef::Config).to receive(:find_chef_repo_path).and_return("/var/chef")
app.reconfigure
+ expect(Chef::Config.has_key?(:chef_repo_path)).to be_truthy
+ expect(Chef::Config[:chef_repo_path]).to eq ("/var/chef")
end
- end
-
- describe "after the application has been configured" do
- before do
- Chef::Config[:solo] = true
- allow(Chef::Daemon).to receive(:change_privilege)
- chef_client = double("Chef::Client")
- allow(Chef::Client).to receive(:new).and_return(chef_client)
- # this is all stuff the reconfigure method needs
- allow(app).to receive(:configure_opt_parser).and_return(true)
+ it "runs chef-client in local mode" do
+ allow(app).to receive(:setup_application).and_return(true)
+ allow(app).to receive(:run_application).and_return(true)
allow(app).to receive(:configure_chef).and_return(true)
allow(app).to receive(:configure_logging).and_return(true)
+ expect(Chef::Application::Client).to receive_message_chain(:new, :run)
+ app.run
end
- it "should change privileges" do
- expect(Chef::Daemon).to receive(:change_privilege).and_return(true)
- app.setup_application
- end
- end
-
- it_behaves_like "an application that loads a dot d" do
- let(:dot_d_config_name) { :solo_d_dir }
end
end
diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb
index 8ab6e13204..76be387c22 100644
--- a/spec/unit/application_spec.rb
+++ b/spec/unit/application_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: AJ Christensen (<aj@junglist.gen.nz>)
# Author:: Mark Mzyk (mmzyk@chef.io)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -101,6 +101,7 @@ describe Chef::Application do
@app = Chef::Application.new
allow(@app).to receive(:parse_options).and_return(true)
+ allow(::File).to receive(:read).with("/proc/sys/crypto/fips_enabled").and_call_original
expect(Chef::Config).to receive(:export_proxies).and_return(true)
end
@@ -171,7 +172,7 @@ describe Chef::Application do
@app.config[:config_file] = "/etc/chef/notfound"
end
it "should use the passed in command line options and defaults" do
- expect(Chef::Config).to receive(:merge!)
+ expect(Chef::Config).to receive(:merge!).at_least(:once)
@app.configure_chef
end
end
@@ -187,53 +188,56 @@ describe Chef::Application do
allow(Chef::Log).to receive(:level=)
@monologger = double("Monologger")
expect(MonoLogger).to receive(:new).with(Chef::Config[:log_location]).and_return(@monologger)
+ allow(MonoLogger).to receive(:new).with(STDOUT).and_return(@monologger)
+ allow(@monologger).to receive(:formatter=).with(Chef::Log.logger.formatter)
expect(Chef::Log).to receive(:init).with(@monologger)
@app.configure_logging
end
shared_examples_for "log_level_is_auto" do
- context "when STDOUT is to a tty" do
+ before do
+ allow(STDOUT).to receive(:tty?).and_return(true)
+ end
+
+ it "configures the log level to :warn" do
+ @app.configure_logging
+ expect(Chef::Log.level).to eq(:warn)
+ end
+
+ context "when force_formater is configured" do
before do
- allow(STDOUT).to receive(:tty?).and_return(true)
+ Chef::Config[:force_formatter] = true
end
- it "configures the log level to :warn" do
+ it "configures the log level to warn" do
@app.configure_logging
expect(Chef::Log.level).to eq(:warn)
end
-
- context "when force_logger is configured" do
- before do
- Chef::Config[:force_logger] = true
- end
-
- it "configures the log level to info" do
- @app.configure_logging
- expect(Chef::Log.level).to eq(:info)
- end
- end
end
- context "when STDOUT is not to a tty" do
+ context "when force_logger is configured" do
before do
- allow(STDOUT).to receive(:tty?).and_return(false)
+ Chef::Config[:force_logger] = true
end
- it "configures the log level to :info" do
+ it "configures the log level to info" do
@app.configure_logging
expect(Chef::Log.level).to eq(:info)
end
+ end
- context "when force_formatter is configured" do
- before do
- Chef::Config[:force_formatter] = true
- end
- it "sets the log level to :warn" do
- @app.configure_logging
- expect(Chef::Log.level).to eq(:warn)
- end
+ context "when both are is configured" do
+ before do
+ Chef::Config[:force_logger] = true
+ Chef::Config[:force_formatter] = true
+ end
+
+ it "configures the log level to warn" do
+ @app.configure_logging
+ expect(Chef::Log.level).to eq(:warn)
end
end
+
end
context "when log_level is not set" do
@@ -299,16 +303,23 @@ describe Chef::Application do
Chef::Application.fatal! "blah"
end
- describe "when an exit code is supplied" do
+ describe "when a standard exit code is supplied" do
it "should exit with the given exit code" do
- expect(Process).to receive(:exit).with(-100).and_return(true)
+ expect(Process).to receive(:exit).with(42).and_return(true)
+ Chef::Application.fatal! "blah", 42
+ end
+ end
+
+ describe "when a non-standard exit code is supplied" do
+ it "should exit with the default exit code" do
+ expect(Process).to receive(:exit).with(1).and_return(true)
Chef::Application.fatal! "blah", -100
end
end
describe "when an exit code is not supplied" do
it "should exit with the default exit code" do
- expect(Process).to receive(:exit).with(-1).and_return(true)
+ expect(Process).to receive(:exit).with(1).and_return(true)
Chef::Application.fatal! "blah"
end
end
@@ -361,7 +372,7 @@ describe Chef::Application do
end
end
- context 'when called with an Array-like argument (#size)' do
+ context "when called with an Array-like argument (#size)" do
before do
allow(app).to receive(:fork_chef_client).and_return(true)
allow(app).to receive(:run_with_graceful_exit_option).and_return(true)
@@ -408,4 +419,68 @@ describe Chef::Application do
end
end
end
+
+ describe "merged config" do
+ class MyTestConfig < Chef::Config
+ extend Mixlib::Config
+
+ default :test_config_1, "config default"
+ default :test_config_2, "config default"
+ end
+
+ class MyAppClass < Chef::Application
+ # there's an implicit test here that mixlib-cli's separate_default_options is being inherited
+ option :test_config_2, long: "--test-config2 CONFIG", default: "cli default"
+ end
+
+ before(:each) do
+ MyTestConfig.reset
+ @original_argv = ARGV.dup
+ ARGV.clear
+ @app = MyAppClass.new
+ expect(@app).to receive(:chef_config).at_least(:once).and_return(MyTestConfig)
+ expect(Chef::ConfigFetcher).to receive(:new).and_return(fake_config_fetcher)
+ allow(@app).to receive(:log).and_return(instance_double(Mixlib::Log, warn: nil)) # ignorken
+ end
+
+ after(:each) do
+ ARGV.replace(@original_argv)
+ end
+
+ let(:fake_config_fetcher) { instance_double(Chef::ConfigFetcher, expanded_path: "/thisbetternotexist", :"config_missing?" => false, read_config: "" ) }
+
+ it "reading a mixlib-config default works" do
+ @app.parse_options
+ @app.load_config_file
+ expect(MyTestConfig[:test_config_1]).to eql("config default")
+ end
+
+ it "a mixlib-cli default overrides a mixlib-config default" do
+ @app.parse_options
+ @app.load_config_file
+ expect(MyTestConfig[:test_config_2]).to eql("cli default")
+ end
+
+ it "a set mixlib-config value overrides a mixlib-config default" do
+ expect(fake_config_fetcher).to receive(:read_config).and_return(%q{test_config_1 "config setting"})
+ @app.parse_options
+ @app.load_config_file
+ expect(MyTestConfig[:test_config_1]).to eql("config setting")
+ end
+
+ it "a set mixlib-config value overrides a mixlib-cli default" do
+ expect(fake_config_fetcher).to receive(:read_config).and_return(%q{test_config_2 "config setting"})
+ @app.parse_options
+ @app.load_config_file
+ expect(MyTestConfig[:test_config_2]).to eql("config setting")
+ end
+
+ it "a set mixlib-cli value overrides everything else" do
+ expect(fake_config_fetcher).to receive(:read_config).and_return(%q{test_config_2 "config setting"})
+ ARGV.replace("--test-config2 cli-setting".split)
+ @app.parse_options
+ @app.load_config_file
+ expect(MyTestConfig[:test_config_2]).to eql("cli-setting")
+ end
+ end
end
diff --git a/spec/unit/audit/audit_event_proxy_spec.rb b/spec/unit/audit/audit_event_proxy_spec.rb
index 2496aef3e7..0e35343352 100644
--- a/spec/unit/audit/audit_event_proxy_spec.rb
+++ b/spec/unit/audit/audit_event_proxy_spec.rb
@@ -34,8 +34,9 @@ describe Chef::Audit::AuditEventProxy do
describe "#example_group_started" do
let(:description) { "poots" }
- let(:group) { double("ExampleGroup", :parent_groups => parents,
- :description => description) }
+ let(:group) do
+ double("ExampleGroup", :parent_groups => parents,
+ :description => description) end
let(:notification) { double("Notification", :group => group) }
context "when notified from a top-level example group" do
@@ -43,7 +44,7 @@ describe Chef::Audit::AuditEventProxy do
let(:parents) { [double("ExampleGroup")] }
it "notifies control_group_started event" do
- expect(Chef::Log).to receive(:debug).
+ expect(Chef::Log).to receive(:trace).
with("Entered \`control_group\` block named poots")
expect(events).to receive(:control_group_started).
with(description)
@@ -120,35 +121,36 @@ describe Chef::Audit::AuditEventProxy do
let(:examples) { [example] }
- let(:example) { double("Example", :metadata => metadata,
- :description => example_description,
- :full_description => full_description, :exception => nil) }
+ let(:example) do
+ double("Example", :metadata => metadata,
+ :description => example_description,
+ :full_description => full_description, :exception => nil) end
- let(:metadata) {
+ let(:metadata) do
{
:described_class => described_class,
:example_group => example_group,
:line_number => line,
}
- }
+ end
- let(:example_group) {
+ let(:example_group) do
{
:description => group_description,
:parent_example_group => parent_group,
}
- }
+ end
- let(:parent_group) {
+ let(:parent_group) do
{
:description => control_group_name,
:parent_example_group => nil,
}
- }
+ end
let(:line) { 27 }
- let(:control_data) {
+ let(:control_data) do
{
:name => example_description,
:desc => full_description,
@@ -157,7 +159,7 @@ describe Chef::Audit::AuditEventProxy do
:context => context,
:line_number => line,
}
- }
+ end
shared_examples "built control" do
@@ -218,12 +220,14 @@ describe Chef::Audit::AuditEventProxy do
let(:control_group_name) { "application ports" }
let(:group_description) { "#{resource_type} #{resource_name}" }
let(:example_description) { "should not be listening" }
- let(:full_description) { [control_group_name, group_description,
- example_description].join(" ") }
+ let(:full_description) do
+ [control_group_name, group_description,
+ example_description].join(" ") end
# Metadata fields
- let(:described_class) { double("Serverspec::Type::Port",
- :class => "Serverspec::Type::Port", :name => resource_name) }
+ let(:described_class) do
+ double("Serverspec::Type::Port",
+ :class => "Serverspec::Type::Port", :name => resource_name) end
# Control data fields
let(:resource_type) { "Port" }
@@ -246,8 +250,9 @@ describe Chef::Audit::AuditEventProxy do
let(:control_group_name) { "application ports" }
let(:group_description) { "port 111" }
let(:example_description) { "is not listening" }
- let(:full_description) { [control_group_name, group_description,
- example_description].join(" ") }
+ let(:full_description) do
+ [control_group_name, group_description,
+ example_description].join(" ") end
# Metadata fields
let(:described_class) { nil }
@@ -276,27 +281,29 @@ describe Chef::Audit::AuditEventProxy do
let(:outer_group_description) { "File \"tmp/audit\"" }
let(:group_description) { "#{resource_type} #{resource_name}" }
let(:example_description) { "is a file" }
- let(:full_description) { [control_group_name, outer_group_description,
- group_description, example_description].join(" ") }
+ let(:full_description) do
+ [control_group_name, outer_group_description,
+ group_description, example_description].join(" ") end
# Metadata parts
- let(:described_class) { double("Serverspec::Type::File",
- :class => "Serverspec::Type::File", :name => resource_name) }
+ let(:described_class) do
+ double("Serverspec::Type::File",
+ :class => "Serverspec::Type::File", :name => resource_name) end
# Example group parts
- let(:parent_group) {
+ let(:parent_group) do
{
:description => outer_group_description,
:parent_example_group => control_group,
}
- }
+ end
- let(:control_group) {
+ let(:control_group) do
{
:description => control_group_name,
:parent_example_group => nil,
}
- }
+ end
# Control data parts
let(:resource_type) { "File" }
diff --git a/spec/unit/audit/audit_reporter_spec.rb b/spec/unit/audit/audit_reporter_spec.rb
index 1db56a550e..3edee46932 100644
--- a/spec/unit/audit/audit_reporter_spec.rb
+++ b/spec/unit/audit/audit_reporter_spec.rb
@@ -2,7 +2,7 @@
# Author:: Tyler Ball (<tball@chef.io>)
# Author:: Claire McQuin (<claire@chef.io>)
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,13 +28,14 @@ describe Chef::Audit::AuditReporter do
let(:run_id) { 0 }
let(:start_time) { Time.new(2014, 12, 3, 9, 31, 05, "-08:00") }
let(:end_time) { Time.new(2014, 12, 3, 9, 36, 14, "-08:00") }
- let(:run_status) { instance_double(Chef::RunStatus, :node => node, :run_id => run_id,
- :start_time => start_time, :end_time => end_time) }
+ let(:run_status) do
+ instance_double(Chef::RunStatus, :node => node, :run_id => run_id,
+ :start_time => start_time, :end_time => end_time) end
describe "#audit_phase_start" do
- it "notifies audit phase start to debug log" do
- expect(Chef::Log).to receive(:debug).with(/Audit Reporter starting/)
+ it "notifies audit phase start to trace log" do
+ expect(Chef::Log).to receive(:trace).with(/Audit Reporter starting/)
reporter.audit_phase_start(run_status)
end
@@ -85,9 +86,10 @@ describe Chef::Audit::AuditReporter do
context "when audit phase failed" do
- let(:audit_error) { double("AuditError", :class => "Chef::Exceptions::AuditError",
- :message => "Audit phase failed with error message: derpderpderp",
- :backtrace => ["/path/recipe.rb:57", "/path/library.rb:106"]) }
+ let(:audit_error) do
+ double("AuditError", :class => "Chef::Exceptions::AuditError",
+ :message => "Audit phase failed with error message: derpderpderp",
+ :backtrace => ["/path/recipe.rb:57", "/path/library.rb:106"]) end
before do
reporter.instance_variable_set(:@audit_phase_error, audit_error)
@@ -124,8 +126,8 @@ EOM
let(:response) { double("response", :code => code) }
before do
- expect(Chef::Log).to receive(:debug).with(/Sending audit report/)
- expect(Chef::Log).to receive(:debug).with(/Audit Report/)
+ expect(Chef::Log).to receive(:trace).with(/Sending audit report/)
+ expect(Chef::Log).to receive(:trace).with(/Audit Report/)
allow(error).to receive(:response).and_return(response)
expect(error).to receive(:respond_to?).with(:response).and_return(true)
end
@@ -135,7 +137,7 @@ EOM
let(:code) { "404" }
it "logs that the server doesn't support audit reporting" do
- expect(Chef::Log).to receive(:debug).with(/Server doesn't support audit reporting/)
+ expect(Chef::Log).to receive(:trace).with(/Server doesn't support audit reporting/)
reporter.run_completed(node)
end
end
@@ -201,12 +203,12 @@ EOM
context "when auditing is not enabled" do
before do
- allow(Chef::Log).to receive(:debug)
+ allow(Chef::Log).to receive(:trace)
end
it "doesn't send reports" do
expect(reporter).to receive(:auditing_enabled?).and_return(false)
- expect(Chef::Log).to receive(:debug).with("Audit Reports are disabled. Skipping sending reports.")
+ expect(Chef::Log).to receive(:trace).with("Audit Reports are disabled. Skipping sending reports.")
reporter.run_completed(node)
end
@@ -215,13 +217,13 @@ EOM
context "when the run fails before audits" do
before do
- allow(Chef::Log).to receive(:debug)
+ allow(Chef::Log).to receive(:trace)
end
it "doesn't send reports" do
expect(reporter).to receive(:auditing_enabled?).and_return(true)
expect(reporter).to receive(:run_status).and_return(nil)
- expect(Chef::Log).to receive(:debug).with("Run failed before audit mode was initialized, not sending audit report to server")
+ expect(Chef::Log).to receive(:trace).with("Run failed before audit mode was initialized, not sending audit report to server")
reporter.run_completed(node)
end
@@ -233,13 +235,15 @@ EOM
let(:audit_data) { Chef::Audit::AuditData.new(node.name, run_id) }
let(:run_data) { audit_data.to_hash }
- let(:audit_error) { double("AuditError", :class => "Chef::Exceptions::AuditError",
- :message => "Audit phase failed with error message: derpderpderp",
- :backtrace => ["/path/recipe.rb:57", "/path/library.rb:106"]) }
+ let(:audit_error) do
+ double("AuditError", :class => "Chef::Exceptions::AuditError",
+ :message => "Audit phase failed with error message: derpderpderp",
+ :backtrace => ["/path/recipe.rb:57", "/path/library.rb:106"]) end
- let(:run_error) { double("RunError", :class => "Chef::Exceptions::RunError",
- :message => "This error shouldn't be reported.",
- :backtrace => ["fix it", "fix it", "fix it"]) }
+ let(:run_error) do
+ double("RunError", :class => "Chef::Exceptions::RunError",
+ :message => "This error shouldn't be reported.",
+ :backtrace => ["fix it", "fix it", "fix it"]) end
before do
allow(reporter).to receive(:auditing_enabled?).and_return(true)
@@ -276,23 +280,27 @@ EOM
shared_context "audit data" do
- let(:control_group_foo) { instance_double(Chef::Audit::ControlGroupData,
- :metadata => double("foo metadata")) }
- let(:control_group_bar) { instance_double(Chef::Audit::ControlGroupData,
- :metadata => double("bar metadata")) }
+ let(:control_group_foo) do
+ instance_double(Chef::Audit::ControlGroupData,
+ :metadata => double("foo metadata")) end
+ let(:control_group_bar) do
+ instance_double(Chef::Audit::ControlGroupData,
+ :metadata => double("bar metadata")) end
- let(:ordered_control_groups) {
+ let(:ordered_control_groups) do
{
"foo" => control_group_foo,
"bar" => control_group_bar,
}
- }
+ end
- let(:audit_data) { instance_double(Chef::Audit::AuditData,
- :add_control_group => true) }
+ let(:audit_data) do
+ instance_double(Chef::Audit::AuditData,
+ :add_control_group => true) end
- let(:run_context) { instance_double(Chef::RunContext,
- :audits => ordered_control_groups) }
+ let(:run_context) do
+ instance_double(Chef::RunContext,
+ :audits => ordered_control_groups) end
before do
allow(reporter).to receive(:ordered_control_groups).and_return(ordered_control_groups)
@@ -305,13 +313,13 @@ EOM
describe "#audit_phase_complete" do
include_context "audit data"
- it "notifies audit phase finished to debug log" do
- expect(Chef::Log).to receive(:debug).with(/Audit Reporter completed/)
+ it "notifies audit phase finished to trace log" do
+ expect(Chef::Log).to receive(:trace).with(/Audit Reporter completed/)
reporter.audit_phase_complete("Output from audit mode")
end
it "collects audit data" do
- ordered_control_groups.each do |_name, group|
+ ordered_control_groups.each_value do |group|
expect(audit_data).to receive(:add_control_group).with(group)
end
reporter.audit_phase_complete("Output from audit mode")
@@ -323,13 +331,13 @@ EOM
let(:error) { double("Exception") }
- it "notifies audit phase failed to debug log" do
- expect(Chef::Log).to receive(:debug).with(/Audit Reporter failed/)
+ it "notifies audit phase failed to trace log" do
+ expect(Chef::Log).to receive(:trace).with(/Audit Reporter failed/)
reporter.audit_phase_failed(error, "Output from audit mode")
end
it "collects audit data" do
- ordered_control_groups.each do |_name, group|
+ ordered_control_groups.each_value do |group|
expect(audit_data).to receive(:add_control_group).with(group)
end
reporter.audit_phase_failed(error, "Output from audit mode")
@@ -340,8 +348,9 @@ EOM
include_context "audit data"
let(:name) { "bat" }
- let(:control_group) { instance_double(Chef::Audit::ControlGroupData,
- :metadata => double("metadata")) }
+ let(:control_group) do
+ instance_double(Chef::Audit::ControlGroupData,
+ :metadata => double("metadata")) end
before do
allow(Chef::Audit::ControlGroupData).to receive(:new).
diff --git a/spec/unit/audit/control_group_data_spec.rb b/spec/unit/audit/control_group_data_spec.rb
index a7c8850350..ea4ac260f9 100644
--- a/spec/unit/audit/control_group_data_spec.rb
+++ b/spec/unit/audit/control_group_data_spec.rb
@@ -122,9 +122,10 @@ describe Chef::Audit::ControlData do
let(:context) { nil }
let(:line_number) { 27 }
- let(:control_data) { described_class.new(name: name,
- resource_type: resource_type, resource_name: resource_name,
- context: context, line_number: line_number) }
+ let(:control_data) do
+ described_class.new(name: name,
+ resource_type: resource_type, resource_name: resource_name,
+ context: context, line_number: line_number) end
describe "#to_hash" do
@@ -171,7 +172,7 @@ describe Chef::Audit::ControlGroupData do
let(:context) { nil }
let(:line_number) { 0 }
- let(:control_data) {
+ let(:control_data) do
{
:name => name,
:resource_type => resource_type,
@@ -179,16 +180,17 @@ describe Chef::Audit::ControlGroupData do
:context => context,
:line_number => line_number,
}
- }
+ end
end
shared_context "control" do
include_context "control data"
- let(:control) { Chef::Audit::ControlData.new(name: name,
- resource_type: resource_type, resource_name: resource_name,
- context: context, line_number: line_number) }
+ let(:control) do
+ Chef::Audit::ControlData.new(name: name,
+ resource_type: resource_type, resource_name: resource_name,
+ context: context, line_number: line_number) end
before do
allow(Chef::Audit::ControlData).to receive(:new).
@@ -436,15 +438,18 @@ describe Chef::Audit::ControlGroupData do
let(:control_hash_2) { { :line_number => 13 } }
let(:control_hash_3) { { :line_number => 35 } }
- let(:control_1) { double("control 1",
+ let(:control_1) do
+ double("control 1",
:line_number => control_hash_1[:line_number],
- :to_hash => control_hash_1) }
- let(:control_2) { double("control 2",
+ :to_hash => control_hash_1) end
+ let(:control_2) do
+ double("control 2",
:line_number => control_hash_2[:line_number],
- :to_hash => control_hash_2) }
- let(:control_3) { double("control 3",
+ :to_hash => control_hash_2) end
+ let(:control_3) do
+ double("control 3",
:line_number => control_hash_3[:line_number],
- :to_hash => control_hash_3) }
+ :to_hash => control_hash_3) end
let(:control_list) { [control_1, control_2, control_3] }
let(:ordered_control_hashes) { [control_hash_2, control_hash_1, control_hash_3] }
diff --git a/spec/unit/chef_class_spec.rb b/spec/unit/chef_class_spec.rb
index 3c8411ef2a..21987c01ab 100644
--- a/spec/unit/chef_class_spec.rb
+++ b/spec/unit/chef_class_spec.rb
@@ -85,7 +85,7 @@ describe "Chef class" do
end
end
- context '#event_handler' do
+ context "#event_handler" do
it "adds a new handler" do
x = 1
Chef.event_handler do
diff --git a/spec/unit/chef_fs/config_spec.rb b/spec/unit/chef_fs/config_spec.rb
index 5fc5ad57ac..0adcbbfbfb 100644
--- a/spec/unit/chef_fs/config_spec.rb
+++ b/spec/unit/chef_fs/config_spec.rb
@@ -103,4 +103,133 @@ describe Chef::ChefFS::Config do
expect(local_fs.child_paths["users"]).to eq([platform_path("/base_path/users")])
end
end
+
+ describe "formats paths", :unix_only do
+
+ let(:single_repo_path) do
+ Mash.new({
+ chef_repo_path: "/base_path",
+ })
+ end
+
+ let(:double_repo_path) do
+ Mash.new({
+ chef_repo_path: %w{ /base_path /second_base_path },
+ })
+ end
+
+ describe "#server_path" do
+ it "returns nil if no paths match" do
+ cfg = Chef::ChefFS::Config.new(single_repo_path, "/my_repo/cookbooks")
+ expect(cfg.server_path("foo")).to be_nil
+ end
+
+ context "with only repo paths" do
+ it "returns / if in the repo path" do
+ cwd = "/base_path/cookbooks"
+ cfg = Chef::ChefFS::Config.new(single_repo_path, cwd)
+ expect(Chef::ChefFS::PathUtils).to receive(:realest_path).with("/base_path/cookbooks", cwd).and_return("/base_path/cookbooks")
+ expect(Chef::ChefFS::PathUtils).to receive(:realest_path).with("/base_path", cwd).and_return("/base_path/cookbooks")
+ expect(cfg.server_path("/base_path/cookbooks")).to eq("/")
+ end
+
+ it "checks all the repo paths" do
+ cwd = "/second_base_path/cookbooks"
+ cfg = Chef::ChefFS::Config.new(double_repo_path, cwd)
+ expect(Chef::ChefFS::PathUtils).to receive(:realest_path).with("/second_base_path/cookbooks", cwd).and_return("/second_base_path/cookbooks")
+ expect(Chef::ChefFS::PathUtils).to receive(:realest_path).with("/base_path", cwd).and_return("/base_path/cookbooks")
+ expect(Chef::ChefFS::PathUtils).to receive(:realest_path).with("/second_base_path", cwd).and_return("/second_base_path/cookbooks")
+ expect(cfg.server_path("/second_base_path/cookbooks")).to eq("/")
+ end
+ end
+
+ context "with specific object locations" do
+ let(:single_cookbook_path) do
+ Mash.new({
+ cookbook_path: "/base_path/cookbooks",
+ role_path: "/base_path/roles",
+ })
+ end
+
+ let(:cwd) { "/base_path/cookbooks" }
+ let(:cfg) { Chef::ChefFS::Config.new(single_cookbook_path, cwd) }
+
+ before do
+ expect(Chef::ChefFS::PathUtils).to receive(:realest_path).with("/base_path/cookbooks", cwd).and_return("/base_path/cookbooks")
+ allow(Chef::ChefFS::PathUtils).to receive(:realest_path).with("/base_path/roles", cwd).and_return("/base_path/roles")
+ end
+
+ it "resolves a relative path" do
+ expect(Chef::ChefFS::PathUtils).to receive(:realest_path).with("blah", cwd).and_return("/base_path/cookbooks/blah")
+ expect(cfg.server_path("blah")).to eql("/cookbooks/blah")
+ end
+
+ it "resolves a relative path in a parent directory" do
+ expect(Chef::ChefFS::PathUtils).to receive(:realest_path).with("../roles/blah", cwd).and_return("/base_path/roles/blah")
+ expect(Chef::ChefFS::PathUtils).to receive(:realest_path).with("/base_path/roles", cwd).and_return("/base_path/roles")
+ expect(cfg.server_path("../roles/blah")).to eql("/roles/blah")
+ end
+
+ it "ignores a relative path that's outside the repository" do
+ expect(Chef::ChefFS::PathUtils).to receive(:realest_path).with("../../readme.txt", cwd).and_return("/readme.txt")
+ expect(cfg.server_path("../../readme.txt")).to be_nil
+ end
+
+ it "deals with splat paths" do
+ expect(Chef::ChefFS::PathUtils).to receive(:realest_path).with("*/*ab*", cwd).and_return("/base_path/cookbooks/*/*ab*")
+ expect(cfg.server_path("*/*ab*")).to eql("/cookbooks/*/*ab*")
+ end
+
+ it "resolves an absolute path" do
+ expect(Chef::ChefFS::PathUtils).to receive(:realest_path).with("/base_path/cookbooks/blah", cwd).and_return("/base_path/cookbooks/blah")
+ expect(cfg.server_path("/base_path/cookbooks/blah")).to eql("/cookbooks/blah")
+ end
+
+ it "deals with an absolute path with splats" do
+ expect(Chef::ChefFS::PathUtils).to receive(:realest_path).with("/*/cookbooks/blah", cwd).and_return("/*/cookbooks/blah")
+ expect(cfg.server_path("/*/cookbooks/blah")).to be_nil
+ end
+ end
+ end
+
+ describe "#format_path" do
+ Entry = Struct.new(:path)
+
+ let(:config) do
+ Mash.new({
+ chef_repo_path: "/base_path",
+ cookbook_path: "/base_path/cookbooks",
+ role_path: "/base_path/roles",
+ })
+ end
+
+ let (:path) { "/roles/foo.json" }
+ let (:entry) { Entry.new(path) }
+
+ it "returns the entry's path if the cwd isn't in the config" do
+ cfg = Chef::ChefFS::Config.new(config, "/my_repo/cookbooks")
+ expect(cfg).to receive(:base_path).and_return(nil)
+ expect(cfg.format_path(entry)).to eq(path)
+ end
+
+ it "returns . if the cwd is the same as the entry's path" do
+ cfg = Chef::ChefFS::Config.new(config, "/base_path/roles/foo.json")
+ expect(cfg).to receive(:base_path).and_return("/roles/foo.json").at_least(:once)
+ expect(cfg.format_path(entry)).to eq(".")
+ end
+
+ it "returns a relative path if the cwd is in the repo" do
+ cfg = Chef::ChefFS::Config.new(config, "/base_path/roles")
+ expect(cfg).to receive(:base_path).and_return("/roles").at_least(:once)
+ expect(cfg.format_path(entry)).to eq("foo.json")
+ end
+
+ it "returns a relative path if the cwd is at the root of repo" do
+ cfg = Chef::ChefFS::Config.new(config, "/base_path")
+ expect(cfg).to receive(:base_path).and_return("/").at_least(:once)
+ expect(cfg.format_path(entry)).to eq("roles/foo.json")
+ end
+
+ end
+ end
end
diff --git a/spec/unit/chef_fs/data_handler/data_bag_item_data_handler.rb b/spec/unit/chef_fs/data_handler/data_bag_item_data_handler.rb
new file mode 100644
index 0000000000..6d6e2fb0e6
--- /dev/null
+++ b/spec/unit/chef_fs/data_handler/data_bag_item_data_handler.rb
@@ -0,0 +1,82 @@
+#
+# Author:: Sandra Tiffin (<sandi.tiffin@gmail.com>)
+# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "lib/chef/chef_fs/data_handler/data_bag_item_data_handler.rb"
+
+class TestDataBag < Mash
+ attr_accessor :name
+
+ def initialize(bag_name)
+ @name = bag_name
+ end
+end
+
+class TestDataBagItem < Mash
+ attr_accessor :name, :parent
+
+ def path_for_printing
+ "/some/path"
+ end
+
+ def initialize(bag_name, item_name)
+ @name = "#{item_name}.json"
+ @parent = TestDataBag.new(bag_name)
+ end
+end
+
+describe Chef::ChefFS::DataHandler::DataBagItemDataHandler do
+ let(:handler) { described_class.new }
+
+ describe "#verify_integrity" do
+ context "json id does not match data bag item name" do
+ let(:entry) { TestDataBagItem.new("luggage", "bag") }
+ let(:object) do
+ { "raw_data" => { "id" => "duffel" } }
+ end
+ it "rejects the data bag item name" do
+ expect { |b| handler.verify_integrity(object, entry, &b) }.to yield_with_args
+ end
+ end
+
+ context "using a reserved word for the data bag name" do
+ %w{node role environment client}.each do |reserved_word|
+ let(:entry) { TestDataBagItem.new(reserved_word, "bag") }
+ let(:object) do
+ { "raw_data" => { "id" => "bag" } }
+ end
+ it "rejects the data bag name '#{reserved_word}'" do
+ expect { |b| handler.verify_integrity(object, entry, &b) }.to yield_with_args
+ end
+ end
+ end
+
+ context "using a reserved word as part of the data bag name" do
+ %w{xnode rolex xenvironmentx xclientx}.each do |bag_name|
+ let(:entry) { TestDataBagItem.new("#{bag_name}", "bag") }
+ let(:object) do
+ { "raw_data" => { "id" => "bag" } }
+ end
+ it "allows the data bag name '#{bag_name}'" do
+ expect(handler.verify_integrity(object, entry)).to be_nil
+ end
+ end
+ end
+
+ end
+end
diff --git a/spec/unit/chef_fs/data_handler/group_handler_spec.rb b/spec/unit/chef_fs/data_handler/group_handler_spec.rb
index 849bc84f92..ac0252162f 100644
--- a/spec/unit/chef_fs/data_handler/group_handler_spec.rb
+++ b/spec/unit/chef_fs/data_handler/group_handler_spec.rb
@@ -29,7 +29,7 @@ class TestEntry < Mash
end
describe Chef::ChefFS::DataHandler::GroupDataHandler do
- describe '#normalize_for_post' do
+ describe "#normalize_for_post" do
let(:entry) do
TestEntry.new("workers.json", "hive")
end
diff --git a/spec/unit/chef_fs/diff_spec.rb b/spec/unit/chef_fs/diff_spec.rb
index c1c9050101..d946fcb9e7 100644
--- a/spec/unit/chef_fs/diff_spec.rb
+++ b/spec/unit/chef_fs/diff_spec.rb
@@ -32,7 +32,7 @@ describe "diff", :uses_diff => true do
include FileSystemSupport
context "with two filesystems with all types of difference" do
- let(:a) {
+ let(:a) do
memory_fs("a", {
:both_dirs => {
:sub_both_dirs => { :subsub => nil },
@@ -58,8 +58,8 @@ describe "diff", :uses_diff => true do
:dir_in_a_file_in_b => {},
:file_in_a_dir_in_b => nil,
}, /cannot_be_in_a/)
- }
- let(:b) {
+ end
+ let(:b) do
memory_fs("b", {
:both_dirs => {
:sub_both_dirs => { :subsub => nil },
@@ -85,7 +85,7 @@ describe "diff", :uses_diff => true do
:dir_in_a_file_in_b => nil,
:file_in_a_dir_in_b => {},
}, /cannot_be_in_b/)
- }
+ end
it "Chef::ChefFS::CommandLine.diff_print(/)" do
results = []
Chef::ChefFS::CommandLine.diff_print(pattern("/"), a, b, nil, nil) do |diff|
diff --git a/spec/unit/chef_fs/file_system/operation_failed_error_spec.rb b/spec/unit/chef_fs/file_system/operation_failed_error_spec.rb
index b0b5a547cd..acdbe35c45 100644
--- a/spec/unit/chef_fs/file_system/operation_failed_error_spec.rb
+++ b/spec/unit/chef_fs/file_system/operation_failed_error_spec.rb
@@ -17,7 +17,7 @@
#
require "spec_helper"
-require "chef/chef_fs/file_system/operation_failed_error"
+require "chef/chef_fs/file_system/exceptions"
describe Chef::ChefFS::FileSystem::OperationFailedError do
context "message" do
@@ -30,17 +30,17 @@ describe Chef::ChefFS::FileSystem::OperationFailedError do
allow(@response).to receive(:code).and_return("400")
allow(@response).to receive(:body).and_return(response_body)
exception = Net::HTTPServerException.new("(exception) unauthorized", @response)
- expect {
+ expect do
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, exception), error_message
- }.to raise_error(Chef::ChefFS::FileSystem::OperationFailedError, "#{error_message} cause: #{response_body}")
+ end.to raise_error(Chef::ChefFS::FileSystem::OperationFailedError, "#{error_message} cause: #{response_body}")
end
end
context "does not have a cause attribute" do
it "does not include error cause" do
- expect {
+ expect do
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self), error_message
- }.to raise_error(Chef::ChefFS::FileSystem::OperationFailedError, error_message)
+ end.to raise_error(Chef::ChefFS::FileSystem::OperationFailedError, error_message)
end
end
end
diff --git a/spec/unit/chef_fs/file_system/repository/base_file_spec.rb b/spec/unit/chef_fs/file_system/repository/base_file_spec.rb
index bb0c267368..625ef32dca 100644
--- a/spec/unit/chef_fs/file_system/repository/base_file_spec.rb
+++ b/spec/unit/chef_fs/file_system/repository/base_file_spec.rb
@@ -47,7 +47,7 @@ describe Chef::ChefFS::FileSystem::Repository::BaseFile do
end
context "#is_json_file?" do
- it "returns false when the file is not json" do
+ it "returns false when the file is not json", pending: "We assume that everything is ruby or JSON" do
file = described_class.new("test_file.dpkg", parent)
expect(file.is_json_file?).to be_falsey
end
@@ -63,11 +63,16 @@ describe Chef::ChefFS::FileSystem::Repository::BaseFile do
expect(file.name_valid?).to be_falsey
end
- it "rejects non json files" do
+ it "rejects non json files", pending: "We assume that everything is ruby or JSON" do
file = described_class.new("test_file.dpkg", parent)
expect(file.name_valid?).to be_falsey
end
+ it "allows ruby files" do
+ file = described_class.new("test_file.rb", parent)
+ expect(file.name_valid?).to be_truthy
+ end
+
it "allows correctly named files" do
expect(file.name_valid?).to be_truthy
end
@@ -105,14 +110,7 @@ describe Chef::ChefFS::FileSystem::Repository::BaseFile do
context "#write" do
context "minimises a json object" do
- it "not if pretty json is off" do
- expect(file).to_not receive(:minimize)
- file.write(content)
- end
-
- it "not if it's not a json file" do
- file = described_class.new("test_file.dpkg", parent)
- file.write_pretty_json = true
+ it "unless pretty json is off" do
expect(file).to_not receive(:minimize)
file.write(content)
end
diff --git a/spec/unit/chef_fs/file_system/repository/directory_spec.rb b/spec/unit/chef_fs/file_system/repository/directory_spec.rb
index e050181be9..e44cc15167 100644
--- a/spec/unit/chef_fs/file_system/repository/directory_spec.rb
+++ b/spec/unit/chef_fs/file_system/repository/directory_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright 2012-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -76,6 +76,7 @@ describe Chef::ChefFS::FileSystem::Repository::Directory do
context "#create_child" do
it "creates a new TestFile" do
expect(TestFile).to receive(:new).with("test_child", test_directory).and_return(file_double)
+ allow(file_double).to receive(:file_path).and_return("#{test_directory}/test_child")
expect(file_double).to receive(:write).with("test")
test_directory.create_child("test_child", "test")
end
@@ -151,7 +152,7 @@ describe Chef::ChefFS::FileSystem::Repository::Directory do
end
after do
- FileUtils.rmdir(tmp_dir)
+ FileUtils.rm_rf(tmp_dir)
end
end
diff --git a/spec/unit/chef_fs/file_system_spec.rb b/spec/unit/chef_fs/file_system_spec.rb
index 429e039dda..c7ed83524b 100644
--- a/spec/unit/chef_fs/file_system_spec.rb
+++ b/spec/unit/chef_fs/file_system_spec.rb
@@ -55,7 +55,7 @@ describe Chef::ChefFS::FileSystem do
end
context "with a populated filesystem" do
- let(:fs) {
+ let(:fs) do
memory_fs("", {
:a => {
:aa => {
@@ -69,7 +69,7 @@ describe Chef::ChefFS::FileSystem do
:x => "",
:y => {},
})
- }
+ end
context "list" do
it "/**" do
list_should_yield_paths(fs, "/**", "/", "/a", "/x", "/y", "/a/aa", "/a/aa/c", "/a/aa/zz", "/a/ab", "/a/ab/c")
@@ -140,7 +140,7 @@ describe Chef::ChefFS::FileSystem do
it "is empty /y" do
expect(Chef::ChefFS::FileSystem.resolve_path(fs, "/y").empty?).to be true
end
- it 'is not a directory and can\'t be tested /x' do
+ it "is not a directory and can't be tested /x" do
expect { Chef::ChefFS::FileSystem.resolve_path(fs, "/x").empty? }.to raise_error(NoMethodError)
end
end
diff --git a/spec/unit/chef_fs/parallelizer.rb b/spec/unit/chef_fs/parallelizer.rb
index 9de8077677..84637f7283 100644
--- a/spec/unit/chef_fs/parallelizer.rb
+++ b/spec/unit/chef_fs/parallelizer.rb
@@ -35,9 +35,9 @@ describe Chef::ChefFS::Parallelizer do
context "With :ordered => false (unordered output)" do
it "An empty input produces an empty output" do
- parallelize([], :ordered => false) do
+ expect(parallelize([], :ordered => false) do
sleep 10
- end.to_a == []
+ end.to_a).to eql([])
expect(elapsed_time).to be < 0.1
end
@@ -107,9 +107,9 @@ describe Chef::ChefFS::Parallelizer do
context "With :ordered => true (ordered output)" do
it "An empty input produces an empty output" do
- parallelize([]) do
+ expect(parallelize([]) do
sleep 10
- end.to_a == []
+ end.to_a).to eql([])
expect(elapsed_time).to be < 0.1
end
@@ -211,9 +211,7 @@ describe Chef::ChefFS::Parallelizer do
occupying_job_finished[0] = true
end.wait
end
- until started
- sleep(0.01)
- end
+ sleep(0.01) until started
end
after :each do
diff --git a/spec/unit/client_spec.rb b/spec/unit/client_spec.rb
index fe9b1afe01..001be10e0b 100644
--- a/spec/unit/client_spec.rb
+++ b/spec/unit/client_spec.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Tim Hinderliter (<tim@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -38,12 +38,23 @@ describe Chef::Client do
end
it "runs ohai with only the minimum required plugins" do
- expected_filter = %w{fqdn machinename hostname platform platform_version os os_version}
+ expected_filter = %w{fqdn machinename hostname platform platform_version ohai_time os os_version}
expect(ohai_system).to receive(:all_plugins).with(expected_filter)
client.run_ohai
end
end
+ context "when Ohai tells us to fail" do
+ it "fails" do
+ ohai_system = Ohai::System.new
+ module Ohai::Exceptions
+ class CriticalPluginFailure < Error; end
+ end
+ expect(ohai_system).to receive(:all_plugins) { raise Ohai::Exceptions::CriticalPluginFailure }
+ expect { client.run_ohai }.to raise_error(SystemExit)
+ end
+ end
+
describe "authentication protocol selection" do
context "when FIPS is disabled" do
before do
@@ -71,48 +82,40 @@ describe Chef::Client do
describe "configuring output formatters" do
context "when no formatter has been configured" do
- context "and STDOUT is a TTY" do
+ it "configures the :doc formatter" do
+ expect(client.formatters_for_run).to eq([[:doc]])
+ end
+
+ context "and force_logger is set" do
before do
- allow(STDOUT).to receive(:tty?).and_return(true)
+ Chef::Config[:force_logger] = true
end
- it "configures the :doc formatter" do
- expect(client.formatters_for_run).to eq([[:doc]])
+ it "configures the :null formatter" do
+ expect(client.formatters_for_run).to eq([[:null]])
end
+ end
- context "and force_logger is set" do
- before do
- Chef::Config[:force_logger] = true
- end
-
- it "configures the :null formatter" do
- expect(Chef::Config[:force_logger]).to be_truthy
- expect(client.formatters_for_run).to eq([[:null]])
- end
-
+ context "and force_formatter is set" do
+ before do
+ Chef::Config[:force_formatter] = true
end
+ it "configures the :doc formatter" do
+ expect(client.formatters_for_run).to eq([[:doc]])
+ end
end
- context "and STDOUT is not a TTY" do
+ context "both are set" do
before do
- allow(STDOUT).to receive(:tty?).and_return(false)
- end
-
- it "configures the :null formatter" do
- expect(client.formatters_for_run).to eq([[:null]])
+ Chef::Config[:force_formatter] = true
+ Chef::Config[:force_logger] = true
end
- context "and force_formatter is set" do
- before do
- Chef::Config[:force_formatter] = true
- end
- it "it configures the :doc formatter" do
- expect(client.formatters_for_run).to eq([[:doc]])
- end
+ it "configures the :doc formatter" do
+ expect(client.formatters_for_run).to eq([[:doc]])
end
end
-
end
context "when a formatter is configured" do
@@ -188,7 +191,7 @@ describe Chef::Client do
# ---Client#sync_cookbooks -- downloads the list of cookbooks to sync
#
expect_any_instance_of(Chef::CookbookSynchronizer).to receive(:sync_cookbooks)
- expect(Chef::ServerAPI).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_cookbook_sync)
+ expect(Chef::ServerAPI).to receive(:new).with(Chef::Config[:chef_server_url], version_class: Chef::CookbookManifestVersions).and_return(http_cookbook_sync)
expect(http_cookbook_sync).to receive(:post).
with("environments/_default/cookbook_versions", { :run_list => ["override_recipe"] }).
and_return({})
@@ -222,7 +225,7 @@ describe Chef::Client do
# ---Client#sync_cookbooks -- downloads the list of cookbooks to sync
#
expect_any_instance_of(Chef::CookbookSynchronizer).to receive(:sync_cookbooks)
- expect(Chef::ServerAPI).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_cookbook_sync)
+ expect(Chef::ServerAPI).to receive(:new).with(Chef::Config[:chef_server_url], version_class: Chef::CookbookManifestVersions).and_return(http_cookbook_sync)
expect(http_cookbook_sync).to receive(:post).
with("environments/_default/cookbook_versions", { :run_list => ["new_run_list_recipe"] }).
and_return({})
@@ -402,6 +405,55 @@ describe Chef::Client do
end
end
+ describe "load_required_recipe" do
+ let(:rest) { double("Chef::ServerAPI (required recipe)") }
+ let(:run_context) { double("Chef::RunContext") }
+ let(:recipe) { double("Chef::Recipe (required recipe)") }
+ let(:required_recipe) do
+ <<EOM
+fake_recipe_variable = "for reals"
+EOM
+ end
+
+ context "when required_recipe is configured" do
+
+ before(:each) do
+ expect(rest).to receive(:get).with("required_recipe").and_return(required_recipe)
+ expect(Chef::Recipe).to receive(:new).with(nil, nil, run_context).and_return(recipe)
+ expect(recipe).to receive(:from_file)
+ end
+
+ it "fetches the recipe and adds it to the run context" do
+ client.load_required_recipe(rest, run_context)
+ end
+
+ context "when the required_recipe has bad contents" do
+ let(:required_recipe) do
+ <<EOM
+this is not a recipe
+EOM
+ end
+ it "should not raise an error" do
+ expect { client.load_required_recipe(rest, run_context) }.not_to raise_error()
+ end
+ end
+ end
+
+ context "when required_recipe returns 404" do
+ let(:http_response) { Net::HTTPNotFound.new("1.1", "404", "Not Found") }
+ let(:http_exception) { Net::HTTPServerException.new('404 "Not Found"', http_response) }
+
+ before(:each) do
+ expect(rest).to receive(:get).with("required_recipe").and_raise(http_exception)
+ end
+
+ it "should log and continue on" do
+ expect(logger).to receive(:trace)
+ client.load_required_recipe(rest, run_context)
+ end
+ end
+ end
+
describe "windows_admin_check" do
context "platform is not windows" do
before do
@@ -430,7 +482,7 @@ describe Chef::Client do
end
it "should not log a warning message" do
- expect(Chef::Log).not_to receive(:warn)
+ expect(logger).not_to receive(:warn)
client.do_windows_admin_check
end
@@ -447,7 +499,7 @@ describe Chef::Client do
end
it "should log a warning message" do
- expect(Chef::Log).to receive(:warn)
+ expect(logger).to receive(:warn)
client.do_windows_admin_check
end
@@ -462,7 +514,7 @@ describe Chef::Client do
describe "assert_cookbook_path_not_empty" do
before do
- Chef::Config[:solo] = true
+ Chef::Config[:solo_legacy_mode] = true
Chef::Config[:cookbook_path] = ["/path/to/invalid/cookbook_path"]
end
@@ -508,7 +560,6 @@ describe Chef::Client do
expect { client.node_name }.to raise_error(Chef::Exceptions::CannotDetermineNodeName)
end
end
-
end
describe "always attempt to run handlers" do
diff --git a/spec/unit/config_fetcher_spec.rb b/spec/unit/config_fetcher_spec.rb
index 35cf27f2af..a674d4de33 100644
--- a/spec/unit/config_fetcher_spec.rb
+++ b/spec/unit/config_fetcher_spec.rb
@@ -58,7 +58,7 @@ describe Chef::ConfigFetcher do
and_return(invalid_json)
expect(Chef::Application).to receive(:fatal!).
- with(invalid_json_error_regex, 2)
+ with(invalid_json_error_regex)
fetcher.fetch_json
end
end
@@ -104,7 +104,7 @@ describe Chef::ConfigFetcher do
with("").and_return(invalid_json)
expect(Chef::Application).to receive(:fatal!).
- with(invalid_json_error_regex, 2)
+ with(invalid_json_error_regex)
fetcher.fetch_json
end
end
diff --git a/spec/unit/cookbook/cookbook_version_loader_spec.rb b/spec/unit/cookbook/cookbook_version_loader_spec.rb
index 87d0f1e032..40a054abee 100644
--- a/spec/unit/cookbook/cookbook_version_loader_spec.rb
+++ b/spec/unit/cookbook/cookbook_version_loader_spec.rb
@@ -40,42 +40,45 @@ describe Chef::Cookbook::CookbookVersionLoader do
File.join(cookbook_path, cookbook_relative_path)
end
+ def full_paths_for_part(part)
+ loaded_cookbook.files_for(part).inject([]) { |memo, f| memo << f[:full_path]; memo }
+ end
+
it "loads attribute files of the cookbook" do
- expect(loaded_cookbook.attribute_filenames).to include(full_path("/attributes/default.rb"))
- expect(loaded_cookbook.attribute_filenames).to include(full_path("/attributes/smokey.rb"))
+ expect(full_paths_for_part("attributes")).to include(full_path("/attributes/default.rb"))
+ expect(full_paths_for_part("attributes")).to include(full_path("/attributes/smokey.rb"))
end
it "loads definition files" do
- expect(loaded_cookbook.definition_filenames).to include(full_path("/definitions/client.rb"))
- expect(loaded_cookbook.definition_filenames).to include(full_path("/definitions/server.rb"))
+ expect(full_paths_for_part("definitions")).to include(full_path("/definitions/client.rb"))
+ expect(full_paths_for_part("definitions")).to include(full_path("/definitions/server.rb"))
end
it "loads recipes" do
- expect(loaded_cookbook.recipe_filenames).to include(full_path("/recipes/default.rb"))
- expect(loaded_cookbook.recipe_filenames).to include(full_path("/recipes/gigantor.rb"))
- expect(loaded_cookbook.recipe_filenames).to include(full_path("/recipes/one.rb"))
- expect(loaded_cookbook.recipe_filenames).to include(full_path("/recipes/return.rb"))
+ expect(full_paths_for_part("recipes")).to include(full_path("/recipes/default.rb"))
+ expect(full_paths_for_part("recipes")).to include(full_path("/recipes/gigantor.rb"))
+ expect(full_paths_for_part("recipes")).to include(full_path("/recipes/one.rb"))
+ expect(full_paths_for_part("recipes")).to include(full_path("/recipes/return.rb"))
end
it "loads libraries" do
- expect(loaded_cookbook.library_filenames).to include(full_path("/libraries/openldap.rb"))
- expect(loaded_cookbook.library_filenames).to include(full_path("/libraries/openldap/version.rb"))
+ expect(full_paths_for_part("libraries")).to include(full_path("/libraries/openldap.rb"))
+ expect(full_paths_for_part("libraries")).to include(full_path("/libraries/openldap/version.rb"))
end
it "loads static files in the files/ dir" do
- expect(loaded_cookbook.file_filenames).to include(full_path("/files/default/remotedir/remotesubdir/remote_subdir_file1.txt"))
- expect(loaded_cookbook.file_filenames).to include(full_path("/files/default/remotedir/remotesubdir/remote_subdir_file2.txt"))
+ expect(full_paths_for_part("files")).to include(full_path("/files/default/remotedir/remotesubdir/remote_subdir_file1.txt"))
+ expect(full_paths_for_part("files")).to include(full_path("/files/default/remotedir/remotesubdir/remote_subdir_file2.txt"))
end
it "loads files that start with a ." do
- expect(loaded_cookbook.file_filenames).to include(full_path("/files/default/.dotfile"))
- expect(loaded_cookbook.file_filenames).to include(full_path("/files/default/.ssh/id_rsa"))
- expect(loaded_cookbook.file_filenames).to include(full_path("/files/default/remotedir/.a_dotdir/.a_dotfile_in_a_dotdir"))
+ expect(full_paths_for_part("files")).to include(full_path("/files/default/.dotfile"))
+ expect(full_paths_for_part("files")).to include(full_path("/files/default/.ssh/id_rsa"))
+ expect(full_paths_for_part("files")).to include(full_path("/files/default/remotedir/.a_dotdir/.a_dotfile_in_a_dotdir"))
end
it "loads root files that start with a ." do
expect(loaded_cookbook.all_files).to include(full_path(".root_dotfile"))
- expect(loaded_cookbook.root_filenames).to include(full_path(".root_dotfile"))
end
it "loads all unignored files, even if they don't match a segment type" do
@@ -97,13 +100,22 @@ describe Chef::Cookbook::CookbookVersionLoader do
let(:cookbook_path) { File.join(CHEF_SPEC_DATA, "kitchen/openldap") }
it "skips ignored files" do
- expect(loaded_cookbook.recipe_filenames).to include(full_path("recipes/gigantor.rb"))
- expect(loaded_cookbook.recipe_filenames).to include(full_path("recipes/woot.rb"))
- expect(loaded_cookbook.recipe_filenames).to_not include(full_path("recipes/ignoreme.rb"))
+ expect(full_paths_for_part("recipes")).to include(full_path("recipes/gigantor.rb"))
+ expect(full_paths_for_part("recipes")).to include(full_path("recipes/woot.rb"))
+ expect(full_paths_for_part("recipes")).to_not include(full_path("recipes/ignoreme.rb"))
end
end
+ context "when a cookbook's metadata.rb does not parse but the compiled metadata.json is present" do
+ let(:cookbook_path) { File.join(CHEF_SPEC_DATA, "prefer_metadata_json") }
+
+ it "reads the cookbook" do
+ expect(loaded_cookbook.metadata.name.to_s).to eq("prefer_metadata_json")
+ expect(loaded_cookbook.metadata.version.to_s).to eq("1.2.3")
+ end
+ end
+
context "when the given path is not actually a cookbook" do
let(:cookbook_path) { File.join(CHEF_SPEC_DATA, "cookbooks/NOTHING_HERE_FOLKS") }
diff --git a/spec/unit/cookbook/file_vendor_spec.rb b/spec/unit/cookbook/file_vendor_spec.rb
index 139a5932f9..557e1b8775 100644
--- a/spec/unit/cookbook/file_vendor_spec.rb
+++ b/spec/unit/cookbook/file_vendor_spec.rb
@@ -16,6 +16,7 @@
# limitations under the License.
#
require "spec_helper"
+require "chef/cookbook_version"
describe Chef::Cookbook::FileVendor do
@@ -25,6 +26,12 @@ describe Chef::Cookbook::FileVendor do
let(:http) { double("Chef::ServerAPI") }
+ # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest
+ let(:manifest) do
+ cbv = Chef::CookbookVersion.new("bob", Array(Dir.tmpdir))
+ cbv.cookbook_manifest
+ end
+
before do
file_vendor_class.fetch_from_remote(http)
end
@@ -39,8 +46,11 @@ describe Chef::Cookbook::FileVendor do
context "with a manifest from a cookbook version" do
- # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest
- let(:manifest) { { :cookbook_name => "bob", :name => "bob-1.2.3" } }
+ # # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest
+ # let(:manifest) do
+ # cbv = Chef::CookbookVersion.new("bob", Array(Dir.tmpdir))
+ # cbv.cookbook_manifest
+ # end
it "creates a RemoteFileVendor for a given manifest" do
file_vendor = file_vendor_class.create_from_manifest(manifest)
@@ -53,9 +63,6 @@ describe Chef::Cookbook::FileVendor do
context "with a manifest from a cookbook artifact" do
- # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest
- let(:manifest) { { :name => "bob" } }
-
it "creates a RemoteFileVendor for a given manifest" do
file_vendor = file_vendor_class.create_from_manifest(manifest)
expect(file_vendor).to be_a_kind_of(Chef::Cookbook::RemoteFileVendor)
@@ -70,8 +77,10 @@ describe Chef::Cookbook::FileVendor do
let(:cookbook_path) { %w{/var/chef/cookbooks /var/chef/other_cookbooks} }
- # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest
- let(:manifest) { { :cookbook_name => "bob" } }
+ let(:manifest) do
+ cbv = Chef::CookbookVersion.new("bob", Array(Dir.tmpdir))
+ cbv.cookbook_manifest
+ end
before do
file_vendor_class.fetch_from_disk(cookbook_path)
@@ -94,4 +103,21 @@ describe Chef::Cookbook::FileVendor do
end
+ context "when vendoring a cookbook with a name mismatch" do
+ let(:cookbook_path) { File.join(CHEF_SPEC_DATA, "cookbooks") }
+
+ let(:manifest) do
+ cbv = Chef::CookbookVersion.new("name-mismatch", Array(Dir.tmpdir))
+ cbv.cookbook_manifest
+ end
+
+ before do
+ file_vendor_class.fetch_from_disk(cookbook_path)
+ end
+
+ it "retrieves the file from the correct location based on path to the cookbook that conatins the correct name metadata" do
+ file_vendor = file_vendor_class.create_from_manifest(manifest)
+ file_vendor.get_filename("metadata.rb")
+ end
+ end
end
diff --git a/spec/unit/cookbook/gem_installer_spec.rb b/spec/unit/cookbook/gem_installer_spec.rb
new file mode 100644
index 0000000000..91e6959331
--- /dev/null
+++ b/spec/unit/cookbook/gem_installer_spec.rb
@@ -0,0 +1,85 @@
+require "spec_helper"
+require "bundler/dsl"
+
+describe Chef::Cookbook::GemInstaller do
+ let(:cookbook_collection) do
+ {
+ test: double(
+ :cookbook,
+ metadata: double(
+ :metadata,
+ gems: [["httpclient"], ["nokogiri"]]
+ )
+ ),
+ test2: double(
+ :cookbook,
+ metadata: double(
+ :metadata,
+ gems: [["httpclient", ">= 2.0"]]
+ )
+ ),
+ test3: double(
+ :cookbook,
+ metadata: double(
+ :metadata,
+ gems: [["httpclient", ">= 1.0"]]
+ )
+ ),
+ }
+ end
+
+ let(:gem_installer) do
+ described_class.new(cookbook_collection, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ let(:gemfile) do
+ StringIO.new
+ end
+
+ let(:shell_out) do
+ double(:shell_out, stdout: "")
+ end
+
+ let(:bundler_dsl) do
+ b = Bundler::Dsl.new
+ b.instance_eval(gemfile.string)
+ b
+ end
+
+ before(:each) do
+ # Prepare mocks: using a StringIO instead of a File
+ expect(Dir).to receive(:mktmpdir).and_yield("")
+ expect(File).to receive(:open).and_yield(gemfile)
+ expect(gemfile).to receive(:path).and_return("")
+ expect(IO).to receive(:read).and_return("")
+ expect(gem_installer).to receive(:shell_out!).and_return(shell_out)
+
+ end
+
+ it "generates a valid Gemfile" do
+ expect { gem_installer.install }.to_not raise_error
+
+ expect { bundler_dsl }.to_not raise_error
+ end
+
+ it "generate a Gemfile with all constraints" do
+ expect { gem_installer.install }.to_not raise_error
+
+ expect(bundler_dsl.dependencies.find { |d| d.name == "httpclient" }.requirements_list.length).to eql(2)
+ end
+
+ it "generates a valid Gemfile when Chef::Config[:rubygems_url] is set to a String" do
+ Chef::Config[:rubygems_url] = "https://www.rubygems.org"
+ expect { gem_installer.install }.to_not raise_error
+
+ expect(bundler_dsl.dependencies.find { |d| d.name == "httpclient" }.requirements_list.length).to eql(2)
+ end
+
+ it "generates a valid Gemfile when Chef::Config[:rubygems_url] is set to an Array" do
+ Chef::Config[:rubygems_url] = [ "https://www.rubygems.org" ]
+
+ expect { gem_installer.install }.to_not raise_error
+
+ expect(bundler_dsl.dependencies.find { |d| d.name == "httpclient" }.requirements_list.length).to eql(2)
+ end
+end
diff --git a/spec/unit/cookbook/manifest_v0_spec.rb b/spec/unit/cookbook/manifest_v0_spec.rb
new file mode 100644
index 0000000000..0f5cfbe7a4
--- /dev/null
+++ b/spec/unit/cookbook/manifest_v0_spec.rb
@@ -0,0 +1,133 @@
+#
+# Copyright:: Copyright 2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "spec_helper"
+require "chef/cookbook_manifest"
+require "chef/digester"
+require "pathname"
+
+describe Chef::Cookbook::ManifestV0 do
+ let(:version) { "1.2.3" }
+
+ let(:identifier) { "9e10455ce2b4a4e29424b7064b1d67a1a25c9d3b" }
+
+ let(:metadata) do
+ Chef::Cookbook::Metadata.new.tap do |m|
+ m.version(version)
+ end
+ end
+
+ let(:cookbook_root) { "/tmp/blah" }
+
+ let(:cookbook_version) do
+ Chef::CookbookVersion.new("tatft", cookbook_root).tap do |c|
+ c.metadata = metadata
+ c.identifier = identifier
+ end
+ end
+
+ let(:cookbook_manifest) { Chef::CookbookManifest.new(cookbook_version) }
+
+ let(:cookbook_root) { File.join(CHEF_SPEC_DATA, "cb_version_cookbooks", "tatft") }
+
+ let(:all_files) { Dir[File.join(cookbook_root, "**", "**")].reject { |f| File.directory? f } }
+
+ let(:expected_hash) do
+ {
+ "attributes" => [{ "name" => "default.rb", "path" => "attributes/default.rb", "checksum" => "a88697db56181498a8828d5531271ad9", "specificity" => "default" }],
+ "chef_type" => "cookbook_version",
+ "cookbook_name" => "tatft",
+ "definitions" => [{ "name" => "runit_service.rb", "path" => "definitions/runit_service.rb", "checksum" => "c40cf9b4c6eb15a8e49e31602f701161", "specificity" => "default" }],
+ "files" => [{ "name" => "giant_blob.tgz", "path" => "files/default/giant_blob.tgz", "checksum" => "5b4b194bb80938bb18da7af5c823cb1b", "specificity" => "default" }],
+ "frozen?" => false,
+ "libraries" => [{ "name" => "ownage.rb", "path" => "libraries/ownage.rb", "checksum" => "4686edd9968909034692e09e058d90d9", "specificity" => "default" }],
+ "name" => "tatft-1.2.3",
+ "providers" => [{ "name" => "lwp.rb", "path" => "providers/lwp.rb", "checksum" => "bc189d68f77bb054d1070aeff7669557", "specificity" => "default" }],
+ "recipes" => [{ "name" => "default.rb", "path" => "recipes/default.rb", "checksum" => "09bc749f00c68717d288de9c8d7c644f", "specificity" => "default" }],
+ "resources" => [{ "name" => "lwr.rb", "path" => "resources/lwr.rb", "checksum" => "609c40d3d3f269e7edf230277a240ef5", "specificity" => "default" }],
+ "root_files" => [{ "name" => "README.rdoc", "path" => "README.rdoc", "checksum" => "cd7be9a1b9b1f33e3bcd9c3f4bc8dde5", "specificity" => "default" }],
+ "templates" => [{ "name" => "configuration.erb", "path" => "templates/default/configuration.erb", "checksum" => "d41d8cd98f00b204e9800998ecf8427e", "specificity" => "default" }],
+ "version" => "1.2.3",
+ }
+ end
+
+ describe "#from_hash" do
+ let(:source_hash) do
+ {
+ "attributes" => [{ "name" => "default.rb", "path" => "attributes/default.rb", "checksum" => "a88697db56181498a8828d5531271ad9", "specificity" => "default" }],
+ "recipes" => [{ "name" => "default.rb", "path" => "recipes/default.rb", "checksum" => "09bc749f00c68717d288de9c8d7c644f", "specificity" => "default" }],
+ "root_files" => [{ "name" => "README.rdoc", "path" => "README.rdoc", "checksum" => "cd7be9a1b9b1f33e3bcd9c3f4bc8dde5", "specificity" => "default" }],
+ "name" => "tatft-1.2.3",
+ "version" => "1.2.3",
+ }
+ end
+
+ it "preserves the version" do
+ result = described_class.from_hash(source_hash)
+ expect(result["version"]).to eq "1.2.3"
+ end
+
+ it "creates an all_files key and populates it" do
+ result = described_class.from_hash(source_hash)
+ expect(result[:all_files].map { |f| f["name"] }).to match_array %w{ recipes/default.rb attributes/default.rb README.rdoc }
+ end
+
+ it "deletes unwanted segment types" do
+ result = described_class.from_hash(source_hash)
+ expect(result["attributes"]).to be_nil
+ end
+
+ it "preserves frozeness" do
+ source_hash["frozen?"] = true
+ result = described_class.from_hash(source_hash)
+ expect(result["frozen?"]).to be true
+ end
+ end
+
+ describe "#to_hash" do
+ it "accepts a cookbook manifest" do
+ result = described_class.to_hash(cookbook_manifest)
+ expect(result).to be_a(Hash)
+ end
+
+ it "preserves frozeness" do
+ cookbook_version.freeze_version
+ expect(described_class.to_hash(cookbook_manifest)["frozen?"]).to be true
+ end
+ end
+
+ context "ensures that all segments exist" do
+ Chef::Cookbook::ManifestV0::COOKBOOK_SEGMENTS.each do |segment|
+ it "with #{segment}" do
+ result = described_class.to_hash(cookbook_manifest)
+ expect(result[segment]).to be_empty
+ end
+ end
+ end
+
+ context "when given a cookbook with some files" do
+ before do
+ cookbook_version.all_files = all_files
+ end
+
+ Chef::Cookbook::ManifestV0::COOKBOOK_SEGMENTS.each do |segment|
+ it "places the files for #{segment} correctly" do
+ result = described_class.to_hash(cookbook_manifest)
+ expect(result[segment]).to eq(expected_hash[segment])
+ end
+ end
+ end
+end
diff --git a/spec/unit/cookbook/manifest_v2_spec.rb b/spec/unit/cookbook/manifest_v2_spec.rb
new file mode 100644
index 0000000000..23df950f4a
--- /dev/null
+++ b/spec/unit/cookbook/manifest_v2_spec.rb
@@ -0,0 +1,70 @@
+#
+# Copyright:: Copyright 2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "spec_helper"
+require "chef/cookbook_manifest"
+require "chef/digester"
+require "pathname"
+
+describe Chef::Cookbook::ManifestV2 do
+ let(:version) { "1.2.3" }
+
+ let(:identifier) { "9e10455ce2b4a4e29424b7064b1d67a1a25c9d3b" }
+
+ let(:metadata) do
+ Chef::Cookbook::Metadata.new.tap do |m|
+ m.version(version)
+ end
+ end
+
+ let(:cookbook_root) { "/tmp/blah" }
+
+ let(:cookbook_version) do
+ Chef::CookbookVersion.new("tatft", cookbook_root).tap do |c|
+ c.metadata = metadata
+ c.identifier = identifier
+ end
+ end
+
+ let(:cookbook_manifest) { Chef::CookbookManifest.new(cookbook_version) }
+
+ let(:cookbook_root) { File.join(CHEF_SPEC_DATA, "cb_version_cookbooks", "tatft") }
+
+ let(:all_files) { Dir[File.join(cookbook_root, "**", "**")].reject { |f| File.directory? f } }
+
+ describe "#to_hash" do
+ it "accepts a cookbook manifest" do
+ result = described_class.to_hash(cookbook_manifest)
+ expect(result).to be_a(Hash)
+ end
+
+ it "preserves frozeness" do
+ cookbook_version.freeze_version
+ expect(described_class.to_hash(cookbook_manifest)["frozen?"]).to be true
+ end
+ end
+
+ context "when given a cookbook with some files" do
+ before do
+ cookbook_version.all_files = all_files
+ end
+
+ it "populates all_files correctly" do
+ result = described_class.to_hash(cookbook_manifest)
+ expect(result["all_files"][0]).not_to include(:full_path)
+ end
+ end
+end
diff --git a/spec/unit/cookbook/metadata_spec.rb b/spec/unit/cookbook/metadata_spec.rb
index c6d7e41283..a2cb35e60d 100644
--- a/spec/unit/cookbook/metadata_spec.rb
+++ b/spec/unit/cookbook/metadata_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,8 +28,7 @@ describe Chef::Cookbook::Metadata do
before do
@fields = [ :name, :description, :long_description, :maintainer,
:maintainer_email, :license, :platforms, :dependencies,
- :recommendations, :suggestions, :conflicting, :providing,
- :replacing, :attributes, :groupings, :recipes, :version,
+ :providing, :attributes, :recipes, :version,
:source_url, :issues_url, :privacy, :ohai_versions, :chef_versions,
:gems ]
end
@@ -99,11 +98,11 @@ describe Chef::Cookbook::Metadata do
end
it "has an empty maintainer field" do
- expect(metadata.maintainer).to eq(nil)
+ expect(metadata.maintainer).to eq("")
end
it "has an empty maintainer_email field" do
- expect(metadata.maintainer).to eq(nil)
+ expect(metadata.maintainer_email).to eq("")
end
it "has an empty platforms list" do
@@ -114,30 +113,10 @@ describe Chef::Cookbook::Metadata do
expect(metadata.dependencies).to eq(Mash.new)
end
- it "has an empty recommends list" do
- expect(metadata.recommendations).to eq(Mash.new)
- end
-
- it "has an empty suggestions list" do
- expect(metadata.suggestions).to eq(Mash.new)
- end
-
- it "has an empty conflicts list" do
- expect(metadata.conflicting).to eq(Mash.new)
- end
-
- it "has an empty replaces list" do
- expect(metadata.replacing).to eq(Mash.new)
- end
-
it "has an empty attributes list" do
expect(metadata.attributes).to eq(Mash.new)
end
- it "has an empty groupings list" do
- expect(metadata.groupings).to eq(Mash.new)
- end
-
it "has an empty recipes list" do
expect(metadata.recipes).to eq(Mash.new)
end
@@ -206,7 +185,7 @@ describe Chef::Cookbook::Metadata do
:issues_url => "http://example.com/issues",
:privacy => true,
}
- params.sort { |a, b| a.to_s <=> b.to_s }.each do |field, field_value|
+ params.sort_by(&:to_s).each do |field, field_value|
describe field do
it "should be set-able via #{field}" do
expect(metadata.send(field, field_value)).to eql(field_value)
@@ -234,13 +213,9 @@ describe Chef::Cookbook::Metadata do
dep_types = {
:depends => [ :dependencies, "foo::bar", "> 0.2" ],
- :recommends => [ :recommendations, "foo::bar", ">= 0.2" ],
- :suggests => [ :suggestions, "foo::bar", "> 0.2" ],
- :conflicts => [ :conflicting, "foo::bar", "~> 0.2" ],
:provides => [ :providing, "foo::bar", "<= 0.2" ],
- :replaces => [ :replacing, "foo::bar", "= 0.2.1" ],
}
- dep_types.sort { |a, b| a.to_s <=> b.to_s }.each do |dep, dep_args|
+ dep_types.sort_by(&:to_s).each do |dep, dep_args|
check_with = dep_args.shift
describe dep do
it "should be set-able via #{dep}" do
@@ -255,13 +230,9 @@ describe Chef::Cookbook::Metadata do
dep_types = {
:depends => [ :dependencies, "foo::bar", ">0.2", "> 0.2" ],
- :recommends => [ :recommendations, "foo::bar", ">=0.2", ">= 0.2" ],
- :suggests => [ :suggestions, "foo::bar", ">0.2", "> 0.2" ],
- :conflicts => [ :conflicting, "foo::bar", "~>0.2", "~> 0.2" ],
:provides => [ :providing, "foo::bar", "<=0.2", "<= 0.2" ],
- :replaces => [ :replacing, "foo::bar", "=0.2.1", "= 0.2.1" ],
}
- dep_types.sort { |a, b| a.to_s <=> b.to_s }.each do |dep, dep_args|
+ dep_types.sort_by(&:to_s).each do |dep, dep_args|
check_with = dep_args.shift
normalized_version = dep_args.pop
describe dep do
@@ -278,11 +249,7 @@ describe Chef::Cookbook::Metadata do
describe "in the obsoleted format" do
dep_types = {
:depends => [ "foo::bar", "> 0.2", "< 1.0" ],
- :recommends => [ "foo::bar", ">= 0.2", "< 1.0" ],
- :suggests => [ "foo::bar", "> 0.2", "< 1.0" ],
- :conflicts => [ "foo::bar", "> 0.2", "< 1.0" ],
:provides => [ "foo::bar", "> 0.2", "< 1.0" ],
- :replaces => [ "foo::bar", "> 0.2.1", "< 1.0" ],
}
dep_types.each do |dep, dep_args|
@@ -295,11 +262,7 @@ describe Chef::Cookbook::Metadata do
describe "with obsolete operators" do
dep_types = {
:depends => [ "foo::bar", ">> 0.2"],
- :recommends => [ "foo::bar", ">> 0.2"],
- :suggests => [ "foo::bar", ">> 0.2"],
- :conflicts => [ "foo::bar", ">> 0.2"],
:provides => [ "foo::bar", ">> 0.2"],
- :replaces => [ "foo::bar", ">> 0.2.1"],
}
dep_types.each do |dep, dep_args|
@@ -452,33 +415,6 @@ describe Chef::Cookbook::Metadata do
end
end
- describe "attribute groupings" do
- it "should allow you set a grouping" do
- group = {
- "title" => "MySQL Tuning",
- "description" => "Setting from the my.cnf file that allow you to tune your mysql server",
- }
- expect(metadata.grouping("/db/mysql/databases/tuning", group)).to eq(group)
- end
- it "should not accept anything but a string for display_name" do
- expect {
- metadata.grouping("db/mysql/databases", :title => "foo")
- }.not_to raise_error
- expect {
- metadata.grouping("db/mysql/databases", :title => Hash.new)
- }.to raise_error(ArgumentError)
- end
-
- it "should not accept anything but a string for the description" do
- expect {
- metadata.grouping("db/mysql/databases", :description => "foo")
- }.not_to raise_error
- expect {
- metadata.grouping("db/mysql/databases", :description => Hash.new)
- }.to raise_error(ArgumentError)
- end
- end
-
describe "cookbook attributes" do
it "should allow you set an attributes metadata" do
attrs = {
@@ -498,63 +434,63 @@ describe Chef::Cookbook::Metadata do
end
it "should not accept anything but a string for display_name" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :display_name => "foo")
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :display_name => Hash.new)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should not accept anything but a string for the description" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :description => "foo")
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :description => Hash.new)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should not accept anything but a string for the source_url" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :source_url => "foo")
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :source_url => Hash.new)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should not accept anything but a string for the issues_url" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :issues_url => "foo")
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :issues_url => Hash.new)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should not accept anything but true or false for the privacy flag" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :privacy => true)
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :privacy => false)
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :privacy => "true")
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should not accept anything but an array of strings for choice" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :choice => %w{dedicated shared})
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :choice => [10, "shared"])
- }.to raise_error(ArgumentError)
- expect {
+ end.to raise_error(ArgumentError)
+ expect do
metadata.attribute("db/mysql/databases", :choice => Hash.new)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should set choice to empty array by default" do
@@ -563,15 +499,15 @@ describe Chef::Cookbook::Metadata do
end
it "should let calculated be true or false" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :calculated => true)
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :calculated => false)
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :calculated => Hash.new)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should set calculated to false by default" do
@@ -580,55 +516,55 @@ describe Chef::Cookbook::Metadata do
end
it "accepts String for the attribute type" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :type => "string")
- }.not_to raise_error
+ end.not_to raise_error
end
it "accepts Array for the attribute type" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :type => "array")
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :type => Array.new)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "accepts symbol for the attribute type" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :type => "symbol")
- }.not_to raise_error
+ end.not_to raise_error
end
it "should let type be hash (backwards compatibility only)" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :type => "hash")
- }.not_to raise_error
+ end.not_to raise_error
end
it "should let required be required, recommended or optional" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :required => "required")
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :required => "recommended")
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :required => "optional")
- }.not_to raise_error
+ end.not_to raise_error
end
it "should convert required true to required" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :required => true)
- }.not_to raise_error
+ end.not_to raise_error
#attrib = metadata.attributes["db/mysql/databases"][:required].should == "required"
end
it "should convert required false to optional" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :required => false)
- }.not_to raise_error
+ end.not_to raise_error
#attrib = metadata.attributes["db/mysql/databases"][:required].should == "optional"
end
@@ -638,12 +574,12 @@ describe Chef::Cookbook::Metadata do
end
it "should make sure recipes is an array" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :recipes => [])
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :required => Hash.new)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should set recipes to an empty array by default" do
@@ -652,24 +588,24 @@ describe Chef::Cookbook::Metadata do
end
it "should allow the default value to be a string, array, hash, boolean or numeric" do
- expect {
+ expect do
metadata.attribute("db/mysql/databases", :default => [])
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :default => {})
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :default => "alice in chains")
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :default => 1337)
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :default => true)
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
metadata.attribute("db/mysql/databases", :required => :not_gonna_do_it)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should limit the types allowed in the choice array" do
@@ -678,94 +614,97 @@ describe Chef::Cookbook::Metadata do
:choice => %w{test1 test2},
:default => "test1",
}
- expect {
+ expect do
metadata.attribute("test_cookbook/test", options)
- }.not_to raise_error
+ end.not_to raise_error
options = {
:type => "boolean",
:choice => [ true, false ],
:default => true,
}
- expect {
+ expect do
metadata.attribute("test_cookbook/test", options)
- }.not_to raise_error
+ end.not_to raise_error
options = {
:type => "numeric",
:choice => [ 1337, 420 ],
:default => 1337,
}
- expect {
+ expect do
metadata.attribute("test_cookbook/test", options)
- }.not_to raise_error
+ end.not_to raise_error
options = {
:type => "numeric",
:choice => [ true, "false" ],
:default => false,
}
- expect {
+ expect do
metadata.attribute("test_cookbook/test", options)
- }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end.to raise_error(Chef::Exceptions::ValidationFailed)
end
it "should error if default used with calculated" do
- expect {
+ expect do
attrs = {
:calculated => true,
:default => [ "I thought you said calculated" ],
}
metadata.attribute("db/mysql/databases", attrs)
- }.to raise_error(ArgumentError)
- expect {
+ end.to raise_error(ArgumentError)
+ expect do
attrs = {
:calculated => true,
:default => "I thought you said calculated",
}
metadata.attribute("db/mysql/databases", attrs)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should allow a default that is a choice" do
- expect {
+ expect do
attrs = {
:choice => %w{a b c},
:default => "b",
}
metadata.attribute("db/mysql/databases", attrs)
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
attrs = {
:choice => %w{a b c d e},
:default => %w{b d},
}
metadata.attribute("db/mysql/databases", attrs)
- }.not_to raise_error
+ end.not_to raise_error
end
it "should error if default is not a choice" do
- expect {
+ expect do
attrs = {
:choice => %w{a b c},
:default => "d",
}
metadata.attribute("db/mysql/databases", attrs)
- }.to raise_error(ArgumentError)
- expect {
+ end.to raise_error(ArgumentError)
+ expect do
attrs = {
:choice => %w{a b c d e},
:default => %w{b z},
}
metadata.attribute("db/mysql/databases", attrs)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
end
describe "recipes" do
let(:cookbook) do
c = Chef::CookbookVersion.new("test_cookbook")
- c.recipe_files = [ "default.rb", "enlighten.rb" ]
+ c.manifest = { all_files: [
+ { name: "recipes/default.rb", path: "recipes/default.rb", checksum: "my_only_friend" },
+ { name: "recipes/enlighten.rb", path: "recipes/enlighten.rb", checksum: "my_only_friend" },
+ ] }
c
end
@@ -801,11 +740,7 @@ describe Chef::Cookbook::Metadata do
metadata.depends "bobo", "= 1.0"
metadata.depends "bubu", "=1.0"
metadata.depends "bobotclown", "= 1.1"
- metadata.recommends "snark", "< 3.0"
- metadata.suggests "kindness", "> 2.0"
- metadata.conflicts "hatred"
metadata.provides "foo(:bar, :baz)"
- metadata.replaces "snarkitron"
metadata.recipe "test_cookbook::enlighten", "is your buddy"
metadata.attribute "bizspark/has_login",
:display_name => "You have nothing"
@@ -840,11 +775,7 @@ describe Chef::Cookbook::Metadata do
license
platforms
dependencies
- suggestions
- recommendations
- conflicting
providing
- replacing
attributes
recipes
version
@@ -885,11 +816,7 @@ describe Chef::Cookbook::Metadata do
license
platforms
dependencies
- suggestions
- recommendations
- conflicting
providing
- replacing
attributes
recipes
version
@@ -911,42 +838,55 @@ describe Chef::Cookbook::Metadata do
@hash = metadata.to_hash
end
- [:dependencies,
- :recommendations,
- :suggestions,
- :conflicting,
- :replacing].each do |to_check|
- it "should transform deprecated greater than syntax for :#{to_check}" do
- @hash[to_check.to_s]["foo::bar"] = ">> 0.2"
- deserial = Chef::Cookbook::Metadata.from_hash(@hash)
- expect(deserial.send(to_check)["foo::bar"]).to eq("> 0.2")
- end
+ it "should transform deprecated greater than syntax for :dependencies" do
+ @hash[:dependencies.to_s]["foo::bar"] = ">> 0.2"
+ deserial = Chef::Cookbook::Metadata.from_hash(@hash)
+ expect(deserial.send(:dependencies)["foo::bar"]).to eq("> 0.2")
+ end
- it "should transform deprecated less than syntax for :#{to_check}" do
- @hash[to_check.to_s]["foo::bar"] = "<< 0.2"
- deserial = Chef::Cookbook::Metadata.from_hash(@hash)
- expect(deserial.send(to_check)["foo::bar"]).to eq("< 0.2")
- end
+ it "should transform deprecated less than syntax for :dependencies" do
+ @hash[:dependencies.to_s]["foo::bar"] = "<< 0.2"
+ deserial = Chef::Cookbook::Metadata.from_hash(@hash)
+ expect(deserial.send(:dependencies)["foo::bar"]).to eq("< 0.2")
+ end
- it "should ignore multiple dependency constraints for :#{to_check}" do
- @hash[to_check.to_s]["foo::bar"] = [ ">= 1.0", "<= 5.2" ]
- deserial = Chef::Cookbook::Metadata.from_hash(@hash)
- expect(deserial.send(to_check)["foo::bar"]).to eq([])
- end
+ it "should ignore multiple dependency constraints for :dependencies" do
+ @hash[:dependencies.to_s]["foo::bar"] = [ ">= 1.0", "<= 5.2" ]
+ deserial = Chef::Cookbook::Metadata.from_hash(@hash)
+ expect(deserial.send(:dependencies)["foo::bar"]).to eq([])
+ end
- it "should accept an empty array of dependency constraints for :#{to_check}" do
- @hash[to_check.to_s]["foo::bar"] = []
- deserial = Chef::Cookbook::Metadata.from_hash(@hash)
- expect(deserial.send(to_check)["foo::bar"]).to eq([])
- end
+ it "should accept an empty array of dependency constraints for :dependencies" do
+ @hash[:dependencies.to_s]["foo::bar"] = []
+ deserial = Chef::Cookbook::Metadata.from_hash(@hash)
+ expect(deserial.send(:dependencies)["foo::bar"]).to eq([])
+ end
+
+ it "should accept single-element arrays of dependency constraints for :dependencies" do
+ @hash[:dependencies.to_s]["foo::bar"] = [ ">= 2.0" ]
+ deserial = Chef::Cookbook::Metadata.from_hash(@hash)
+ expect(deserial.send(:dependencies)["foo::bar"]).to eq(">= 2.0")
+ end
+ end
- it "should accept single-element arrays of dependency constraints for :#{to_check}" do
- @hash[to_check.to_s]["foo::bar"] = [ ">= 2.0" ]
- deserial = Chef::Cookbook::Metadata.from_hash(@hash)
- expect(deserial.send(to_check)["foo::bar"]).to eq(">= 2.0")
+ describe "from_file" do
+ it "ignores unknown metadata fields in metadata.rb files" do
+ expect(Chef::Log).to receive(:trace).with(/ignoring method some_spiffy_new_metadata_field/)
+ Tempfile.open("metadata.rb") do |f|
+ f.write <<-EOF
+ some_spiffy_new_metadata_field "stuff its set to"
+ EOF
+ f.close
+ metadata.from_file(f.path)
end
end
end
+ describe "from_json" do
+ it "ignores unknown metadata fields in metdata.json files" do
+ json = %q{{ "some_spiffy_new_metadata_field": "stuff its set to" }}
+ metadata.from_json(json)
+ end
+ end
end
end
diff --git a/spec/unit/cookbook/synchronizer_spec.rb b/spec/unit/cookbook/synchronizer_spec.rb
index 97e7569cf4..1e6c9ef48c 100644
--- a/spec/unit/cookbook/synchronizer_spec.rb
+++ b/spec/unit/cookbook/synchronizer_spec.rb
@@ -50,7 +50,7 @@ describe Chef::CookbookCacheCleaner do
end
it "does not remove anything on chef-solo" do
- Chef::Config[:solo] = true
+ Chef::Config[:solo_legacy_mode] = true
allow(cleaner.cache).to receive(:find).and_return(%w{cookbooks/valid1/recipes/default.rb cookbooks/valid2/recipes/default.rb})
expect(cleaner.cache).not_to receive(:delete)
cleaner.cleanup_file_cache
@@ -62,6 +62,7 @@ describe Chef::CookbookSynchronizer do
let(:cookbook_a_default_recipe) do
{
"path" => "recipes/default.rb",
+ "name" => "recipes/default.rb",
"url" => "http://chef.example.com/abc123",
"checksum" => "abc123",
}
@@ -70,6 +71,7 @@ describe Chef::CookbookSynchronizer do
let(:cookbook_a_default_attrs) do
{
"path" => "attributes/default.rb",
+ "name" => "attributes/default.rb",
"url" => "http://chef.example.com/abc456",
"checksum" => "abc456",
}
@@ -78,6 +80,7 @@ describe Chef::CookbookSynchronizer do
let(:cookbook_a_template) do
{
"path" => "templates/default/apache2.conf.erb",
+ "name" => "templates/apache2.conf.erb",
"url" => "http://chef.example.com/ffffff",
"checksum" => "abc125",
}
@@ -86,18 +89,14 @@ describe Chef::CookbookSynchronizer do
let(:cookbook_a_file) do
{
"path" => "files/default/megaman.conf",
+ "name" => "files/megaman.conf",
"url" => "http://chef.example.com/megaman.conf",
"checksum" => "abc124",
}
end
let(:cookbook_a_manifest) do
- segments = [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates, :root_files ]
- cookbook_a_manifest = segments.inject({}) { |h, segment| h[segment.to_s] = []; h }
- cookbook_a_manifest["recipes"] = [ cookbook_a_default_recipe ]
- cookbook_a_manifest["attributes"] = [ cookbook_a_default_attrs ]
- cookbook_a_manifest["templates"] = [ cookbook_a_template ]
- cookbook_a_manifest["files"] = [ cookbook_a_file ]
+ cookbook_a_manifest = { all_files: [ cookbook_a_default_recipe, cookbook_a_default_attrs, cookbook_a_template, cookbook_a_file ] }
cookbook_a_manifest
end
@@ -117,8 +116,12 @@ describe Chef::CookbookSynchronizer do
let(:no_lazy_load) { true }
+ let(:skip_cookbook_sync) { false }
+
let(:synchronizer) do
Chef::Config[:no_lazy_load] = no_lazy_load
+ Chef::Config[:file_cache_path] = "/file-cache"
+ Chef::Config[:skip_cookbook_sync] = skip_cookbook_sync
Chef::CookbookSynchronizer.new(cookbook_manifest, events)
end
@@ -293,7 +296,7 @@ describe Chef::CookbookSynchronizer do
# Current file has fff000, want abc123
expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file).
with("/file-cache/cookbooks/cookbook_a/recipes/default.rb").
- and_return("fff000")
+ and_return("fff000").at_least(:once)
# Fetch and copy default.rb attribute file
expect(server_api).to receive(:streaming_request).
@@ -309,7 +312,7 @@ describe Chef::CookbookSynchronizer do
# Current file has fff000, want abc456
expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file).
with("/file-cache/cookbooks/cookbook_a/attributes/default.rb").
- and_return("fff000")
+ and_return("fff000").at_least(:once)
end
def setup_no_lazy_files_and_templates_chksum_mismatch_expectations
@@ -366,12 +369,12 @@ describe Chef::CookbookSynchronizer do
# Current file has abc123, want abc123
expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file).
with("/file-cache/cookbooks/cookbook_a/recipes/default.rb").
- and_return("abc123")
+ and_return("abc123").at_least(:once)
# Current file has abc456, want abc456
expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file).
with("/file-cache/cookbooks/cookbook_a/attributes/default.rb").
- and_return("abc456")
+ and_return("abc456").at_least(:once)
# :load called twice
expect(file_cache).to receive(:load).
@@ -414,6 +417,13 @@ describe Chef::CookbookSynchronizer do
and_return("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb")
end
+ describe "#server_api" do
+ it "sets keepalive to true" do
+ expect(Chef::ServerAPI).to receive(:new).with(Chef::Config[:chef_server_url], keepalives: true)
+ synchronizer.server_api
+ end
+ end
+
describe "when syncing cookbooks with the server" do
let(:server_api) { double("Chef::ServerAPI (mock)") }
@@ -516,5 +526,32 @@ describe Chef::CookbookSynchronizer do
end
end
end
+
+ context "when Chef::Config[:skip_cookbook_sync] is true" do
+ let(:skip_cookbook_sync) { true }
+
+ it "loads the cookbook files and warns the user that this isn't supported" do
+ expect(file_cache).to receive(:load).
+ with("cookbooks/cookbook_a/recipes/default.rb", false).
+ once.
+ and_return("/file-cache/cookbooks/cookbook_a/recipes/default.rb")
+ expect(file_cache).to receive(:load).
+ with("cookbooks/cookbook_a/attributes/default.rb", false).
+ once.
+ and_return("/file-cache/cookbooks/cookbook_a/attributes/default.rb")
+ expect(file_cache).to receive(:load).
+ with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false).
+ once.
+ and_return("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb")
+ expect(file_cache).to receive(:load).
+ with("cookbooks/cookbook_a/files/default/megaman.conf", false).
+ once.
+ and_return("/file-cache/cookbooks/cookbook_a/files/default/megaman.conf")
+ expect(Chef::Log).to receive(:warn).
+ with("skipping cookbook synchronization! DO NOT LEAVE THIS ENABLED IN PRODUCTION!!!").
+ once
+ synchronizer.sync_cookbooks
+ end
+ end
end
end
diff --git a/spec/unit/cookbook/syntax_check_spec.rb b/spec/unit/cookbook/syntax_check_spec.rb
index 228f695106..a24fa3ab2a 100644
--- a/spec/unit/cookbook/syntax_check_spec.rb
+++ b/spec/unit/cookbook/syntax_check_spec.rb
@@ -25,6 +25,7 @@ describe Chef::Cookbook::SyntaxCheck do
end
let(:cookbook_path) { File.join(CHEF_SPEC_DATA, "cookbooks", "openldap") }
+ let(:unsafe_cookbook_path) { 'C:\AGENT-HOME\xml-data\build-dir\76808194-76906499\artifact\cookbooks/java' }
let(:syntax_check) { Chef::Cookbook::SyntaxCheck.new(cookbook_path) }
let(:open_ldap_cookbook_files) do
@@ -53,18 +54,19 @@ describe Chef::Cookbook::SyntaxCheck do
@recipes = %w{default.rb gigantor.rb one.rb return.rb}.map { |f| File.join(cookbook_path, "recipes", f) }
@spec_files = [ File.join(cookbook_path, "spec", "spec_helper.rb") ]
@ruby_files = @attr_files + @libr_files + @defn_files + @recipes + @spec_files + [File.join(cookbook_path, "metadata.rb")]
- basenames = %w{ helpers_via_partial_test.erb
+ @basenames = %w{ helpers_via_partial_test.erb
helper_test.erb
helpers.erb
openldap_stuff.conf.erb
nested_openldap_partials.erb
nested_partial.erb
+ openldap_nested_variable_stuff.erb
openldap_variable_stuff.conf.erb
test.erb
some_windows_line_endings.erb
all_windows_line_endings.erb
no_windows_line_endings.erb }
- @template_files = basenames.map { |f| File.join(cookbook_path, "templates", "default", f) }
+ @template_files = @basenames.map { |f| File.join(cookbook_path, "templates", "default", f) }
end
after do
@@ -94,6 +96,11 @@ describe Chef::Cookbook::SyntaxCheck do
end
end
+ it "safely handles a path containing control characters" do
+ syntax_check = Chef::Cookbook::SyntaxCheck.new(unsafe_cookbook_path)
+ expect { syntax_check.remove_uninteresting_ruby_files(@basenames) }.not_to raise_error
+ end
+
describe "when first created" do
it "has the path to the cookbook to syntax check" do
expect(syntax_check.cookbook_path).to eq(cookbook_path)
diff --git a/spec/unit/cookbook_loader_spec.rb b/spec/unit/cookbook_loader_spec.rb
index db10a4eb4f..19f3160d0c 100644
--- a/spec/unit/cookbook_loader_spec.rb
+++ b/spec/unit/cookbook_loader_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,6 +31,10 @@ describe Chef::CookbookLoader do
let(:cookbook_loader) { Chef::CookbookLoader.new(repo_paths) }
+ def full_paths_for_part(cb, part)
+ cookbook_loader[cb].files_for(part).inject([]) { |memo, f| memo << f[:full_path]; memo }
+ end
+
it "checks each directory only once when loading (CHEF-3487)" do
cookbook_paths = []
repo_paths.each do |repo_path|
@@ -83,7 +87,7 @@ describe Chef::CookbookLoader do
describe "each" do
it "should allow you to iterate over cookbooks with each" do
seen = Hash.new
- cookbook_loader.each do |cookbook_name, cookbook|
+ cookbook_loader.each_key do |cookbook_name|
seen[cookbook_name] = true
end
expect(seen).to have_key("openldap")
@@ -92,16 +96,10 @@ describe Chef::CookbookLoader do
it "should iterate in alphabetical order" do
seen = Array.new
- cookbook_loader.each do |cookbook_name, cookbook|
+ cookbook_loader.each_key do |cookbook_name|
seen << cookbook_name
end
- expect(seen[0]).to eq("angrybash")
- expect(seen[1]).to eq("apache2")
- expect(seen[2]).to eq("borken")
- expect(seen[3]).to eq("ignorken")
- expect(seen[4]).to eq("java")
- expect(seen[5]).to eq("name-mismatch")
- expect(seen[6]).to eq("openldap")
+ expect(seen).to eq %w{angrybash apache2 borken ignorken java name-mismatch openldap preseed supports-platform-constraints}
end
end
@@ -112,63 +110,63 @@ describe Chef::CookbookLoader do
end
it "should allow you to override an attribute file via cookbook_path" do
- expect(cookbook_loader[:openldap].attribute_filenames.detect { |f|
+ expect(full_paths_for_part(:openldap, "attributes").detect do |f|
f =~ /cookbooks\/openldap\/attributes\/default.rb/
- }).not_to eql(nil)
- expect(cookbook_loader[:openldap].attribute_filenames.detect { |f|
+ end).not_to eql(nil)
+ expect(full_paths_for_part(:openldap, "attributes").detect do |f|
f =~ /kitchen\/openldap\/attributes\/default.rb/
- }).to eql(nil)
+ end).to eql(nil)
end
it "should load different attribute files from deeper paths" do
- expect(cookbook_loader[:openldap].attribute_filenames.detect { |f|
+ expect(full_paths_for_part(:openldap, "attributes").detect do |f|
f =~ /kitchen\/openldap\/attributes\/robinson.rb/
- }).not_to eql(nil)
+ end).not_to eql(nil)
end
it "should allow you to override a definition file via cookbook_path" do
- expect(cookbook_loader[:openldap].definition_filenames.detect { |f|
+ expect(full_paths_for_part(:openldap, "definitions").detect do |f|
f =~ /cookbooks\/openldap\/definitions\/client.rb/
- }).not_to eql(nil)
- expect(cookbook_loader[:openldap].definition_filenames.detect { |f|
+ end).not_to eql(nil)
+ expect(full_paths_for_part(:openldap, "definitions").detect do |f|
f =~ /kitchen\/openldap\/definitions\/client.rb/
- }).to eql(nil)
+ end).to eql(nil)
end
it "should load definition files from deeper paths" do
- expect(cookbook_loader[:openldap].definition_filenames.detect { |f|
+ expect(full_paths_for_part(:openldap, "definitions").detect do |f|
f =~ /kitchen\/openldap\/definitions\/drewbarrymore.rb/
- }).not_to eql(nil)
+ end).not_to eql(nil)
end
it "should allow you to override a recipe file via cookbook_path" do
- expect(cookbook_loader[:openldap].recipe_filenames.detect { |f|
+ expect(full_paths_for_part(:openldap, "recipes").detect do |f|
f =~ /cookbooks\/openldap\/recipes\/gigantor.rb/
- }).not_to eql(nil)
- expect(cookbook_loader[:openldap].recipe_filenames.detect { |f|
+ end).not_to eql(nil)
+ expect(full_paths_for_part(:openldap, "recipes").detect do |f|
f =~ /kitchen\/openldap\/recipes\/gigantor.rb/
- }).to eql(nil)
+ end).to eql(nil)
end
it "should load recipe files from deeper paths" do
- expect(cookbook_loader[:openldap].recipe_filenames.detect { |f|
+ expect(full_paths_for_part(:openldap, "recipes").detect do |f|
f =~ /kitchen\/openldap\/recipes\/woot.rb/
- }).not_to eql(nil)
+ end).not_to eql(nil)
end
it "should allow you to have an 'ignore' file, which skips loading files in later cookbooks" do
- expect(cookbook_loader[:openldap].recipe_filenames.detect { |f|
+ expect(full_paths_for_part(:openldap, "recipes").detect do |f|
f =~ /kitchen\/openldap\/recipes\/ignoreme.rb/
- }).to eql(nil)
+ end).to eql(nil)
end
it "should find files that start with a ." do
- expect(cookbook_loader[:openldap].file_filenames.detect { |f|
+ expect(full_paths_for_part(:openldap, "files").detect do |f|
f =~ /\.dotfile$/
- }).to match(/\.dotfile$/)
- expect(cookbook_loader[:openldap].file_filenames.detect { |f|
+ end).to match(/\.dotfile$/)
+ expect(full_paths_for_part(:openldap, "files").detect do |f|
f =~ /\.ssh\/id_rsa$/
- }).to match(/\.ssh\/id_rsa$/)
+ end).to match(/\.ssh\/id_rsa$/)
end
it "should load the metadata for the cookbook" do
@@ -208,7 +206,7 @@ describe Chef::CookbookLoader do
it "should have loaded the correct cookbook" do
seen = Hash.new
- cookbook_loader.each do |cookbook_name, cookbook|
+ cookbook_loader.each_key do |cookbook_name|
seen[cookbook_name] = true
end
expect(seen).to have_key("openldap")
@@ -236,7 +234,7 @@ describe Chef::CookbookLoader do
it "should not load the other cookbooks" do
seen = Hash.new
- cookbook_loader.each do |cookbook_name, cookbook|
+ cookbook_loader.each_key do |cookbook_name|
seen[cookbook_name] = true
end
expect(seen).not_to have_key("apache2")
@@ -274,7 +272,7 @@ describe Chef::CookbookLoader do
it "should load all cookbooks" do
seen = Hash.new
- cookbook_loader.each do |cookbook_name, cookbook|
+ cookbook_loader.each_key do |cookbook_name|
seen[cookbook_name] = true
end
expect(seen).to have_key("openldap")
diff --git a/spec/unit/cookbook_manifest_spec.rb b/spec/unit/cookbook_manifest_spec.rb
index acf0ade9f9..ab4b5cbfb7 100644
--- a/spec/unit/cookbook_manifest_spec.rb
+++ b/spec/unit/cookbook_manifest_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# Copyright:: Copyright 2015-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -81,11 +81,6 @@ describe Chef::CookbookManifest do
expect(cookbook_manifest.frozen_version?).to be(false)
end
- it "delegates `segment_filenames' to cookbook_version" do
- expect(cookbook_version).to receive(:segment_filenames).with(:recipes).and_return([])
- expect(cookbook_manifest.segment_filenames(:recipes)).to eq([])
- end
-
end
context "when given an empty cookbook" do
@@ -101,15 +96,7 @@ describe Chef::CookbookManifest do
"frozen?" => false,
- "recipes" => [],
- "definitions" => [],
- "libraries" => [],
- "attributes" => [],
- "files" => [],
- "templates" => [],
- "resources" => [],
- "providers" => [],
- "root_files" => [],
+ "all_files" => [],
}
end
@@ -123,30 +110,32 @@ describe Chef::CookbookManifest do
let(:cookbook_root) { File.join(CHEF_SPEC_DATA, "cb_version_cookbooks", "tatft") }
- let(:attribute_filenames) { Dir[File.join(cookbook_root, "attributes", "**", "*.rb")] }
- let(:definition_filenames) { Dir[File.join(cookbook_root, "definitions", "**", "*.rb")] }
- let(:file_filenames) { Dir[File.join(cookbook_root, "files", "**", "*.tgz")] }
- let(:recipe_filenames) { Dir[File.join(cookbook_root, "recipes", "**", "*.rb")] }
- let(:template_filenames) { Dir[File.join(cookbook_root, "templates", "**", "*.erb")] }
- let(:library_filenames) { Dir[File.join(cookbook_root, "libraries", "**", "*.rb")] }
- let(:resource_filenames) { Dir[File.join(cookbook_root, "resources", "**", "*.rb")] }
- let(:provider_filenames) { Dir[File.join(cookbook_root, "providers", "**", "*.rb")] }
- let(:root_filenames) { Array(File.join(cookbook_root, "README.rdoc")) }
- let(:metadata_filenames) { Array(File.join(cookbook_root, "metadata.json")) }
+ let(:all_files) { Dir[File.join(cookbook_root, "**", "**")].reject { |f| File.directory? f } }
let(:match_md5) { /[0-9a-f]{32}/ }
- def map_to_file_specs(paths)
+ def map_to_file_specs(paths, full: false)
paths.map do |path|
relative_path = Pathname.new(path).relative_path_from(Pathname.new(cookbook_root)).to_s
+ parts = relative_path.split("/")
+ name = if %w{templates files}.include?(parts[0]) && parts.length == 3
+ File.join(parts[0], parts[2])
+ else
+ relative_path
+ end
+
{
- "name" => File.basename(path),
+ "name" => name,
"path" => relative_path,
"checksum" => Chef::Digester.generate_md5_checksum_for_file(path),
"specificity" => "default",
- }
+ }.tap do |fp|
+ if full
+ fp["full_path"] = path
+ end
+ end
end
end
@@ -161,40 +150,34 @@ describe Chef::CookbookManifest do
"frozen?" => false,
- "recipes" => map_to_file_specs(recipe_filenames),
- "definitions" => map_to_file_specs(definition_filenames),
- "libraries" => map_to_file_specs(library_filenames),
- "attributes" => map_to_file_specs(attribute_filenames),
- "files" => map_to_file_specs(file_filenames),
- "templates" => map_to_file_specs(template_filenames),
- "resources" => map_to_file_specs(resource_filenames),
- "providers" => map_to_file_specs(provider_filenames),
- "root_files" => map_to_file_specs(root_filenames),
+ "all_files" => map_to_file_specs(all_files),
}
end
before do
- cookbook_version.attribute_filenames = attribute_filenames
- cookbook_version.definition_filenames = definition_filenames
- cookbook_version.file_filenames = file_filenames
- cookbook_version.recipe_filenames = recipe_filenames
- cookbook_version.template_filenames = template_filenames
- cookbook_version.library_filenames = library_filenames
- cookbook_version.resource_filenames = resource_filenames
- cookbook_version.provider_filenames = provider_filenames
- cookbook_version.root_filenames = root_filenames
- cookbook_version.metadata_filenames = metadata_filenames
+ cookbook_version.all_files = all_files
end
it "converts the CookbookVersion to a ruby Hash representation" do
cookbook_manifest_hash = cookbook_manifest.to_hash
expect(cookbook_manifest_hash.keys).to match_array(expected_hash.keys)
- cookbook_manifest_hash.each do |key, value|
+ cookbook_manifest_hash.each_key do |key|
expect(cookbook_manifest_hash[key]).to eq(expected_hash[key])
end
end
+ context ".each_file" do
+ it "yields all the files" do
+ files = map_to_file_specs(all_files, full: true)
+ expect(cookbook_manifest.to_enum(:each_file)).to match_array(files)
+ end
+
+ it "excludes certain file parts" do
+ files = map_to_file_specs(all_files, full: true).reject { |f| seg = f["name"].split("/")[0]; %w{ files templates }.include?(seg) }
+ expect(cookbook_manifest.to_enum(:each_file, excluded_parts: %w{ files templates })).to match_array(files)
+ end
+ end
end
describe "providing upstream URLs for save" do
diff --git a/spec/unit/cookbook_site_streaming_uploader_spec.rb b/spec/unit/cookbook_site_streaming_uploader_spec.rb
index 10963386dd..0e9c277b11 100644
--- a/spec/unit/cookbook_site_streaming_uploader_spec.rb
+++ b/spec/unit/cookbook_site_streaming_uploader_spec.rb
@@ -49,10 +49,6 @@ describe Chef::CookbookSiteStreamingUploader do
cookbook = @loader[:openldap]
files_count = Dir.glob(File.join(@cookbook_repo, cookbook.name.to_s, "**", "*"), File::FNM_DOTMATCH).count { |file| File.file?(file) }
- # The fixture cookbook contains a spec/spec_helper.rb file, which is not
- # a part of any cookbook segment, so it is not uploaded.
- files_count -= 1
-
expect(Tempfile).to receive(:new).with("chef-#{cookbook.name}-build").and_return(FakeTempfile.new("chef-#{cookbook.name}-build"))
expect(FileUtils).to receive(:mkdir_p).exactly(files_count + 1).times
expect(FileUtils).to receive(:cp).exactly(files_count).times
diff --git a/spec/unit/cookbook_spec.rb b/spec/unit/cookbook_spec.rb
index 33b4a3ccd8..ac3a1373ea 100644
--- a/spec/unit/cookbook_spec.rb
+++ b/spec/unit/cookbook_spec.rb
@@ -19,7 +19,8 @@
require "spec_helper"
describe Chef::CookbookVersion do
-# COOKBOOK_PATH = File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "cookbooks", "openldap"))
+ COOKBOOK_PATH = File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "cookbooks", "openldap"))
+
before(:each) do
@cookbook_repo = File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "cookbooks"))
cl = Chef::CookbookLoader.new(@cookbook_repo)
@@ -37,26 +38,21 @@ describe Chef::CookbookVersion do
end
it "should allow you to set the list of attribute files and create the mapping from short names to paths" do
- @cookbook.attribute_filenames = [ "attributes/one.rb", "attributes/two.rb" ]
- expect(@cookbook.attribute_filenames).to eq([ "attributes/one.rb", "attributes/two.rb" ])
- expect(@cookbook.attribute_filenames_by_short_filename.keys.sort).to eql(%w{one two})
- expect(@cookbook.attribute_filenames_by_short_filename["one"]).to eq("attributes/one.rb")
- expect(@cookbook.attribute_filenames_by_short_filename["two"]).to eq("attributes/two.rb")
+ expect(@cookbook.attribute_filenames_by_short_filename.keys.sort).to eql(%w{default smokey})
+ expect(@cookbook.attribute_filenames_by_short_filename["default"]).to eq(File.join(COOKBOOK_PATH, "attributes/default.rb"))
+ expect(@cookbook.attribute_filenames_by_short_filename["smokey"]).to eq(File.join(COOKBOOK_PATH, "attributes/smokey.rb"))
end
it "should allow you to set the list of recipe files and create the mapping of recipe short name to filename" do
- @cookbook.recipe_filenames = [ "recipes/one.rb", "recipes/two.rb" ]
- expect(@cookbook.recipe_filenames).to eq([ "recipes/one.rb", "recipes/two.rb" ])
- expect(@cookbook.recipe_filenames_by_name.keys.sort).to eql(%w{one two})
- expect(@cookbook.recipe_filenames_by_name["one"]).to eq("recipes/one.rb")
- expect(@cookbook.recipe_filenames_by_name["two"]).to eq("recipes/two.rb")
+ expect(@cookbook.recipe_filenames_by_name.keys.sort).to eql(%w{default gigantor one return})
+ expect(@cookbook.recipe_filenames_by_name["one"]).to eq(File.join(COOKBOOK_PATH, "recipes/one.rb"))
+ expect(@cookbook.recipe_filenames_by_name["gigantor"]).to eq(File.join(COOKBOOK_PATH, "recipes/gigantor.rb"))
end
it "should generate a list of recipes by fully-qualified name" do
- @cookbook.recipe_filenames = [ "recipes/one.rb", "/recipes/two.rb", "three.rb" ]
expect(@cookbook.fully_qualified_recipe_names.include?("openldap::one")).to eq(true)
- expect(@cookbook.fully_qualified_recipe_names.include?("openldap::two")).to eq(true)
- expect(@cookbook.fully_qualified_recipe_names.include?("openldap::three")).to eq(true)
+ expect(@cookbook.fully_qualified_recipe_names.include?("openldap::gigantor")).to eq(true)
+ expect(@cookbook.fully_qualified_recipe_names.include?("openldap::return")).to eq(true)
end
it "should raise an ArgumentException if you try to load a bad recipe name" do
diff --git a/spec/unit/cookbook_uploader_spec.rb b/spec/unit/cookbook_uploader_spec.rb
index c30df71e34..2c36c2c9c7 100644
--- a/spec/unit/cookbook_uploader_spec.rb
+++ b/spec/unit/cookbook_uploader_spec.rb
@@ -65,7 +65,7 @@ describe Chef::CookbookUploader do
it "creates an HTTP client with default configuration when not initialized with one" do
default_http_client = double("Chef::ServerAPI")
- expect(Chef::ServerAPI).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(default_http_client)
+ expect(Chef::ServerAPI).to receive(:new).with(Chef::Config[:chef_server_url], version_class: Chef::CookbookManifestVersions).and_return(default_http_client)
uploader = described_class.new(cookbooks_to_upload)
expect(uploader.rest).to eq(default_http_client)
end
diff --git a/spec/unit/cookbook_version_file_specificity_spec.rb b/spec/unit/cookbook_version_file_specificity_spec.rb
index df9e6571d8..ba7aaa59f5 100644
--- a/spec/unit/cookbook_version_file_specificity_spec.rb
+++ b/spec/unit/cookbook_version_file_specificity_spec.rb
@@ -23,177 +23,204 @@ describe Chef::CookbookVersion, "file specificity" do
before(:each) do
@cookbook = Chef::CookbookVersion.new("test-cookbook", "/cookbook-folder")
@cookbook.manifest = {
- "files" =>
+ "all_files" =>
[
# afile.rb
{
- :name => "afile.rb",
+ :name => "files/afile.rb",
:path => "files/host-examplehost.example.org/afile.rb",
+ :full_path => "/cookbook-folder/files/host-examplehost.example.org/afile.rb",
:checksum => "csum-host",
:specificity => "host-examplehost.example.org",
},
{
- :name => "afile.rb",
+ :name => "files/afile.rb",
:path => "files/ubuntu-9.10/afile.rb",
+ :full_path => "/cookbook-folder/files/ubuntu-9.10/afile.rb",
:checksum => "csum-platver-full",
:specificity => "ubuntu-9.10",
},
{
- :name => "afile.rb",
+ :name => "files/afile.rb",
:path => "files/newubuntu-9/afile.rb",
+ :full_path => "/cookbook-folder/files/newubuntu-9/afile.rb",
:checksum => "csum-platver-partial",
:specificity => "newubuntu-9",
},
{
- :name => "afile.rb",
+ :name => "files/afile.rb",
:path => "files/ubuntu/afile.rb",
+ :full_path => "/cookbook-folder/files/ubuntu/afile.rb",
:checksum => "csum-plat",
:specificity => "ubuntu",
},
{
- :name => "afile.rb",
+ :name => "files/afile.rb",
:path => "files/default/afile.rb",
+ :full_path => "/cookbook-folder/files/default/afile.rb",
:checksum => "csum-default",
:specificity => "default",
},
# for different/odd platform_versions
{
- :name => "bfile.rb",
+ :name => "files/bfile.rb",
:path => "files/fakeos-2.0.rc.1/bfile.rb",
+ :full_path => "/cookbook-folder/files/fakeos-2.0.rc.1/bfile.rb",
:checksum => "csum2-platver-full",
:specificity => "fakeos-2.0.rc.1",
},
{
- :name => "bfile.rb",
+ :name => "files/bfile.rb",
:path => "files/newfakeos-2.0.rc/bfile.rb",
+ :full_path => "/cookbook-folder/files/newfakeos-2.0.rc/bfile.rb",
:checksum => "csum2-platver-partial",
:specificity => "newfakeos-2.0.rc",
},
{
- :name => "bfile.rb",
+ :name => "files/bfile.rb",
:path => "files/fakeos-maple tree/bfile.rb",
+ :full_path => "/cookbook-folder/files/fakeos-maple tree/bfile.rb",
:checksum => "csum3-platver-full",
:specificity => "maple tree",
},
{
- :name => "bfile.rb",
+ :name => "files/bfile.rb",
:path => "files/fakeos-1/bfile.rb",
+ :full_path => "/cookbook-folder/files/fakeos-1/bfile.rb",
:checksum => "csum4-platver-full",
:specificity => "fakeos-1",
},
# directory adirectory
{
- :name => "anotherfile1.rb",
+ :name => "files/anotherfile1.rb",
:path => "files/host-examplehost.example.org/adirectory/anotherfile1.rb.host",
+ :full_path => "/cookbook-folder/files/host-examplehost.example.org/adirectory/anotherfile1.rb.host",
:checksum => "csum-host-1",
:specificity => "host-examplehost.example.org",
},
{
- :name => "anotherfile2.rb",
+ :name => "files/anotherfile2.rb",
:path => "files/host-examplehost.example.org/adirectory/anotherfile2.rb.host",
+ :full_path => "/cookbook-folder/files/host-examplehost.example.org/adirectory/anotherfile2.rb.host",
:checksum => "csum-host-2",
:specificity => "host-examplehost.example.org",
},
{
- :name => "anotherfile1.rb",
+ :name => "files/anotherfile1.rb",
:path => "files/ubuntu-9.10/adirectory/anotherfile1.rb.platform-full-version",
+ :full_path => "/cookbook-folder/files/ubuntu-9.10/adirectory/anotherfile1.rb.platform-full-version",
:checksum => "csum-platver-full-1",
:specificity => "ubuntu-9.10",
},
{
- :name => "anotherfile2.rb",
+ :name => "files/anotherfile2.rb",
:path => "files/ubuntu-9.10/adirectory/anotherfile2.rb.platform-full-version",
+ :full_path => "/cookbook-folder/files/ubuntu-9.10/adirectory/anotherfile2.rb.platform-full-version",
:checksum => "csum-platver-full-2",
:specificity => "ubuntu-9.10",
},
{
- :name => "anotherfile1.rb",
+ :name => "files/anotherfile1.rb",
:path => "files/newubuntu-9/adirectory/anotherfile1.rb.platform-partial-version",
+ :full_path => "/cookbook-folder/files/newubuntu-9/adirectory/anotherfile1.rb.platform-partial-version",
:checksum => "csum-platver-partial-1",
:specificity => "newubuntu-9",
},
{
- :name => "anotherfile2.rb",
+ :name => "files/anotherfile2.rb",
:path => "files/newubuntu-9/adirectory/anotherfile2.rb.platform-partial-version",
+ :full_path => "/cookbook-folder/files/newubuntu-9/adirectory/anotherfile2.rb.platform-partial-version",
:checksum => "csum-platver-partial-2",
:specificity => "nweubuntu-9",
},
{
- :name => "anotherfile1.rb",
+ :name => "files/anotherfile1.rb",
:path => "files/ubuntu/adirectory/anotherfile1.rb.platform",
+ :full_path => "/cookbook-folder/files/ubuntu/adirectory/anotherfile1.rb.platform",
:checksum => "csum-plat-1",
:specificity => "ubuntu",
},
{
- :name => "anotherfile2.rb",
+ :name => "files/anotherfile2.rb",
:path => "files/ubuntu/adirectory/anotherfile2.rb.platform",
+ :full_path => "/cookbook-folder/files/ubuntu/adirectory/anotherfile2.rb.platform",
:checksum => "csum-plat-2",
:specificity => "ubuntu",
},
{
- :name => "anotherfile1.rb",
+ :name => "files/anotherfile1.rb",
:path => "files/default/adirectory/anotherfile1.rb.default",
+ :full_path => "/cookbook-folder/files/default/adirectory/anotherfile1.rb.default",
:checksum => "csum-default-1",
:specificity => "default",
},
{
- :name => "anotherfile2.rb",
+ :name => "files/anotherfile2.rb",
:path => "files/default/adirectory/anotherfile2.rb.default",
+ :full_path => "/cookbook-folder/files/default/adirectory/anotherfile2.rb.default",
:checksum => "csum-default-2",
:specificity => "default",
},
# for different/odd platform_versions
{
- :name => "anotherfile1.rb",
+ :name => "files/anotherfile1.rb",
:path => "files/fakeos-2.0.rc.1/adirectory/anotherfile1.rb.platform-full-version",
+ :full_path => "/cookbook-folder/files/fakeos-2.0.rc.1/adirectory/anotherfile1.rb.platform-full-version",
:checksum => "csum2-platver-full-1",
:specificity => "fakeos-2.0.rc.1",
},
{
- :name => "anotherfile2.rb",
+ :name => "files/anotherfile2.rb",
:path => "files/fakeos-2.0.rc.1/adirectory/anotherfile2.rb.platform-full-version",
+ :full_path => "/cookbook-folder/files/fakeos-2.0.rc.1/adirectory/anotherfile2.rb.platform-full-version",
:checksum => "csum2-platver-full-2",
:specificity => "fakeos-2.0.rc.1",
},
{
- :name => "anotherfile1.rb",
+ :name => "files/anotherfile1.rb",
:path => "files/newfakeos-2.0.rc.1/adirectory/anotherfile1.rb.platform-partial-version",
+ :full_path => "/cookbook-folder/files/newfakeos-2.0.rc.1/adirectory/anotherfile1.rb.platform-partial-version",
:checksum => "csum2-platver-partial-1",
:specificity => "newfakeos-2.0.rc",
},
{
- :name => "anotherfile2.rb",
+ :name => "files/anotherfile2.rb",
:path => "files/newfakeos-2.0.rc.1/adirectory/anotherfile2.rb.platform-partial-version",
+ :full_path => "/cookbook-folder/files/newfakeos-2.0.rc.1/adirectory/anotherfile2.rb.platform-partial-version",
:checksum => "csum2-platver-partial-2",
:specificity => "newfakeos-2.0.rc",
},
{
- :name => "anotherfile1.rb",
+ :name => "files/anotherfile1.rb",
:path => "files/fakeos-maple tree/adirectory/anotherfile1.rb.platform-full-version",
+ :full_path => "/cookbook-folder/files/fakeos-maple tree/adirectory/anotherfile1.rb.platform-full-version",
:checksum => "csum3-platver-full-1",
:specificity => "fakeos-maple tree",
},
{
- :name => "anotherfile2.rb",
+ :name => "files/anotherfile2.rb",
:path => "files/fakeos-maple tree/adirectory/anotherfile2.rb.platform-full-version",
+ :full_path => "/cookbook-folder/files/fakeos-maple tree/adirectory/anotherfile2.rb.platform-full-version",
:checksum => "csum3-platver-full-2",
:specificity => "fakeos-maple tree",
},
{
- :name => "anotherfile1.rb",
+ :name => "files/anotherfile1.rb",
:path => "files/fakeos-1/adirectory/anotherfile1.rb.platform-full-version",
+ :full_path => "/cookbook-folder/files/fakeos-1/adirectory/anotherfile1.rb.platform-full-version",
:checksum => "csum4-platver-full-1",
:specificity => "fakeos-1",
},
{
- :name => "anotherfile2.rb",
+ :name => "files/anotherfile2.rb",
:path => "files/fakeos-1/adirectory/anotherfile2.rb.platform-full-version",
+ :full_path => "/cookbook-folder/files/fakeos-1/adirectory/anotherfile2.rb.platform-full-version",
:checksum => "csum4-platver-full-2",
:specificity => "fakeos-1",
},
@@ -304,24 +331,24 @@ describe Chef::CookbookVersion, "file specificity" do
it "should raise a FileNotFound exception without match" do
node = Chef::Node.new
- expect {
+ expect do
@cookbook.preferred_manifest_record(node, :files, "doesn't_exist.rb")
- }.to raise_error(Chef::Exceptions::FileNotFound)
+ end.to raise_error(Chef::Exceptions::FileNotFound)
end
it "should raise a FileNotFound exception consistently without match" do
node = Chef::Node.new
- expect {
+ expect do
@cookbook.preferred_manifest_record(node, :files, "doesn't_exist.rb")
- }.to raise_error(Chef::Exceptions::FileNotFound)
+ end.to raise_error(Chef::Exceptions::FileNotFound)
- expect {
+ expect do
@cookbook.preferred_manifest_record(node, :files, "doesn't_exist.rb")
- }.to raise_error(Chef::Exceptions::FileNotFound)
+ end.to raise_error(Chef::Exceptions::FileNotFound)
- expect {
+ expect do
@cookbook.preferred_manifest_record(node, :files, "doesn't_exist.rb")
- }.to raise_error(Chef::Exceptions::FileNotFound)
+ end.to raise_error(Chef::Exceptions::FileNotFound)
end
describe "when fetching the contents of a directory by file specificity" do
diff --git a/spec/unit/cookbook_version_spec.rb b/spec/unit/cookbook_version_spec.rb
index 7def2f5749..83fb3f578f 100644
--- a/spec/unit/cookbook_version_spec.rb
+++ b/spec/unit/cookbook_version_spec.rb
@@ -25,38 +25,6 @@ describe Chef::CookbookVersion do
expect(cookbook_version.name).to eq("tatft")
end
- it "has no attribute files" do
- expect(cookbook_version.attribute_filenames).to be_empty
- end
-
- it "has no resource definition files" do
- expect(cookbook_version.definition_filenames).to be_empty
- end
-
- it "has no cookbook files" do
- expect(cookbook_version.file_filenames).to be_empty
- end
-
- it "has no recipe files" do
- expect(cookbook_version.recipe_filenames).to be_empty
- end
-
- it "has no library files" do
- expect(cookbook_version.library_filenames).to be_empty
- end
-
- it "has no LWRP resource files" do
- expect(cookbook_version.resource_filenames).to be_empty
- end
-
- it "has no LWRP provider files" do
- expect(cookbook_version.provider_filenames).to be_empty
- end
-
- it "has no metadata files" do
- expect(cookbook_version.metadata_filenames).to be_empty
- end
-
it "has an empty set of all_files" do
expect(cookbook_version.all_files).to be_empty
end
@@ -82,17 +50,7 @@ describe Chef::CookbookVersion do
let(:cookbook_paths_by_type) do
{
# Dunno if the paths here are representitive of what is set by CookbookLoader...
- all_files: Dir[File.join(cookbook_root, "**", "*.rb")],
- attribute_filenames: Dir[File.join(cookbook_root, "attributes", "**", "*.rb")],
- definition_filenames: Dir[File.join(cookbook_root, "definitions", "**", "*.rb")],
- file_filenames: Dir[File.join(cookbook_root, "files", "**", "*.tgz")],
- recipe_filenames: Dir[File.join(cookbook_root, "recipes", "**", "*.rb")],
- template_filenames: Dir[File.join(cookbook_root, "templates", "**", "*.erb")],
- library_filenames: Dir[File.join(cookbook_root, "libraries", "**", "*.rb")],
- resource_filenames: Dir[File.join(cookbook_root, "resources", "**", "*.rb")],
- provider_filenames: Dir[File.join(cookbook_root, "providers", "**", "*.rb")],
- root_filenames: Array(File.join(cookbook_root, "README.rdoc")),
- metadata_filenames: Array(File.join(cookbook_root, "metadata.json")),
+ all_files: Dir[File.join(cookbook_root, "**", "**")],
}
end
@@ -102,26 +60,17 @@ describe Chef::CookbookVersion do
let(:cookbook_version) do
Chef::CookbookVersion.new("tatft", cookbook_root).tap do |c|
- # Currently the cookbook loader finds all the files then tells CookbookVersion
- # where they are.
- c.attribute_filenames = cookbook_paths_by_type[:attribute_filenames]
- c.definition_filenames = cookbook_paths_by_type[:definition_filenames]
- c.recipe_filenames = cookbook_paths_by_type[:recipe_filenames]
- c.template_filenames = cookbook_paths_by_type[:template_filenames]
- c.file_filenames = cookbook_paths_by_type[:file_filenames]
- c.library_filenames = cookbook_paths_by_type[:library_filenames]
- c.resource_filenames = cookbook_paths_by_type[:resource_filenames]
- c.provider_filenames = cookbook_paths_by_type[:provider_filenames]
- c.root_filenames = cookbook_paths_by_type[:root_filenames]
- c.metadata_filenames = cookbook_paths_by_type[:metadata_filenames]
+ # Currently the cookbook loader finds all the files then tells CookbookVersion
+ # where they are.
+ c.all_files = cookbook_paths_by_type[:all_files]
end
end
# Used to test file-specificity related file lookups
let(:node) do
Chef::Node.new.tap do |n|
- n.set[:platform] = "ubuntu"
- n.set[:platform_version] = "13.04"
+ n.normal[:platform] = "ubuntu"
+ n.normal[:platform_version] = "13.04"
n.name("testing")
end
end
@@ -168,18 +117,7 @@ describe Chef::CookbookVersion do
let(:cookbook_paths_by_type) do
{
- # Dunno if the paths here are representitive of what is set by CookbookLoader...
- all_files: Dir[File.join(cookbook_root, "**", "*.rb")],
- attribute_filenames: Dir[File.join(cookbook_root, "attributes", "**", "*.rb")],
- definition_filenames: Dir[File.join(cookbook_root, "definitions", "**", "*.rb")],
- file_filenames: Dir[File.join(cookbook_root, "files", "**", "*.*")],
- recipe_filenames: Dir[File.join(cookbook_root, "recipes", "**", "*.rb")],
- template_filenames: Dir[File.join(cookbook_root, "templates", "**", "*.*")],
- library_filenames: Dir[File.join(cookbook_root, "libraries", "**", "*.rb")],
- resource_filenames: Dir[File.join(cookbook_root, "resources", "**", "*.rb")],
- provider_filenames: Dir[File.join(cookbook_root, "providers", "**", "*.rb")],
- root_filenames: Array(File.join(cookbook_root, "README.rdoc")),
- metadata_filenames: Array(File.join(cookbook_root, "metadata.json")),
+ all_files: Dir[File.join(cookbook_root, "**", "**")],
}
end
@@ -187,24 +125,15 @@ describe Chef::CookbookVersion do
let(:cookbook_version) do
Chef::CookbookVersion.new("cookbook2", cookbook_root).tap do |c|
- c.attribute_filenames = cookbook_paths_by_type[:attribute_filenames]
- c.definition_filenames = cookbook_paths_by_type[:definition_filenames]
- c.recipe_filenames = cookbook_paths_by_type[:recipe_filenames]
- c.template_filenames = cookbook_paths_by_type[:template_filenames]
- c.file_filenames = cookbook_paths_by_type[:file_filenames]
- c.library_filenames = cookbook_paths_by_type[:library_filenames]
- c.resource_filenames = cookbook_paths_by_type[:resource_filenames]
- c.provider_filenames = cookbook_paths_by_type[:provider_filenames]
- c.root_filenames = cookbook_paths_by_type[:root_filenames]
- c.metadata_filenames = cookbook_paths_by_type[:metadata_filenames]
+ c.all_files = cookbook_paths_by_type[:all_files]
end
end
# Used to test file-specificity related file lookups
let(:node) do
Chef::Node.new.tap do |n|
- n.set[:platform] = "ubuntu"
- n.set[:platform_version] = "13.04"
+ n.normal[:platform] = "ubuntu"
+ n.normal[:platform_version] = "13.04"
n.name("testing")
end
end
@@ -255,19 +184,19 @@ describe Chef::CookbookVersion do
it "should sort based on the version number" do
examples = [
- # smaller, larger
- ["1.0", "2.0"],
- ["1.2.3", "1.2.4"],
- ["1.2.3", "1.3.0"],
- ["1.2.3", "1.3"],
- ["1.2.3", "2.1.1"],
- ["1.2.3", "2.1"],
- ["1.2", "1.2.4"],
- ["1.2", "1.3.0"],
- ["1.2", "1.3"],
- ["1.2", "2.1.1"],
- ["1.2", "2.1"],
- ]
+ # smaller, larger
+ ["1.0", "2.0"],
+ ["1.2.3", "1.2.4"],
+ ["1.2.3", "1.3.0"],
+ ["1.2.3", "1.3"],
+ ["1.2.3", "2.1.1"],
+ ["1.2.3", "2.1"],
+ ["1.2", "1.2.4"],
+ ["1.2", "1.3.0"],
+ ["1.2", "1.3"],
+ ["1.2", "2.1.1"],
+ ["1.2", "2.1"],
+ ]
examples.each do |smaller, larger|
sm = Chef::CookbookVersion.new("foo", "/tmp/blah")
lg = Chef::CookbookVersion.new("foo", "/tmp/blah")
@@ -318,42 +247,4 @@ describe Chef::CookbookVersion do
end
- describe "when deprecation warnings are errors" do
-
- subject(:cbv) { Chef::CookbookVersion.new("version validation", "/tmp/blah") }
-
- it "errors on #status and #status=" do
- expect { cbv.status = :wat }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
- expect { cbv.status }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
- end
-
- end
-
- describe "deprecated features" do
-
- subject(:cbv) { Chef::CookbookVersion.new("tatft", "/tmp/blah").tap { |c| c.version = "1.2.3" } }
-
- before do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- end
-
- it "gives a save URL for the standard cookbook API" do
- expect(cbv.save_url).to eq("cookbooks/tatft/1.2.3")
- end
-
- it "gives a force save URL for the standard cookbook API" do
- expect(cbv.force_save_url).to eq("cookbooks/tatft/1.2.3?force=true")
- end
-
- it "is \"ready\"" do
- # WTF is this? what are the valid states? and why aren't they set with encapsulating methods?
- # [Dan 15-Jul-2010]
- expect(cbv.status).to eq(:ready)
- end
-
- include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
- let(:jsonable) { Chef::CookbookVersion.new("tatft", "/tmp/blah") }
- end
-
- end
end
diff --git a/spec/unit/daemon_spec.rb b/spec/unit/daemon_spec.rb
index e31ce7d151..02736a1daf 100644
--- a/spec/unit/daemon_spec.rb
+++ b/spec/unit/daemon_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@junglist.gen.nz>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,10 +19,13 @@ require "spec_helper"
require "ostruct"
describe Chef::Daemon do
+ let(:testuser) { "thisisausernamewhichshouldnotexist" }
+ let(:testgroup) { "thisisagroupnamewhichshouldnotexist" }
+
before do
if windows?
mock_struct = #Struct::Passwd.new(nil, nil, 111, 111)
- mock_struct = OpenStruct.new(:uid => 2342, :gid => 2342)
+ mock_struct = OpenStruct.new(:uid => 2342, :gid => 2342)
allow(Etc).to receive(:getpwnam).and_return mock_struct
allow(Etc).to receive(:getgrnam).and_return mock_struct
# mock unimplemented methods
@@ -73,8 +76,9 @@ describe Chef::Daemon do
describe ".change_privilege" do
before do
+ allow(Chef::Daemon).to receive(:_change_privilege)
allow(Chef::Application).to receive(:fatal!).and_return(true)
- Chef::Config[:user] = "aj"
+ Chef::Config[:user] = testuser
allow(Dir).to receive(:chdir)
end
@@ -86,28 +90,28 @@ describe Chef::Daemon do
describe "when the user and group options are supplied" do
before do
- Chef::Config[:group] = "staff"
+ Chef::Config[:group] = testgroup
end
it "should log an appropriate info message" do
- expect(Chef::Log).to receive(:info).with("About to change privilege to aj:staff")
+ expect(Chef::Log).to receive(:info).with("About to change privilege to #{testuser}:#{testgroup}")
Chef::Daemon.change_privilege
end
it "should call _change_privilege with the user and group" do
- expect(Chef::Daemon).to receive(:_change_privilege).with("aj", "staff")
+ expect(Chef::Daemon).to receive(:_change_privilege).with(testuser, testgroup)
Chef::Daemon.change_privilege
end
end
describe "when just the user option is supplied" do
it "should log an appropriate info message" do
- expect(Chef::Log).to receive(:info).with("About to change privilege to aj")
+ expect(Chef::Log).to receive(:info).with("About to change privilege to #{testuser}")
Chef::Daemon.change_privilege
end
it "should call _change_privilege with just the user" do
- expect(Chef::Daemon).to receive(:_change_privilege).with("aj")
+ expect(Chef::Daemon).to receive(:_change_privilege).with(testuser)
Chef::Daemon.change_privilege
end
end
@@ -138,18 +142,18 @@ describe Chef::Daemon do
end
it "should initialize the supplemental group list" do
- expect(Process).to receive(:initgroups).with("aj", 20)
- Chef::Daemon._change_privilege("aj")
+ expect(Process).to receive(:initgroups).with(testuser, 20)
+ Chef::Daemon._change_privilege(testuser)
end
it "should attempt to change the process GID" do
expect(Process::GID).to receive(:change_privilege).with(20).and_return(20)
- Chef::Daemon._change_privilege("aj")
+ Chef::Daemon._change_privilege(testuser)
end
it "should attempt to change the process UID" do
expect(Process::UID).to receive(:change_privilege).with(501).and_return(501)
- Chef::Daemon._change_privilege("aj")
+ Chef::Daemon._change_privilege(testuser)
end
end
@@ -159,6 +163,11 @@ describe Chef::Daemon do
allow(Process).to receive(:egid).and_return(999)
end
+ after do
+ allow(Process).to receive(:euid).and_call_original
+ allow(Process).to receive(:egid).and_call_original
+ end
+
it "should log an appropriate error message and fail miserably" do
allow(Process).to receive(:initgroups).and_raise(Errno::EPERM)
error = "Operation not permitted"
@@ -166,7 +175,7 @@ describe Chef::Daemon do
error = "Not owner"
end
expect(Chef::Application).to receive(:fatal!).with("Permission denied when trying to change 999:999 to 501:20. #{error}")
- Chef::Daemon._change_privilege("aj")
+ Chef::Daemon._change_privilege(testuser)
end
end
diff --git a/spec/unit/data_bag_item_spec.rb b/spec/unit/data_bag_item_spec.rb
index 99bf89e583..7094a7b1f7 100644
--- a/spec/unit/data_bag_item_spec.rb
+++ b/spec/unit/data_bag_item_spec.rb
@@ -52,6 +52,10 @@ describe Chef::DataBagItem do
expect { data_bag_item.raw_data = { "id" => "octahedron" } }.not_to raise_error
end
+ it "should let you set the raw_data with a hash containing symbols" do
+ expect { data_bag_item.raw_data = { :id => "octahedron" } }.not_to raise_error
+ end
+
it "should let you set the raw_data from a mash" do
expect { data_bag_item.raw_data = Mash.new({ "id" => "octahedron" }) }.not_to raise_error
end
@@ -91,12 +95,12 @@ describe Chef::DataBagItem do
end
describe "object_name" do
- let(:data_bag_item) {
+ let(:data_bag_item) do
data_bag_item = Chef::DataBagItem.new
data_bag_item.data_bag("dreams")
data_bag_item.raw_data = { "id" => "the_beatdown" }
data_bag_item
- }
+ end
it "should return an object name based on the bag name and the raw_data id" do
expect(data_bag_item.object_name).to eq("data_bag_item_dreams_the_beatdown")
@@ -110,12 +114,12 @@ describe Chef::DataBagItem do
end
describe "class method name" do
- let(:data_bag_item) {
+ let(:data_bag_item) do
data_bag_item = Chef::DataBagItem.new
data_bag_item.data_bag("dreams")
data_bag_item.raw_data = { "id" => "the_beatdown", "name" => "Bruce" }
data_bag_item
- }
+ end
it "should return the object name" do
expect(data_bag_item.name).to eq(data_bag_item.object_name)
@@ -128,11 +132,11 @@ describe Chef::DataBagItem do
end
describe "when used like a Hash" do
- let(:data_bag_item) {
+ let(:data_bag_item) do
data_bag_item = Chef::DataBagItem.new
data_bag_item.raw_data = { "id" => "journey", "trials" => "been through" }
data_bag_item
- }
+ end
it "responds to keys" do
expect(data_bag_item.keys).to include("id")
@@ -158,9 +162,9 @@ describe Chef::DataBagItem do
describe "from_hash" do
context "when hash contains raw_data" do
- let(:data_bag_item) {
+ let(:data_bag_item) do
Chef::DataBagItem.from_hash({ "raw_data" => { "id" => "whoa", "name" => "Bruce", "i_know" => "kung_fu" } })
- }
+ end
it "should have the id key set" do
expect(data_bag_item["id"]).to eq("whoa")
@@ -172,9 +176,9 @@ describe Chef::DataBagItem do
end
context "when hash does not contain raw_data" do
- let(:data_bag_item) {
+ let(:data_bag_item) do
Chef::DataBagItem.from_hash({ "id" => "whoa", "name" => "Bruce", "i_know" => "kung_fu" })
- }
+ end
it "should have the id key set" do
expect(data_bag_item["id"]).to eq("whoa")
@@ -187,12 +191,12 @@ describe Chef::DataBagItem do
end
describe "to_hash" do
- let(:data_bag_item) {
+ let(:data_bag_item) do
data_bag_item = Chef::DataBagItem.new
data_bag_item.data_bag("still_lost")
data_bag_item.raw_data = { "id" => "whoa", "name" => "Bruce", "i_know" => "kung_fu" }
data_bag_item
- }
+ end
let!(:original_data_bag_keys) { data_bag_item.keys }
@@ -223,12 +227,12 @@ describe Chef::DataBagItem do
end
describe "when deserializing from JSON" do
- let(:data_bag_item) {
+ let(:data_bag_item) do
data_bag_item = Chef::DataBagItem.new
data_bag_item.data_bag("mars_volta")
data_bag_item.raw_data = { "id" => "octahedron", "name" => "Bruce", "snooze" => { "finally" => :world_will } }
data_bag_item
- }
+ end
let(:deserial) { Chef::DataBagItem.from_hash(Chef::JSONCompat.parse(Chef::JSONCompat.to_json(data_bag_item))) }
@@ -275,13 +279,13 @@ describe Chef::DataBagItem do
describe "save" do
let(:server) { instance_double(Chef::ServerAPI) }
- let(:data_bag_item) {
+ let(:data_bag_item) do
data_bag_item = Chef::DataBagItem.new
data_bag_item["id"] = "heart of darkness"
data_bag_item.raw_data = { "id" => "heart_of_darkness", "author" => "Conrad" }
data_bag_item.data_bag("books")
data_bag_item
- }
+ end
before do
expect(Chef::ServerAPI).to receive(:new).and_return(server)
@@ -320,12 +324,12 @@ describe Chef::DataBagItem do
describe "destroy" do
let(:server) { instance_double(Chef::ServerAPI) }
- let(:data_bag_item) {
+ let(:data_bag_item) do
data_bag_item = Chef::DataBagItem.new
data_bag_item.data_bag("a_baggy_bag")
data_bag_item.raw_data = { "id" => "some_id" }
data_bag_item
- }
+ end
it "should set default parameters" do
expect(Chef::ServerAPI).to receive(:new).and_return(server)
@@ -365,11 +369,11 @@ describe Chef::DataBagItem do
describe "in solo mode" do
before do
- Chef::Config[:solo] = true
+ Chef::Config[:solo_legacy_mode] = true
end
after do
- Chef::Config[:solo] = false
+ Chef::Config[:solo_legacy_mode] = false
end
it "converts the raw data to a data bag item" do
diff --git a/spec/unit/data_bag_spec.rb b/spec/unit/data_bag_spec.rb
index 92da4cade1..cadd60936e 100644
--- a/spec/unit/data_bag_spec.rb
+++ b/spec/unit/data_bag_spec.rb
@@ -143,13 +143,13 @@ describe Chef::DataBag do
shared_examples_for "data bag in solo mode" do |data_bag_path|
before do
- Chef::Config[:solo] = true
+ Chef::Config[:solo_legacy_mode] = true
Chef::Config[:data_bag_path] = data_bag_path
@paths = Array(data_bag_path)
end
after do
- Chef::Config[:solo] = false
+ Chef::Config[:solo_legacy_mode] = false
end
it "should get the data bag from the data_bag_path" do
@@ -241,9 +241,9 @@ describe Chef::DataBag do
it "should raise an error if the configured data_bag_path is invalid" do
file_dir_stub(@paths.first, false)
- expect {
+ expect do
Chef::DataBag.load("foo")
- }.to raise_error Chef::Exceptions::InvalidDataBagPath, "Data bag path '/var/chef/data_bags' is invalid"
+ end.to raise_error Chef::Exceptions::InvalidDataBagPath, "Data bag path '/var/chef/data_bags' is invalid"
end
end
diff --git a/spec/unit/data_collector/messages/helpers_spec.rb b/spec/unit/data_collector/messages/helpers_spec.rb
new file mode 100644
index 0000000000..a2c6753003
--- /dev/null
+++ b/spec/unit/data_collector/messages/helpers_spec.rb
@@ -0,0 +1,202 @@
+#
+# Author:: Adam Leff (<adamleff@chef.io)
+#
+# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/data_collector/messages/helpers"
+
+class TestMessage
+ extend Chef::DataCollector::Messages::Helpers
+end
+
+describe Chef::DataCollector::Messages::Helpers do
+ describe "#organization" do
+ context "when the run is a solo run" do
+ it "returns the data collector organization" do
+ allow(TestMessage).to receive(:solo_run?).and_return(true)
+ expect(TestMessage).to receive(:data_collector_organization).and_return("org1")
+ expect(TestMessage.organization).to eq("org1")
+ end
+ end
+
+ context "when the run is not a solo run" do
+ it "returns the data collector organization" do
+ allow(TestMessage).to receive(:solo_run?).and_return(false)
+ expect(TestMessage).to receive(:chef_server_organization).and_return("org2")
+ expect(TestMessage.organization).to eq("org2")
+ end
+ end
+ end
+
+ describe "#data_collector_organization" do
+ context "when the org is specified in the config" do
+ it "returns the org from the config" do
+ Chef::Config[:data_collector][:organization] = "org1"
+ expect(TestMessage.data_collector_organization).to eq("org1")
+ end
+ end
+
+ context "when the org is not specified in the config" do
+ it "returns the default chef_solo org" do
+ expect(TestMessage.data_collector_organization).to eq("chef_solo")
+ end
+ end
+ end
+
+ describe "#chef_server_organization" do
+ context "when the URL is properly formatted" do
+ it "returns the org from the parsed URL" do
+ Chef::Config[:chef_server_url] = "http://mycompany.com/organizations/myorg"
+ expect(TestMessage.chef_server_organization).to eq("myorg")
+ end
+ end
+
+ context "when the URL is not properly formatted" do
+ it "returns unknown_organization" do
+ Chef::Config[:chef_server_url] = "http://mycompany.com/what/url/is/this"
+ expect(TestMessage.chef_server_organization).to eq("unknown_organization")
+ end
+ end
+
+ context "when the organization in the URL contains hyphens" do
+ it "returns the full org name" do
+ Chef::Config[:chef_server_url] = "http://mycompany.com/organizations/myorg-test"
+ expect(TestMessage.chef_server_organization).to eq("myorg-test")
+ end
+ end
+ end
+
+ describe "#collector_source" do
+ context "when the run is a solo run" do
+ it "returns chef_solo" do
+ allow(TestMessage).to receive(:solo_run?).and_return(true)
+ expect(TestMessage.collector_source).to eq("chef_solo")
+ end
+ end
+
+ context "when the run is not a solo run" do
+ it "returns chef_client" do
+ allow(TestMessage).to receive(:solo_run?).and_return(false)
+ expect(TestMessage.collector_source).to eq("chef_client")
+ end
+ end
+ end
+
+ describe "#solo_run?" do
+ context "when :solo is set in Chef::Config" do
+ it "returns true" do
+ Chef::Config[:solo] = true
+ Chef::Config[:local_mode] = nil
+ expect(TestMessage.solo_run?).to be_truthy
+ end
+ end
+
+ context "when :local_mode is set in Chef::Config" do
+ it "returns true" do
+ Chef::Config[:solo] = nil
+ Chef::Config[:local_mode] = true
+ expect(TestMessage.solo_run?).to be_truthy
+ end
+ end
+
+ context "when neither :solo or :local_mode is set in Chef::Config" do
+ it "returns false" do
+ Chef::Config[:solo] = nil
+ Chef::Config[:local_mode] = nil
+ expect(TestMessage.solo_run?).to be_falsey
+ end
+ end
+ end
+
+ describe "#node_uuid" do
+ context "when the node UUID is available in Chef::Config" do
+ it "returns the configured value" do
+ Chef::Config[:chef_guid] = "configured_uuid"
+ expect(TestMessage.node_uuid).to eq("configured_uuid")
+ end
+ end
+
+ context "when the node UUID can be read" do
+ it "returns the read-in node UUID" do
+ Chef::Config[:chef_guid] = nil
+ allow(TestMessage).to receive(:read_node_uuid).and_return("read_uuid")
+ expect(TestMessage.node_uuid).to eq("read_uuid")
+ end
+ end
+
+ context "when the node UUID cannot be read" do
+ it "generated a new node UUID" do
+ Chef::Config[:chef_guid] = nil
+ allow(TestMessage).to receive(:read_node_uuid).and_return(nil)
+ allow(TestMessage).to receive(:generate_node_uuid).and_return("generated_uuid")
+ expect(TestMessage.node_uuid).to eq("generated_uuid")
+ end
+ end
+ end
+
+ describe "#generate_node_uuid" do
+ it "generates a new UUID, stores it, and returns it" do
+ expect(SecureRandom).to receive(:uuid).and_return("generated_uuid")
+ expect(TestMessage).to receive(:update_metadata).with("node_uuid", "generated_uuid")
+ expect(TestMessage.generate_node_uuid).to eq("generated_uuid")
+ end
+ end
+
+ describe "#read_node_uuid" do
+ it "reads the node UUID from metadata" do
+ expect(TestMessage).to receive(:metadata).and_return({ "node_uuid" => "read_uuid" })
+ expect(TestMessage.read_node_uuid).to eq("read_uuid")
+ end
+ end
+
+ describe "metadata" do
+ let(:metadata_filename) { "fake_metadata_file.json" }
+
+ before do
+ allow(TestMessage).to receive(:metadata_filename).and_return(metadata_filename)
+ end
+
+ context "when the metadata file exists" do
+ it "returns the contents of the metadata file" do
+ expect(Chef::FileCache).to receive(:load).with(metadata_filename).and_return('{"foo":"bar"}')
+ expect(TestMessage.metadata["foo"]).to eq("bar")
+ end
+ end
+
+ context "when the metadata file does not exist" do
+ it "returns an empty hash" do
+ expect(Chef::FileCache).to receive(:load).with(metadata_filename).and_raise(Chef::Exceptions::FileNotFound)
+ expect(TestMessage.metadata).to eq({})
+ end
+ end
+ end
+
+ describe "#update_metadata" do
+ it "updates the file" do
+ allow(TestMessage).to receive(:metadata_filename).and_return("fake_metadata_file.json")
+ allow(TestMessage).to receive(:metadata).and_return({ "key" => "current_value" })
+ expect(Chef::FileCache).to receive(:store).with(
+ "fake_metadata_file.json",
+ '{"key":"updated_value"}',
+ 0644
+ )
+
+ TestMessage.update_metadata("key", "updated_value")
+ end
+ end
+end
diff --git a/spec/unit/data_collector/messages_spec.rb b/spec/unit/data_collector/messages_spec.rb
new file mode 100644
index 0000000000..f5df85a988
--- /dev/null
+++ b/spec/unit/data_collector/messages_spec.rb
@@ -0,0 +1,329 @@
+#
+# Author:: Adam Leff (<adamleff@chef.io)
+#
+# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "ffi_yajl"
+require "chef/data_collector/messages/helpers"
+
+describe Chef::DataCollector::Messages do
+ describe "#run_start_message" do
+ let(:run_status) { Chef::RunStatus.new(Chef::Node.new, Chef::EventDispatch::Dispatcher.new) }
+ let(:required_fields) do
+ %w{
+ chef_server_fqdn
+ entity_uuid
+ id
+ message_version
+ message_type
+ node_name
+ organization_name
+ run_id
+ source
+ start_time
+ }
+ end
+ let(:optional_fields) { [] }
+
+ before do
+ allow(run_status).to receive(:start_time).and_return(Time.now)
+ end
+
+ it "is not missing any required fields" do
+ missing_fields = required_fields.select do |key|
+ !Chef::DataCollector::Messages.run_start_message(run_status).key?(key)
+ end
+
+ expect(missing_fields).to eq([])
+ end
+
+ it "does not have any extra fields" do
+ extra_fields = Chef::DataCollector::Messages.run_start_message(run_status).keys.select do |key|
+ !required_fields.include?(key) && !optional_fields.include?(key)
+ end
+
+ expect(extra_fields).to eq([])
+ end
+ end
+
+ describe "#run_end_message" do
+ let(:node) { Chef::Node.new }
+ let(:run_status) { Chef::RunStatus.new(node, Chef::EventDispatch::Dispatcher.new) }
+ let(:report1) { double("report1", report_data: { "status" => "updated" }) }
+ let(:report2) { double("report2", report_data: { "status" => "skipped" }) }
+ let(:reporter_data) do
+ {
+ run_status: run_status,
+ resources: [report1, report2],
+ }
+ end
+
+ before do
+ allow(run_status).to receive(:start_time).and_return(Time.now)
+ allow(run_status).to receive(:end_time).and_return(Time.now)
+ end
+
+ it "includes a valid node object in the payload" do
+ message = Chef::DataCollector::Messages.run_end_message(reporter_data)
+ expect(message["node"]).to be_an_instance_of(Chef::Node)
+ end
+
+ it "returns a sane JSON representation of the node object" do
+ node.chef_environment = "my_test_environment"
+ node.run_list.add("recipe[my_test_cookbook::default]")
+ message = FFI_Yajl::Parser.parse(Chef::DataCollector::Messages.run_end_message(reporter_data).to_json)
+
+ expect(message["node"]["chef_environment"]).to eq("my_test_environment")
+ expect(message["node"]["run_list"]).to eq(["recipe[my_test_cookbook::default]"])
+ end
+
+ context "when the run was successful" do
+ let(:required_fields) do
+ %w{
+ chef_server_fqdn
+ entity_uuid
+ id
+ end_time
+ expanded_run_list
+ message_type
+ message_version
+ node
+ node_name
+ organization_name
+ resources
+ run_id
+ run_list
+ source
+ start_time
+ status
+ total_resource_count
+ updated_resource_count
+ deprecations
+ }
+ end
+ let(:optional_fields) { %w{error policy_group policy_name} }
+
+ before do
+ allow(run_status).to receive(:exception).and_return(nil)
+ end
+
+ it "is not missing any required fields" do
+ missing_fields = required_fields.select do |key|
+ !Chef::DataCollector::Messages.run_end_message(reporter_data).key?(key)
+ end
+ expect(missing_fields).to eq([])
+ end
+
+ it "does not have any extra fields" do
+ extra_fields = Chef::DataCollector::Messages.run_end_message(reporter_data).keys.select do |key|
+ !required_fields.include?(key) && !optional_fields.include?(key)
+ end
+ expect(extra_fields).to eq([])
+ end
+
+ it "only includes updated resources in its count" do
+ message = Chef::DataCollector::Messages.run_end_message(reporter_data)
+ expect(message["total_resource_count"]).to eq(2)
+ expect(message["updated_resource_count"]).to eq(1)
+ end
+ end
+
+ context "when the run was not successful" do
+ let(:required_fields) do
+ %w{
+ chef_server_fqdn
+ entity_uuid
+ id
+ end_time
+ error
+ expanded_run_list
+ message_type
+ message_version
+ node
+ node_name
+ organization_name
+ resources
+ run_id
+ run_list
+ source
+ start_time
+ status
+ total_resource_count
+ updated_resource_count
+ deprecations
+ }
+ end
+ let(:optional_fields) { %w{policy_group policy_name} }
+
+ before do
+ allow(run_status).to receive(:exception).and_return(RuntimeError.new("an error happened"))
+ end
+
+ it "is not missing any required fields" do
+ missing_fields = required_fields.select do |key|
+ !Chef::DataCollector::Messages.run_end_message(reporter_data).key?(key)
+ end
+ expect(missing_fields).to eq([])
+ end
+
+ it "does not have any extra fields" do
+ extra_fields = Chef::DataCollector::Messages.run_end_message(reporter_data).keys.select do |key|
+ !required_fields.include?(key) && !optional_fields.include?(key)
+ end
+ expect(extra_fields).to eq([])
+ end
+ end
+ end
+
+ describe "#run_end_message in policy mode" do
+ let(:node) { Chef::Node.new }
+ let(:run_status) { Chef::RunStatus.new(node, Chef::EventDispatch::Dispatcher.new) }
+ let(:report1) { double("report1", report_data: { "status" => "updated" }) }
+ let(:report2) { double("report2", report_data: { "status" => "skipped" }) }
+ let(:reporter_data) do
+ {
+ run_status: run_status,
+ resources: [report1, report2],
+ }
+ end
+
+ before do
+ allow(run_status).to receive(:start_time).and_return(Time.now)
+ allow(run_status).to receive(:end_time).and_return(Time.now)
+ node.policy_group = "test"
+ node.policy_name = "policy-test"
+ end
+
+ it "includes a valid node object in the payload" do
+ message = Chef::DataCollector::Messages.run_end_message(reporter_data)
+ expect(message["node"]).to be_an_instance_of(Chef::Node)
+ end
+
+ it "returns a sane JSON representation of the node object" do
+ node.chef_environment = "my_test_environment"
+ node.run_list.add("recipe[my_test_cookbook::default]")
+ message = FFI_Yajl::Parser.parse(Chef::DataCollector::Messages.run_end_message(reporter_data).to_json)
+
+ expect(message["node"]["chef_environment"]).to eq("my_test_environment")
+ expect(message["node"]["run_list"]).to eq(["recipe[my_test_cookbook::default]"])
+ expect(message["node"]["policy_name"]).to eq("policy-test")
+ expect(message["node"]["policy_group"]).to eq("test")
+ end
+
+ context "when the run was successful" do
+ let(:required_fields) do
+ %w{
+ chef_server_fqdn
+ entity_uuid
+ id
+ end_time
+ expanded_run_list
+ message_type
+ message_version
+ node
+ node_name
+ organization_name
+ resources
+ run_id
+ run_list
+ source
+ start_time
+ status
+ total_resource_count
+ updated_resource_count
+ deprecations
+ policy_name
+ policy_group
+ }
+ end
+ let(:optional_fields) { %w{error} }
+
+ before do
+ allow(run_status).to receive(:exception).and_return(nil)
+ end
+
+ it "is not missing any required fields" do
+ missing_fields = required_fields.select do |key|
+ !Chef::DataCollector::Messages.run_end_message(reporter_data).key?(key)
+ end
+ expect(missing_fields).to eq([])
+ end
+
+ it "does not have any extra fields" do
+ extra_fields = Chef::DataCollector::Messages.run_end_message(reporter_data).keys.select do |key|
+ !required_fields.include?(key) && !optional_fields.include?(key)
+ end
+ expect(extra_fields).to eq([])
+ end
+
+ it "only includes updated resources in its count" do
+ message = Chef::DataCollector::Messages.run_end_message(reporter_data)
+ expect(message["total_resource_count"]).to eq(2)
+ expect(message["updated_resource_count"]).to eq(1)
+ end
+ end
+
+ context "when the run was not successful" do
+ let(:required_fields) do
+ %w{
+ chef_server_fqdn
+ entity_uuid
+ id
+ end_time
+ error
+ expanded_run_list
+ message_type
+ message_version
+ node
+ node_name
+ organization_name
+ resources
+ run_id
+ run_list
+ source
+ start_time
+ status
+ total_resource_count
+ updated_resource_count
+ deprecations
+ policy_name
+ policy_group
+ }
+ end
+ let(:optional_fields) { [] }
+
+ before do
+ allow(run_status).to receive(:exception).and_return(RuntimeError.new("an error happened"))
+ end
+
+ it "is not missing any required fields" do
+ missing_fields = required_fields.select do |key|
+ !Chef::DataCollector::Messages.run_end_message(reporter_data).key?(key)
+ end
+ expect(missing_fields).to eq([])
+ end
+
+ it "does not have any extra fields" do
+ extra_fields = Chef::DataCollector::Messages.run_end_message(reporter_data).keys.select do |key|
+ !required_fields.include?(key) && !optional_fields.include?(key)
+ end
+ expect(extra_fields).to eq([])
+ end
+ end
+ end
+end
diff --git a/spec/unit/data_collector/resource_report_spec.rb b/spec/unit/data_collector/resource_report_spec.rb
new file mode 100644
index 0000000000..278e94b53b
--- /dev/null
+++ b/spec/unit/data_collector/resource_report_spec.rb
@@ -0,0 +1,145 @@
+#
+# Author:: Salim Afiune (<afiune@chef.io)
+#
+# Copyright:: Copyright 2012-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::DataCollector::ResourceReport do
+ let(:cookbook_repo_path) { File.join(CHEF_SPEC_DATA, "cookbooks") }
+ let(:cookbook_collection) { Chef::CookbookCollection.new(Chef::CookbookLoader.new(cookbook_repo_path)) }
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) }
+ let(:resource) { Chef::Resource.new("zelda", run_context) }
+ let(:report) { described_class.new(resource, :create) }
+
+ describe "#skipped" do
+ let(:conditional) { double("Chef::Resource::Conditional") }
+
+ it "should set status and conditional" do
+ report.skipped(conditional)
+ expect(report.conditional).to eq conditional
+ expect(report.status).to eq "skipped"
+ end
+ end
+
+ describe "#up_to_date" do
+ it "should set status" do
+ report.up_to_date
+ expect(report.status).to eq "up-to-date"
+ end
+ end
+
+ describe "#updated" do
+ it "should set status" do
+ report.updated
+ expect(report.status).to eq "updated"
+ end
+ end
+
+ describe "#elapsed_time_in_milliseconds" do
+
+ context "when elapsed_time is not set" do
+ it "should return nil" do
+ allow(report).to receive(:elapsed_time).and_return(nil)
+ expect(report.elapsed_time_in_milliseconds).to eq nil
+ end
+ end
+
+ context "when elapsed_time is set" do
+ it "should return it in milliseconds" do
+ allow(report).to receive(:elapsed_time).and_return(1)
+ expect(report.elapsed_time_in_milliseconds).to eq 1000
+ end
+ end
+ end
+
+ describe "#failed" do
+ let(:exception) { double("Chef::Exception::Test") }
+
+ it "should set exception and status" do
+ report.failed(exception)
+ expect(report.exception).to eq exception
+ expect(report.status).to eq "failed"
+ end
+ end
+
+ describe "#to_hash" do
+ context "for a simple_resource" do
+ let(:resource) do
+ klass = Class.new(Chef::Resource) do
+ resource_name "zelda"
+ end
+ klass.new("hyrule", run_context)
+ end
+ let(:hash) do
+ {
+ "after" => {},
+ "before" => {},
+ "delta" => "",
+ "duration" => "",
+ "id" => "hyrule",
+ "ignore_failure" => false,
+ "name" => "hyrule",
+ "result" => "create",
+ "status" => "unprocessed",
+ "type" => :zelda,
+ }
+ end
+
+ it "returns a hash containing the expected values" do
+ expect(report.to_hash).to eq hash
+ end
+ end
+
+ context "for a lazy_resource that got skipped" do
+ let(:resource) do
+ klass = Class.new(Chef::Resource) do
+ resource_name "butters"
+ property :sword, String, name_property: true, identity: true
+ end
+ resource = klass.new("hyrule")
+ resource.sword = Chef::DelayedEvaluator.new { nil }
+ resource
+ end
+ let(:hash) do
+ {
+ "after" => {},
+ "before" => {},
+ "delta" => "",
+ "duration" => "",
+ "conditional" => "because",
+ "id" => "unknown identity (due to Chef::Exceptions::ValidationFailed)",
+ "ignore_failure" => false,
+ "name" => "hyrule",
+ "result" => "create",
+ "status" => "skipped",
+ "type" => :butters,
+ }
+ end
+ let(:conditional) do
+ double("Chef::Resource::Conditional", :to_text => "because")
+ end
+
+ it "should handle any Exception and throw a helpful message by mocking the identity" do
+ report.skipped(conditional)
+ expect(report.to_hash).to eq hash
+ end
+ end
+ end
+end
diff --git a/spec/unit/data_collector_spec.rb b/spec/unit/data_collector_spec.rb
new file mode 100644
index 0000000000..a469808e63
--- /dev/null
+++ b/spec/unit/data_collector_spec.rb
@@ -0,0 +1,875 @@
+#
+# Author:: Adam Leff (<adamleff@chef.io)
+# Author:: Ryan Cragun (<ryan@chef.io>)
+#
+# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/data_collector"
+require "chef/resource_builder"
+
+describe Chef::DataCollector do
+
+ describe ".register_reporter?" do
+ context "when no data collector URL or output locations are configured" do
+ it "returns false" do
+ Chef::Config[:data_collector][:server_url] = nil
+ Chef::Config[:data_collector][:output_locations] = nil
+ expect(Chef::DataCollector.register_reporter?).to be_falsey
+ end
+ end
+
+ context "when a data collector URL is configured" do
+ before do
+ Chef::Config[:data_collector][:server_url] = "http://data_collector"
+ end
+
+ context "when operating in why_run mode" do
+ it "returns false" do
+ Chef::Config[:why_run] = true
+ expect(Chef::DataCollector.register_reporter?).to be_falsey
+ end
+ end
+
+ context "when not operating in why_run mode" do
+
+ before do
+ Chef::Config[:why_run] = false
+ Chef::Config[:data_collector][:token] = token
+ end
+
+ context "when a token is configured" do
+
+ let(:token) { "supersecrettoken" }
+
+ context "when report is enabled for current mode" do
+ it "returns true" do
+ allow(Chef::DataCollector).to receive(:reporter_enabled_for_current_mode?).and_return(true)
+ expect(Chef::DataCollector.register_reporter?).to be_truthy
+ end
+ end
+
+ context "when report is disabled for current mode" do
+ it "returns false" do
+ allow(Chef::DataCollector).to receive(:reporter_enabled_for_current_mode?).and_return(false)
+ expect(Chef::DataCollector.register_reporter?).to be_falsey
+ end
+ end
+
+ end
+
+ # `Chef::Config[:data_collector][:server_url]` defaults to a URL
+ # relative to the `chef_server_url`, so we use configuration of the
+ # token to infer whether a solo/local mode user intends for data
+ # collection to be enabled.
+ context "when a token is not configured" do
+
+ let(:token) { nil }
+
+ context "when report is enabled for current mode" do
+
+ before do
+ allow(Chef::DataCollector).to receive(:reporter_enabled_for_current_mode?).and_return(true)
+ end
+
+ context "when the current mode is solo" do
+
+ before do
+ Chef::Config[:solo] = true
+ end
+
+ it "returns true" do
+ expect(Chef::DataCollector.register_reporter?).to be(true)
+ end
+
+ end
+
+ context "when the current mode is local mode" do
+
+ before do
+ Chef::Config[:local_mode] = true
+ end
+
+ it "returns false" do
+ expect(Chef::DataCollector.register_reporter?).to be(true)
+ end
+ end
+
+ context "when the current mode is client mode" do
+
+ before do
+ Chef::Config[:local_mode] = false
+ Chef::Config[:solo] = false
+ end
+
+ it "returns true" do
+ expect(Chef::DataCollector.register_reporter?).to be_truthy
+ end
+
+ end
+
+ end
+
+ context "when report is disabled for current mode" do
+ it "returns false" do
+ allow(Chef::DataCollector).to receive(:reporter_enabled_for_current_mode?).and_return(false)
+ expect(Chef::DataCollector.register_reporter?).to be_falsey
+ end
+ end
+
+ end
+
+ end
+ end
+
+ context "when output_locations are configured" do
+ before do
+ Chef::Config[:data_collector][:output_locations] = ["http://data_collector", "/tmp/data_collector.json"]
+ end
+
+ context "when operating in why_run mode" do
+ it "returns false" do
+ Chef::Config[:why_run] = true
+ expect(Chef::DataCollector.register_reporter?).to be_falsey
+ end
+ end
+
+ context "when not operating in why_run mode" do
+
+ before do
+ Chef::Config[:why_run] = false
+ Chef::Config[:data_collector][:token] = token
+ end
+
+ context "when a token is configured" do
+
+ let(:token) { "supersecrettoken" }
+
+ context "when report is enabled for current mode" do
+ it "returns true" do
+ allow(Chef::DataCollector).to receive(:reporter_enabled_for_current_mode?).and_return(true)
+ expect(Chef::DataCollector.register_reporter?).to be_truthy
+ end
+ end
+
+ context "when report is disabled for current mode" do
+ it "returns false" do
+ allow(Chef::DataCollector).to receive(:reporter_enabled_for_current_mode?).and_return(false)
+ expect(Chef::DataCollector.register_reporter?).to be_falsey
+ end
+ end
+
+ end
+
+ # `Chef::Config[:data_collector][:server_url]` defaults to a URL
+ # relative to the `chef_server_url`, so we use configuration of the
+ # token to infer whether a solo/local mode user intends for data
+ # collection to be enabled.
+ context "when a token is not configured" do
+
+ let(:token) { nil }
+
+ context "when report is enabled for current mode" do
+
+ before do
+ allow(Chef::DataCollector).to receive(:reporter_enabled_for_current_mode?).and_return(true)
+ end
+
+ context "when the current mode is solo" do
+
+ before do
+ Chef::Config[:solo] = true
+ end
+
+ it "returns true" do
+ expect(Chef::DataCollector.register_reporter?).to be(true)
+ end
+
+ end
+
+ context "when the current mode is local mode" do
+
+ before do
+ Chef::Config[:local_mode] = true
+ end
+
+ it "returns false" do
+ expect(Chef::DataCollector.register_reporter?).to be(true)
+ end
+ end
+
+ context "when the current mode is client mode" do
+
+ before do
+ Chef::Config[:local_mode] = false
+ Chef::Config[:solo] = false
+ end
+
+ it "returns true" do
+ expect(Chef::DataCollector.register_reporter?).to be_truthy
+ end
+
+ end
+
+ end
+
+ context "when report is disabled for current mode" do
+ it "returns false" do
+ allow(Chef::DataCollector).to receive(:reporter_enabled_for_current_mode?).and_return(false)
+ expect(Chef::DataCollector.register_reporter?).to be_falsey
+ end
+ end
+
+ end
+
+ end
+ end
+ end
+
+ describe ".reporter_enabled_for_current_mode?" do
+ context "when running in solo mode" do
+ before do
+ Chef::Config[:solo] = true
+ Chef::Config[:local_mode] = false
+ end
+
+ context "when data_collector_mode is :solo" do
+ it "returns true" do
+ Chef::Config[:data_collector][:mode] = :solo
+ expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(true)
+ end
+ end
+
+ context "when data_collector_mode is :client" do
+ it "returns false" do
+ Chef::Config[:data_collector][:mode] = :client
+ expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(false)
+ end
+ end
+
+ context "when data_collector_mode is :both" do
+ it "returns true" do
+ Chef::Config[:data_collector][:mode] = :both
+ expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(true)
+ end
+ end
+ end
+
+ context "when running in local mode" do
+ before do
+ Chef::Config[:solo] = false
+ Chef::Config[:local_mode] = true
+ end
+
+ context "when data_collector_mode is :solo" do
+ it "returns true" do
+ Chef::Config[:data_collector][:mode] = :solo
+ expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(true)
+ end
+ end
+
+ context "when data_collector_mode is :client" do
+ it "returns false" do
+ Chef::Config[:data_collector][:mode] = :client
+ expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(false)
+ end
+ end
+
+ context "when data_collector_mode is :both" do
+ it "returns true" do
+ Chef::Config[:data_collector][:mode] = :both
+ expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(true)
+ end
+ end
+ end
+
+ context "when running in client mode" do
+ before do
+ Chef::Config[:solo] = false
+ Chef::Config[:local_mode] = false
+ end
+
+ context "when data_collector_mode is :solo" do
+ it "returns false" do
+ Chef::Config[:data_collector][:mode] = :solo
+ expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(false)
+ end
+ end
+
+ context "when data_collector_mode is :client" do
+ it "returns true" do
+ Chef::Config[:data_collector][:mode] = :client
+ expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(true)
+ end
+ end
+
+ context "when data_collector_mode is :both" do
+ it "returns true" do
+ Chef::Config[:data_collector][:mode] = :both
+ expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(true)
+ end
+ end
+ end
+ end
+
+end
+
+describe Chef::DataCollector::Reporter do
+ let(:reporter) { described_class.new }
+ let(:run_status) { Chef::RunStatus.new(Chef::Node.new, Chef::EventDispatch::Dispatcher.new) }
+
+ let(:token) { "supersecrettoken" }
+
+ before do
+ Chef::Config[:data_collector][:server_url] = "http://my-data-collector-server.mycompany.com"
+ Chef::Config[:data_collector][:token] = token
+ end
+
+ describe "selecting token or signed header authentication" do
+
+ context "when the token is set in the config" do
+
+ before do
+ Chef::Config[:client_key] = "/no/key/should/exist/at/this/path.pem"
+ end
+
+ it "configures an HTTP client that doesn't do signed header auth" do
+ # Initializing with the wrong kind of HTTP class should cause Chef::Exceptions::PrivateKeyMissing
+ expect { reporter.http }.to_not raise_error
+ end
+
+ end
+
+ context "when no token is set in the config" do
+
+ let(:token) { nil }
+
+ let(:client_key) { File.join(CHEF_SPEC_DATA, "ssl", "private_key.pem") }
+
+ before do
+ Chef::Config[:client_key] = client_key
+ end
+
+ it "configures an HTTP client that does signed header auth" do
+ expect { reporter.http }.to_not raise_error
+ expect(reporter.http.options).to have_key(:signing_key_filename)
+ expect(reporter.http.options[:signing_key_filename]).to eq(client_key)
+ end
+ end
+
+ end
+
+ describe "#run_started" do
+ before do
+ allow(reporter).to receive(:update_run_status)
+ allow(reporter).to receive(:send_to_data_collector)
+ allow(Chef::DataCollector::Messages).to receive(:run_start_message)
+ end
+
+ it "updates the run status" do
+ expect(reporter).to receive(:update_run_status).with(run_status)
+ reporter.run_started(run_status)
+ end
+
+ it "sends the RunStart message output to the Data Collector server" do
+ expect(Chef::DataCollector::Messages)
+ .to receive(:run_start_message)
+ .with(run_status)
+ .and_return(key: "value")
+ expect(reporter).to receive(:send_to_data_collector).with({ key: "value" })
+ reporter.run_started(run_status)
+ end
+ end
+
+ describe "when sending a message at chef run completion" do
+
+ let(:node) { Chef::Node.new }
+
+ let(:run_status) do
+ instance_double("Chef::RunStatus",
+ run_id: "run_id",
+ node: node,
+ start_time: Time.new,
+ end_time: Time.new,
+ exception: exception)
+ end
+
+ before do
+ reporter.send(:update_run_status, run_status)
+ end
+
+ describe "#run_completed" do
+
+ let(:exception) { nil }
+
+ it "sends the run completion" do
+ expect(reporter).to receive(:send_to_data_collector) do |message|
+ expect(message).to be_a(Hash)
+ expect(message["status"]).to eq("success")
+ end
+ reporter.run_completed(node)
+ end
+ end
+
+ describe "#run_failed" do
+
+ let(:exception) { StandardError.new("oops") }
+
+ it "updates the exception and sends the run completion" do
+ expect(reporter).to receive(:send_to_data_collector) do |message|
+ expect(message).to be_a(Hash)
+ expect(message["status"]).to eq("failure")
+ end
+ reporter.run_failed("test_exception")
+ end
+ end
+ end
+
+ describe "#converge_start" do
+ it "stashes the run_context for later use" do
+ reporter.converge_start("test_context")
+ expect(reporter.run_context).to eq("test_context")
+ end
+ end
+
+ describe "#converge_complete" do
+ it "detects and processes any unprocessed resources" do
+ expect(reporter).to receive(:detect_unprocessed_resources)
+ reporter.converge_complete
+ end
+ end
+
+ describe "#converge_failed" do
+ it "detects and processes any unprocessed resources" do
+ expect(reporter).to receive(:detect_unprocessed_resources)
+ reporter.converge_failed("exception")
+ end
+ end
+
+ describe "#resource_current_state_loaded" do
+ let(:new_resource) { double("new_resource") }
+ let(:action) { double("action") }
+ let(:current_resource) { double("current_resource") }
+ let(:resource_report) { double("resource_report") }
+
+ context "when resource is a nested resource" do
+ it "does not update the resource report" do
+ allow(reporter).to receive(:nested_resource?).and_return(true)
+ expect(reporter).not_to receive(:update_current_resource_report)
+ reporter.resource_current_state_loaded(new_resource, action, current_resource)
+ end
+ end
+
+ context "when resource is not a nested resource" do
+ it "initializes the resource report" do
+ allow(reporter).to receive(:nested_resource?).and_return(false)
+ expect(reporter).to receive(:initialize_resource_report_if_needed)
+ .with(new_resource, action, current_resource)
+ reporter.resource_current_state_loaded(new_resource, action, current_resource)
+ end
+ end
+ end
+
+ describe "#resource_up_to_date" do
+ let(:new_resource) { double("new_resource") }
+ let(:action) { double("action") }
+ let(:resource_report) { double("resource_report") }
+
+ before do
+ allow(reporter).to receive(:nested_resource?)
+ allow(reporter).to receive(:current_resource_report).and_return(resource_report)
+ allow(resource_report).to receive(:up_to_date)
+ end
+
+ context "when the resource is a nested resource" do
+ it "does not mark the resource report as up-to-date" do
+ allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(true)
+ expect(resource_report).not_to receive(:up_to_date)
+ reporter.resource_up_to_date(new_resource, action)
+ end
+ end
+
+ context "when the resource is not a nested resource" do
+ it "marks the resource report as up-to-date" do
+ allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(false)
+ expect(resource_report).to receive(:up_to_date)
+ reporter.resource_up_to_date(new_resource, action)
+ end
+ end
+ end
+
+ describe "#resource_skipped" do
+ let(:new_resource) { double("new_resource") }
+ let(:action) { double("action") }
+ let(:conditional) { double("conditional") }
+ let(:resource_report) { double("resource_report") }
+
+ before do
+ allow(reporter).to receive(:nested_resource?)
+ allow(resource_report).to receive(:skipped)
+ end
+
+ context "when the resource is a nested resource" do
+ it "does not mark the resource report as skipped" do
+ allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(true)
+ expect(resource_report).not_to receive(:skipped).with(conditional)
+ reporter.resource_skipped(new_resource, action, conditional)
+ end
+ end
+
+ context "when the resource is not a nested resource" do
+ it "initializes the resource report and marks it as skipped" do
+ allow(reporter).to receive(:nested_resource?).and_return(false)
+ allow(reporter).to receive(:current_resource_report).and_return(resource_report)
+ expect(reporter).to receive(:initialize_resource_report_if_needed).with(new_resource, action)
+ expect(resource_report).to receive(:skipped).with(conditional)
+ reporter.resource_skipped(new_resource, action, conditional)
+ end
+ end
+ end
+
+ describe "#resource_updated" do
+ let(:resource_report) { double("resource_report") }
+
+ before do
+ allow(reporter).to receive(:current_resource_report).and_return(resource_report)
+ allow(resource_report).to receive(:updated)
+ end
+
+ it "marks the resource report as updated" do
+ expect(resource_report).to receive(:updated)
+ reporter.resource_updated("new_resource", "action")
+ end
+ end
+
+ describe "#resource_failed" do
+ let(:new_resource) { double("new_resource") }
+ let(:action) { double("action") }
+ let(:exception) { double("exception") }
+ let(:error_mapper) { double("error_mapper") }
+ let(:resource_report) { double("resource_report") }
+
+ before do
+ allow(reporter).to receive(:update_error_description)
+ allow(reporter).to receive(:current_resource_report).and_return(resource_report)
+ allow(resource_report).to receive(:failed)
+ allow(Chef::Formatters::ErrorMapper).to receive(:resource_failed).and_return(error_mapper)
+ allow(error_mapper).to receive(:for_json)
+ end
+
+ it "updates the error description" do
+ expect(Chef::Formatters::ErrorMapper).to receive(:resource_failed).with(
+ new_resource,
+ action,
+ exception
+ ).and_return(error_mapper)
+ expect(error_mapper).to receive(:for_json).and_return("error_description")
+ expect(reporter).to receive(:update_error_description).with("error_description")
+ reporter.resource_failed(new_resource, action, exception)
+ end
+
+ context "when the resource is not a nested resource" do
+ it "marks the resource report as failed" do
+ allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(false)
+ expect(resource_report).to receive(:failed).with(exception)
+ reporter.resource_failed(new_resource, action, exception)
+ end
+ end
+
+ context "when the resource is a nested resource" do
+ it "does not mark the resource report as failed" do
+ allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(true)
+ expect(resource_report).not_to receive(:failed).with(exception)
+ reporter.resource_failed(new_resource, action, exception)
+ end
+ end
+ end
+
+ describe "#resource_completed" do
+ let(:new_resource) { double("new_resource") }
+ let(:resource_report) { double("resource_report") }
+
+ before do
+ allow(reporter).to receive(:update_current_resource_report)
+ allow(reporter).to receive(:add_resource_report)
+ allow(reporter).to receive(:current_resource_report)
+ allow(resource_report).to receive(:finish)
+ end
+
+ context "when there is no current resource report" do
+ it "does not touch the current resource report" do
+ allow(reporter).to receive(:current_resource_report).and_return(nil)
+ expect(reporter).not_to receive(:update_current_resource_report)
+ reporter.resource_completed(new_resource)
+ end
+ end
+
+ context "when there is a current resource report" do
+ before do
+ allow(reporter).to receive(:current_resource_report).and_return(resource_report)
+ end
+
+ context "when the resource is a nested resource" do
+ it "does not mark the resource as finished" do
+ allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(true)
+ expect(resource_report).not_to receive(:finish)
+ reporter.resource_completed(new_resource)
+ end
+ end
+
+ context "when the resource is not a nested resource" do
+ before do
+ allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(false)
+ end
+
+ it "marks the current resource report as finished" do
+ expect(resource_report).to receive(:finish)
+ reporter.resource_completed(new_resource)
+ end
+
+ it "nils out the current resource report" do
+ expect(reporter).to receive(:clear_current_resource_report)
+ reporter.resource_completed(new_resource)
+ end
+ end
+ end
+ end
+
+ describe "#run_list_expanded" do
+ it "sets the expanded run list" do
+ reporter.run_list_expanded("test_run_list")
+ expect(reporter.expanded_run_list).to eq("test_run_list")
+ end
+ end
+
+ describe "#run_list_expand_failed" do
+ let(:node) { double("node") }
+ let(:error_mapper) { double("error_mapper") }
+ let(:exception) { double("exception") }
+
+ it "updates the error description" do
+ expect(Chef::Formatters::ErrorMapper).to receive(:run_list_expand_failed).with(
+ node,
+ exception
+ ).and_return(error_mapper)
+ expect(error_mapper).to receive(:for_json).and_return("error_description")
+ expect(reporter).to receive(:update_error_description).with("error_description")
+ reporter.run_list_expand_failed(node, exception)
+ end
+ end
+
+ describe "#cookbook_resolution_failed" do
+ let(:error_mapper) { double("error_mapper") }
+ let(:exception) { double("exception") }
+ let(:expanded_run_list) { double("expanded_run_list") }
+
+ it "updates the error description" do
+ expect(Chef::Formatters::ErrorMapper).to receive(:cookbook_resolution_failed).with(
+ expanded_run_list,
+ exception
+ ).and_return(error_mapper)
+ expect(error_mapper).to receive(:for_json).and_return("error_description")
+ expect(reporter).to receive(:update_error_description).with("error_description")
+ reporter.cookbook_resolution_failed(expanded_run_list, exception)
+ end
+
+ end
+
+ describe "#cookbook_sync_failed" do
+ let(:cookbooks) { double("cookbooks") }
+ let(:error_mapper) { double("error_mapper") }
+ let(:exception) { double("exception") }
+
+ it "updates the error description" do
+ expect(Chef::Formatters::ErrorMapper).to receive(:cookbook_sync_failed).with(
+ cookbooks,
+ exception
+ ).and_return(error_mapper)
+ expect(error_mapper).to receive(:for_json).and_return("error_description")
+ expect(reporter).to receive(:update_error_description).with("error_description")
+ reporter.cookbook_sync_failed(cookbooks, exception)
+ end
+ end
+
+ describe "#disable_reporter_on_error" do
+ context "when no exception is raise by the block" do
+ it "does not disable the reporter" do
+ expect(reporter).not_to receive(:disable_data_collector_reporter)
+ reporter.send(:disable_reporter_on_error) { true }
+ end
+
+ it "does not raise an exception" do
+ expect { reporter.send(:disable_reporter_on_error) { true } }.not_to raise_error
+ end
+ end
+
+ context "when an unexpected exception is raised by the block" do
+ it "re-raises the exception" do
+ expect { reporter.send(:disable_reporter_on_error) { raise "bummer" } }.to raise_error(RuntimeError)
+ end
+ end
+
+ [ Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
+ Errno::ECONNREFUSED, EOFError, Net::HTTPBadResponse,
+ Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError,
+ Errno::EHOSTDOWN ].each do |exception_class|
+ context "when the block raises a #{exception_class} exception" do
+ it "disables the reporter" do
+ expect(reporter).to receive(:disable_data_collector_reporter)
+ reporter.send(:disable_reporter_on_error) { raise exception_class.new("bummer") }
+ end
+
+ context "when raise-on-failure is enabled" do
+ it "logs an error and raises" do
+ Chef::Config[:data_collector][:raise_on_failure] = true
+ expect(Chef::Log).to receive(:error)
+ expect { reporter.send(:disable_reporter_on_error) { raise exception_class.new("bummer") } }.to raise_error(exception_class)
+ end
+ end
+
+ context "when raise-on-failure is disabled" do
+ it "logs an info message and does not raise an exception" do
+ Chef::Config[:data_collector][:raise_on_failure] = false
+ expect(Chef::Log).to receive(:info)
+ expect { reporter.send(:disable_reporter_on_error) { raise exception_class.new("bummer") } }.not_to raise_error
+ end
+ end
+ end
+ end
+ end
+
+ describe "#validate_data_collector_server_url!" do
+ context "when server_url is empty" do
+ it "raises an exception" do
+ Chef::Config[:data_collector][:server_url] = ""
+ expect { reporter.send(:validate_data_collector_server_url!) }.to raise_error(Chef::Exceptions::ConfigurationError)
+ end
+ end
+
+ context "when server_url is omitted but output_locations is specified" do
+ it "does not an exception" do
+ Chef::Config[:data_collector][:output_locations] = ["http://data_collector", "/tmp/data_collector.json"]
+ expect { reporter.send(:validate_data_collector_server_url!) }.not_to raise_error(Chef::Exceptions::ConfigurationError)
+ end
+ end
+
+ context "when server_url is not empty" do
+ context "when server_url is an invalid URI" do
+ it "raises an exception" do
+ Chef::Config[:data_collector][:server_url] = "this is not a URI"
+ expect { reporter.send(:validate_data_collector_server_url!) }.to raise_error(Chef::Exceptions::ConfigurationError)
+ end
+ end
+
+ context "when server_url is a valid URI" do
+ context "when server_url is a URI with no host" do
+ it "raises an exception" do
+ Chef::Config[:data_collector][:server_url] = "/file/uri.txt"
+ expect { reporter.send(:validate_data_collector_server_url!) }.to raise_error(Chef::Exceptions::ConfigurationError)
+ end
+
+ end
+
+ context "when server_url is a URI with a valid host" do
+ it "does not an exception" do
+ Chef::Config[:data_collector][:server_url] = "http://www.google.com/data-collector"
+ expect { reporter.send(:validate_data_collector_server_url!) }.not_to raise_error
+ end
+ end
+ end
+ end
+ end
+
+ describe "#validate_data_collector_output_locations!" do
+ context "when output_locations is empty" do
+ it "raises an exception" do
+ Chef::Config[:data_collector][:output_locations] = {}
+ expect { reporter.send(:validate_data_collector_output_locations!) }.to raise_error(Chef::Exceptions::ConfigurationError)
+ end
+ end
+
+ context "when valid output_locations are provided" do
+ it "does not raise an exception" do
+ Chef::Config[:data_collector][:output_locations] = { :urls => ["http://data_collector"], :files => ["data_collection.json"] }
+ expect { reporter.send(:validate_data_collector_output_locations!) }.not_to raise_error(Chef::Exceptions::ConfigurationError)
+ end
+ end
+
+ context "when output_locations contains an invalid URI" do
+ it "raises an exception" do
+ Chef::Config[:data_collector][:output_locations] = { :urls => ["this is not a url"], :files => ["/tmp/data_collection.json"] }
+ expect { reporter.send(:validate_data_collector_output_locations!) }.to raise_error(Chef::Exceptions::ConfigurationError)
+ end
+ end
+ end
+
+ describe "#detect_unprocessed_resources" do
+ context "when resources do not override core methods" do
+ it "adds resource reports for any resources that have not yet been processed" do
+ resource_a = Chef::Resource::Service.new("processed service")
+ resource_b = Chef::Resource::Service.new("unprocessed service")
+
+ resource_a.action = [ :enable, :start ]
+ resource_b.action = :start
+
+ run_context = Chef::RunContext.new(Chef::Node.new, Chef::CookbookCollection.new, nil)
+ run_context.resource_collection.insert(resource_a)
+ run_context.resource_collection.insert(resource_b)
+
+ allow(reporter).to receive(:run_context).and_return(run_context)
+
+ # process the actions for resource_a, but not resource_b
+ reporter.resource_up_to_date(resource_a, :enable)
+ reporter.resource_completed(resource_a)
+ reporter.resource_up_to_date(resource_a, :start)
+ reporter.resource_completed(resource_a)
+ expect(reporter.all_resource_reports.size).to eq(2)
+
+ # detect unprocessed resources, which should find that resource_b has not yet been processed
+ reporter.send(:detect_unprocessed_resources)
+ expect(reporter.all_resource_reports.size).to eq(3)
+ end
+ end
+
+ context "when a resource overrides a core method, such as #hash" do
+ it "does not raise an exception" do
+ resource_a = Chef::Resource::Service.new("processed service")
+ resource_b = Chef::Resource::Service.new("unprocessed service")
+
+ resource_a.action = :start
+ resource_b.action = :start
+
+ run_context = Chef::RunContext.new(Chef::Node.new, Chef::CookbookCollection.new, nil)
+ run_context.resource_collection.insert(resource_a)
+ run_context.resource_collection.insert(resource_b)
+
+ allow(reporter).to receive(:run_context).and_return(run_context)
+
+ # override the #hash method on resource_a to return a String instead of
+ # a Fixnum. Without the fix in chef/chef#5604, this would raise an
+ # exception when getting added to the Set/Hash.
+ resource_a.define_singleton_method(:hash) { "a string" }
+
+ # process the actions for resource_a, but not resource_b
+ reporter.resource_up_to_date(resource_a, :start)
+ reporter.resource_completed(resource_a)
+
+ expect { reporter.send(:detect_unprocessed_resources) }.not_to raise_error
+ end
+ end
+ end
+end
diff --git a/spec/unit/decorator/lazy_array_spec.rb b/spec/unit/decorator/lazy_array_spec.rb
new file mode 100644
index 0000000000..0c5c2eeee0
--- /dev/null
+++ b/spec/unit/decorator/lazy_array_spec.rb
@@ -0,0 +1,58 @@
+#
+# Author:: Lamont Granquist (<lamont@chef.io>)
+# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Decorator::LazyArray do
+ def foo
+ @foo ||= 1
+ end
+
+ def bar
+ @bar ||= 2
+ end
+
+ let(:decorator) do
+ Chef::Decorator::LazyArray.new { [ foo, bar ] }
+ end
+
+ it "behaves like an array" do
+ expect(decorator[0]).to eql(1)
+ expect(decorator[1]).to eql(2)
+ end
+
+ it "accessing the array elements is lazy" do
+ expect(decorator[0].class).to eql(Chef::Decorator::Lazy)
+ expect(decorator[1].class).to eql(Chef::Decorator::Lazy)
+ expect(@foo).to be nil
+ expect(@bar).to be nil
+ end
+
+ it "calling a method on the array element runs the proc (and both elements are autovivified)" do
+ expect(decorator[0].nil?).to be false
+ expect(@foo).to equal(1)
+ expect(@bar).to equal(2)
+ end
+
+ it "if we loop over the elements and do nothing then its not lazy" do
+ # we don't know how many elements there are unless we evaluate the proc
+ decorator.each { |i| }
+ expect(@foo).to equal(1)
+ expect(@bar).to equal(2)
+ end
+end
diff --git a/spec/unit/decorator/lazy_spec.rb b/spec/unit/decorator/lazy_spec.rb
new file mode 100644
index 0000000000..46c8955ae8
--- /dev/null
+++ b/spec/unit/decorator/lazy_spec.rb
@@ -0,0 +1,39 @@
+#
+# Author:: Lamont Granquist (<lamont@chef.io>)
+# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Decorator::Lazy do
+ let(:decorator) do
+ @a = 0
+ Chef::Decorator::Lazy.new { @a += 1 }
+ end
+
+ it "decorates an object" do
+ expect(decorator.even?).to be false
+ end
+
+ it "the proc runs and does work" do
+ expect(decorator).to eql(1)
+ end
+
+ it "creating the decorator does not cause the proc to run" do
+ decorator
+ expect(@a).to eql(0)
+ end
+end
diff --git a/spec/unit/decorator_spec.rb b/spec/unit/decorator_spec.rb
new file mode 100644
index 0000000000..6d73db2cc4
--- /dev/null
+++ b/spec/unit/decorator_spec.rb
@@ -0,0 +1,142 @@
+#
+# Author:: Lamont Granquist (<lamont@chef.io>)
+# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+def impersonates_a(klass)
+ it "#is_a?(#{klass}) is true" do
+ expect(decorator.is_a?(klass)).to be true
+ end
+
+ it "#is_a?(Chef::Decorator) is true" do
+ expect(decorator.is_a?(Chef::Decorator)).to be true
+ end
+
+ it "#kind_of?(#{klass}) is true" do
+ expect(decorator.kind_of?(klass)).to be true
+ end
+
+ it "#kind_of?(Chef::Decorator) is true" do
+ expect(decorator.kind_of?(Chef::Decorator)).to be true
+ end
+
+ it "#instance_of?(#{klass}) is false" do
+ expect(decorator.instance_of?(klass)).to be false
+ end
+
+ it "#instance_of?(Chef::Decorator) is true" do
+ expect(decorator.instance_of?(Chef::Decorator)).to be true
+ end
+
+ it "#class is Chef::Decorator" do
+ expect(decorator.class).to eql(Chef::Decorator)
+ end
+end
+
+describe Chef::Decorator do
+ let(:obj) {}
+ let(:decorator) { Chef::Decorator.new(obj) }
+
+ context "when the obj is a string" do
+ let(:obj) { "STRING" }
+
+ impersonates_a(String)
+
+ it "#nil? is false" do
+ expect(decorator.nil?).to be false
+ end
+
+ it "!! is true" do
+ expect(!!decorator).to be true
+ end
+
+ it "dup returns a decorator" do
+ expect(decorator.dup.class).to be Chef::Decorator
+ end
+
+ it "dup dup's the underlying thing" do
+ expect(decorator.dup.__getobj__).not_to equal(decorator.__getobj__)
+ end
+ end
+
+ context "when the obj is a nil" do
+ let(:obj) { nil }
+
+ it "#nil? is true" do
+ expect(decorator.nil?).to be true
+ end
+
+ it "!! is false" do
+ expect(!!decorator).to be false
+ end
+
+ impersonates_a(NilClass)
+ end
+
+ context "when the obj is an empty Hash" do
+ let(:obj) { {} }
+
+ impersonates_a(Hash)
+
+ it "formats it correctly through ffi-yajl and not the JSON gem" do
+ # this relies on a quirk of pretty formatting whitespace between yajl and ruby's JSON
+ expect(FFI_Yajl::Encoder.encode(decorator, pretty: true)).to eql("{\n\n}\n")
+ end
+ end
+
+ context "whent he obj is a Hash with elements" do
+ let(:obj) { { foo: "bar", baz: "qux" } }
+
+ impersonates_a(Hash)
+
+ it "dup is shallow on the Hash" do
+ expect(decorator.dup[:baz]).to equal(decorator[:baz])
+ end
+
+ it "deep mutating the dup'd hash mutates the origin" do
+ decorator.dup[:baz] << "qux"
+ expect(decorator[:baz]).to eql("quxqux")
+ end
+ end
+
+ context "memoizing methods" do
+ let(:obj) { {} }
+
+ it "calls method_missing only once" do
+ expect(decorator).to receive(:method_missing).once.and_call_original
+ expect(decorator.keys).to eql([])
+ expect(decorator.keys).to eql([])
+ end
+
+ it "switching a Hash to an Array responds to keys then does not" do
+ expect(decorator.respond_to?(:keys)).to be true
+ expect(decorator.keys).to eql([])
+ decorator.__setobj__([])
+ expect(decorator.respond_to?(:keys)).to be false
+ expect { decorator.keys }.to raise_error(NoMethodError)
+ end
+
+ it "memoization of methods happens on the instances, not the classes" do
+ decorator2 = Chef::Decorator.new([])
+ expect(decorator.respond_to?(:keys)).to be true
+ expect(decorator.keys).to eql([])
+ expect(decorator2.respond_to?(:keys)).to be false
+ expect { decorator2.keys }.to raise_error(NoMethodError)
+ end
+ end
+end
diff --git a/spec/unit/deprecated_spec.rb b/spec/unit/deprecated_spec.rb
new file mode 100644
index 0000000000..4eba764b63
--- /dev/null
+++ b/spec/unit/deprecated_spec.rb
@@ -0,0 +1,59 @@
+#
+# Copyright:: Copyright 2013-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/deprecated"
+
+describe Chef::Deprecated do
+ class TestDeprecation < Chef::Deprecated::Base
+ def id; 999; end
+
+ def target; "test.html"; end
+
+ def link; "#{Chef::Deprecated::Base::BASE_URL}test.html"; end
+ end
+
+ context "loading a deprecation class" do
+ it "loads the correct class" do
+ expect(Chef::Deprecated.create(:test_deprecation)).to be_an_instance_of(TestDeprecation)
+ end
+
+ it "optionally sets a message" do
+ deprecation = Chef::Deprecated.create(:test_deprecation, "A test message")
+ expect(deprecation.message).to eql("A test message")
+ end
+
+ it "optionally sets the location" do
+ deprecation = Chef::Deprecated.create(:test_deprecation, nil, "A test location")
+ expect(deprecation.location).to eql("A test location")
+ end
+ end
+
+ context "formatting deprecation warnings" do
+ let(:base_url) { Chef::Deprecated::Base::BASE_URL }
+ let(:message) { "A test message" }
+ let(:location) { "the location" }
+
+ it "displays the full URL" do
+ expect(TestDeprecation.new().url).to eql("#{base_url}test.html")
+ end
+
+ it "formats a complete deprecation message" do
+ expect(TestDeprecation.new(message, location).inspect).to eql("#{message} (CHEF-999)#{location}.\nhttps://docs.chef.io/deprecations_test.html")
+ end
+ end
+end
diff --git a/spec/unit/deprecation_spec.rb b/spec/unit/deprecation_spec.rb
index af8e34850b..6e2bcc32fd 100644
--- a/spec/unit/deprecation_spec.rb
+++ b/spec/unit/deprecation_spec.rb
@@ -45,35 +45,14 @@ describe Chef::Deprecation do
add_deprecation_warnings_for(DeprecatedMethods.instance_methods)
end
- method_snapshot_file = File.join(CHEF_SPEC_DATA, "file-providers-method-snapshot-chef-11-4.json")
- method_snapshot = Chef::JSONCompat.parse(File.open(method_snapshot_file).read())
-
- method_snapshot.each do |class_name, old_methods|
- class_object = class_from_string(class_name)
- current_methods = class_object.public_instance_methods.map(&:to_sym)
-
- it "defines all methods on #{class_object} that were available in 11.0" do
- old_methods.each do |old_method|
- expect(current_methods).to include(old_method.to_sym)
- end
- end
- end
-
context "when Chef::Config[:treat_deprecation_warnings_as_errors] is off" do
before do
Chef::Config[:treat_deprecation_warnings_as_errors] = false
end
context "deprecation warning messages" do
- RSpec::Matchers.define_negated_matcher :a_non_empty_array, :be_empty
-
it "should be enabled for deprecated methods" do
- expect(Chef::Log).to receive(:warn).with(a_non_empty_array)
- TestClass.new.deprecated_method(10)
- end
-
- it "should contain stack trace" do
- expect(Chef::Log).to receive(:warn).with(a_string_including(".rb"))
+ expect(Chef).to receive(:deprecated).with(:internal_api, /Method.*of 'TestClass'/)
TestClass.new.deprecated_method(10)
end
end
diff --git a/spec/unit/dsl/audit_spec.rb b/spec/unit/dsl/audit_spec.rb
index a8227f6c99..42e543fdb2 100644
--- a/spec/unit/dsl/audit_spec.rb
+++ b/spec/unit/dsl/audit_spec.rb
@@ -21,14 +21,14 @@ describe Chef::DSL::Audit do
end
it "raises an error when no audit name is given" do
- expect { auditor.control_group do end }.to raise_error(Chef::Exceptions::AuditNameMissing)
+ expect { auditor.control_group {} }.to raise_error(Chef::Exceptions::AuditNameMissing)
end
context "audits already populated" do
let(:audits) { { "unique" => {} } }
it "raises an error if the audit name is a duplicate" do
- expect { auditor.control_group "unique" do end }.to raise_error(Chef::Exceptions::AuditControlGroupDuplicate)
+ expect { auditor.control_group("unique") {} }.to raise_error(Chef::Exceptions::AuditControlGroupDuplicate)
end
end
@@ -36,7 +36,7 @@ describe Chef::DSL::Audit do
let(:auditor) { BadAuditDSLTester.new }
it "fails because it relies on the recipe DSL existing" do
- expect { auditor.control_group "unique" do end }.to raise_error(NoMethodError, /undefined method `cookbook_name'/)
+ expect { auditor.control_group("unique") {} }.to raise_error(NoMethodError, /undefined method `cookbook_name'/)
end
end
diff --git a/spec/unit/dsl/data_query_spec.rb b/spec/unit/dsl/data_query_spec.rb
index 55c6e5f578..f93f07bc52 100644
--- a/spec/unit/dsl/data_query_spec.rb
+++ b/spec/unit/dsl/data_query_spec.rb
@@ -60,14 +60,15 @@ describe Chef::DSL::DataQuery do
let(:item_name) { "item_name" }
- let(:raw_data) {{
+ let(:raw_data) do
+ {
"id" => item_name,
"greeting" => "hello",
"nested" => {
"a1" => [1, 2, 3],
"a2" => { "b1" => true },
},
- }}
+ } end
let(:item) do
item = Chef::DataBagItem.new
diff --git a/spec/unit/dsl/declare_resource_spec.rb b/spec/unit/dsl/declare_resource_spec.rb
index 6dd9317c21..255e85e22e 100644
--- a/spec/unit/dsl/declare_resource_spec.rb
+++ b/spec/unit/dsl/declare_resource_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,6 +32,15 @@ describe Chef::ResourceCollection do
Chef::Recipe.new("hjk", "test", run_context)
end
+ describe "mixed in correctly" do
+ it "the resources() method winds up in the right classes" do
+ methods = [ :resources, :find_resource, :find_resource!, :edit_resource, :edit_resource!, :delete_resource, :delete_resource!, :declare_resource, :build_resource ]
+ expect(Chef::Resource.instance_methods).to include(*methods)
+ expect(Chef::Recipe.instance_methods).to include(*methods)
+ expect(Chef::Provider.instance_methods).to include(*methods)
+ end
+ end
+
describe "#declare_resource" do
before do
recipe.declare_resource(:zen_master, "monkey") do
@@ -50,17 +59,17 @@ describe Chef::ResourceCollection do
describe "#edit_resource!" do
it "raises if nothing is found" do
- expect {
+ expect do
recipe.edit_resource!(:zen_master, "monkey") do
something true
end
- }.to raise_error(Chef::Exceptions::ResourceNotFound)
+ end.to raise_error(Chef::Exceptions::ResourceNotFound)
end
it "raises if nothing is found and no block is given" do
- expect {
+ expect do
recipe.edit_resource!(:zen_master, "monkey")
- }.to raise_error(Chef::Exceptions::ResourceNotFound)
+ end.to raise_error(Chef::Exceptions::ResourceNotFound)
end
it "edits the resource if it finds one" do
@@ -131,20 +140,20 @@ describe Chef::ResourceCollection do
describe "#find_resource!" do
it "raises if nothing is found" do
- expect {
+ expect do
recipe.find_resource!(:zen_master, "monkey")
- }.to raise_error(Chef::Exceptions::ResourceNotFound)
+ end.to raise_error(Chef::Exceptions::ResourceNotFound)
end
it "raises if given a block" do
resource = recipe.declare_resource(:zen_master, "monkey") do
something false
end
- expect {
+ expect do
recipe.find_resource!(:zen_master, "monkey") do
something false
end
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "returns the resource if it finds one" do
@@ -221,9 +230,9 @@ describe Chef::ResourceCollection do
describe "#delete_resource!" do
it "raises if nothing is found" do
- expect {
+ expect do
recipe.delete_resource!(:zen_master, "monkey")
- }.to raise_error(Chef::Exceptions::ResourceNotFound)
+ end.to raise_error(Chef::Exceptions::ResourceNotFound)
end
it "deletes and returns the resource if it finds one" do
@@ -235,6 +244,36 @@ describe Chef::ResourceCollection do
).to eql(resource)
expect(run_context.resource_collection.all_resources.size).to eql(0)
end
+
+ it "removes pending delayed notifications" do
+ recipe.declare_resource(:zen_master, "one")
+ recipe.declare_resource(:zen_master, "two") do
+ notifies :win, "zen_master[one]"
+ end
+ recipe.delete_resource(:zen_master, "two")
+ resource = recipe.declare_resource(:zen_master, "two")
+ expect(resource.delayed_notifications).to eql([])
+ end
+
+ it "removes pending immediate notifications" do
+ recipe.declare_resource(:zen_master, "one")
+ recipe.declare_resource(:zen_master, "two") do
+ notifies :win, "zen_master[one]", :immediate
+ end
+ recipe.delete_resource(:zen_master, "two")
+ resource = recipe.declare_resource(:zen_master, "two")
+ expect(resource.immediate_notifications).to eql([])
+ end
+
+ it "removes pending before notifications" do
+ recipe.declare_resource(:zen_master, "one")
+ recipe.declare_resource(:zen_master, "two") do
+ notifies :win, "zen_master[one]", :before
+ end
+ recipe.delete_resource(:zen_master, "two")
+ resource = recipe.declare_resource(:zen_master, "two")
+ expect(resource.before_notifications).to eql([])
+ end
end
describe "run_context helpers" do
diff --git a/spec/unit/dsl/reboot_pending_spec.rb b/spec/unit/dsl/reboot_pending_spec.rb
index 5cd7c7794f..2a12e27610 100644
--- a/spec/unit/dsl/reboot_pending_spec.rb
+++ b/spec/unit/dsl/reboot_pending_spec.rb
@@ -49,19 +49,6 @@ describe Chef::DSL::RebootPending do
allow(recipe).to receive(:registry_key_exists?).with('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending').and_return(true)
expect(recipe.reboot_pending?).to be_truthy
end
-
- context "version is server 2003" do
- before do
- allow(Chef::Platform).to receive(:windows_server_2003?).and_return(true)
- end
-
- it 'should return true if value "HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile" contains specific data on 2k3' do
- allow(recipe).to receive(:registry_key_exists?).with('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').and_return(true)
- allow(recipe).to receive(:registry_get_values).with('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').and_return(
- [{ :name => "Flags", :type => :dword, :data => 3 }])
- expect(recipe.reboot_pending?).to be_truthy
- end
- end
end
context "platform is ubuntu" do
diff --git a/spec/unit/dsl/recipe_spec.rb b/spec/unit/dsl/recipe_spec.rb
index bc97ecc029..cd01079c15 100644
--- a/spec/unit/dsl/recipe_spec.rb
+++ b/spec/unit/dsl/recipe_spec.rb
@@ -24,11 +24,6 @@ class RecipeDSLExampleClass
include Chef::DSL::Recipe
end
-FullRecipeDSLExampleClass = Struct.new(:cookbook_name, :recipe_name)
-class FullRecipeDSLExampleClass
- include Chef::DSL::Recipe::FullDSL
-end
-
RecipeDSLBaseAPI = Struct.new(:cookbook_name, :recipe_name)
class RecipeDSLExampleSubclass < RecipeDSLBaseAPI
include Chef::DSL::Recipe
@@ -41,14 +36,6 @@ describe Chef::DSL::Recipe do
let(:cookbook_name) { "example_cb" }
let(:recipe_name) { "example_recipe" }
- it "tracks when it is included via FullDSL" do
- expect(Chef::DSL::Recipe::FullDSL.descendants).to include(FullRecipeDSLExampleClass)
- end
-
- it "doesn't track what is included via only the recipe DSL" do
- expect(Chef::DSL::Recipe::FullDSL.descendants).not_to include(RecipeDSLExampleClass)
- end
-
shared_examples_for "A Recipe DSL Implementation" do
it "responds to cookbook_name" do
diff --git a/spec/unit/encrypted_data_bag_item/check_encrypted_spec.rb b/spec/unit/encrypted_data_bag_item/check_encrypted_spec.rb
index c2cb275f9d..7e885f8818 100644
--- a/spec/unit/encrypted_data_bag_item/check_encrypted_spec.rb
+++ b/spec/unit/encrypted_data_bag_item/check_encrypted_spec.rb
@@ -39,14 +39,15 @@ describe Chef::EncryptedDataBagItem::CheckEncrypted do
let(:default_secret) { "abc123SECRET" }
let(:item_name) { "item_name" }
- let(:raw_data) {{
+ let(:raw_data) do
+ {
"id" => item_name,
"greeting" => "hello",
"nested" => {
"a1" => [1, 2, 3],
"a2" => { "b1" => true },
},
- }}
+ } end
let(:version) { 1 }
let(:encoded_data) do
diff --git a/spec/unit/encrypted_data_bag_item_spec.rb b/spec/unit/encrypted_data_bag_item_spec.rb
index e17f7a2331..14b5d9eb28 100644
--- a/spec/unit/encrypted_data_bag_item_spec.rb
+++ b/spec/unit/encrypted_data_bag_item_spec.rb
@@ -39,7 +39,7 @@ describe Chef::EncryptedDataBagItem::Encryptor do
let(:key) { "passwd" }
it "encrypts to format version 1 by default" do
- expect(encryptor).to be_a_instance_of(Chef::EncryptedDataBagItem::Encryptor::Version1Encryptor)
+ expect(encryptor).to be_a_instance_of(Chef::EncryptedDataBagItem::Encryptor::Version3Encryptor)
end
describe "generating a random IV" do
@@ -66,8 +66,8 @@ describe Chef::EncryptedDataBagItem::Encryptor do
final_data = encryptor.for_encrypted_item
expect(final_data["encrypted_data"]).to eq encryptor.encrypted_data
expect(final_data["iv"]).to eq Base64.encode64(encryptor.iv)
- expect(final_data["version"]).to eq 1
- expect(final_data["cipher"]).to eq "aes-256-cbc"
+ expect(final_data["version"]).to eq 3
+ expect(final_data["cipher"]).to eq "aes-256-gcm"
end
end
@@ -238,7 +238,7 @@ describe Chef::EncryptedDataBagItem::Decryptor do
context "when decrypting a version 1 (JSON+aes-256-cbc+random iv) encrypted value" do
let(:encrypted_value) do
- Chef::EncryptedDataBagItem::Encryptor.new(plaintext_data, encryption_key).for_encrypted_item
+ Chef::EncryptedDataBagItem::Encryptor::Version1Encryptor.new(plaintext_data, encryption_key).for_encrypted_item
end
it "selects the correct strategy for version 1" do
@@ -320,11 +320,12 @@ end
describe Chef::EncryptedDataBagItem do
subject { described_class }
let(:encrypted_data_bag_item) { subject.new(encoded_data, secret) }
- let(:plaintext_data) {{
+ let(:plaintext_data) do
+ {
"id" => "item_name",
"greeting" => "hello",
"nested" => { "a1" => [1, 2, 3], "a2" => { "b1" => true } },
- }}
+ } end
let(:secret) { "abc123SECRET" }
let(:encoded_data) { subject.encrypt_data_bag_item(plaintext_data, secret) }
@@ -335,7 +336,7 @@ describe Chef::EncryptedDataBagItem do
end
it "encrypts non-collection objects" do
- expect(encoded_data["greeting"]["version"]).to eq 1
+ expect(encoded_data["greeting"]["version"]).to eq 3
expect(encoded_data["greeting"]).to have_key("iv")
iv = encoded_data["greeting"]["iv"]
@@ -345,7 +346,7 @@ describe Chef::EncryptedDataBagItem do
end
it "encrypts nested values" do
- expect(encoded_data["nested"]["version"]).to eq 1
+ expect(encoded_data["nested"]["version"]).to eq 3
expect(encoded_data["nested"]).to have_key("iv")
iv = encoded_data["nested"]["iv"]
diff --git a/spec/unit/environment_spec.rb b/spec/unit/environment_spec.rb
index 4689c60b39..c604148ec8 100644
--- a/spec/unit/environment_spec.rb
+++ b/spec/unit/environment_spec.rb
@@ -3,7 +3,7 @@
# Author:: Seth Falcon (<seth@ospcode.com>)
# Author:: John Keiser (<jkeiser@ospcode.com>)
# Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright 2010-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -250,7 +250,7 @@ describe Chef::Environment do
end
it "should validate the version string of each cookbook" do
- @cookbook_versions.each do |cookbook, version|
+ @cookbook_versions.each_value do |version|
expect(Chef::Environment).to receive(:validate_cookbook_version).with(version).and_return true
end
Chef::Environment.validate_cookbook_versions(@cookbook_versions)
@@ -288,17 +288,17 @@ describe Chef::Environment do
describe "in solo mode" do
before do
- Chef::Config[:solo] = true
+ Chef::Config[:solo_legacy_mode] = true
end
after do
- Chef::Config[:solo] = false
+ Chef::Config[:solo_legacy_mode] = false
end
- it "should raise and exception" do
- expect {
+ it "should raise an exception" do
+ expect do
Chef::Environment.validate_cookbook_version("= 1.2.3.4")
- }.to raise_error Chef::Exceptions::IllegalVersionConstraint,
+ end.to raise_error Chef::Exceptions::IllegalVersionConstraint,
"Environment cookbook version constraints not allowed in chef-solo"
end
end
@@ -392,12 +392,12 @@ describe Chef::Environment do
describe "when loading" do
describe "in solo mode" do
before do
- Chef::Config[:solo] = true
+ Chef::Config[:solo_legacy_mode] = true
Chef::Config[:environment_path] = "/var/chef/environments"
end
after do
- Chef::Config[:solo] = false
+ Chef::Config[:solo_legacy_mode] = false
end
it "should get the environment from the environment_path" do
@@ -450,9 +450,9 @@ describe Chef::Environment do
it "should raise an error if the configured environment_path is invalid" do
expect(File).to receive(:directory?).with(Chef::Config[:environment_path]).and_return(false)
- expect {
+ expect do
Chef::Environment.load("foo")
- }.to raise_error Chef::Exceptions::InvalidEnvironmentPath, "Environment path '/var/chef/environments' is invalid"
+ end.to raise_error Chef::Exceptions::InvalidEnvironmentPath, "Environment path '/var/chef/environments' is invalid"
end
it "should raise an error if the file does not exist" do
@@ -460,9 +460,9 @@ describe Chef::Environment do
expect(File).to receive(:exists?).with(File.join(Chef::Config[:environment_path], "foo.json")).and_return(false)
expect(File).to receive(:exists?).with(File.join(Chef::Config[:environment_path], "foo.rb")).and_return(false)
- expect {
+ expect do
Chef::Environment.load("foo")
- }.to raise_error Chef::Exceptions::EnvironmentNotFound, "Environment 'foo' could not be loaded from disk"
+ end.to raise_error Chef::Exceptions::EnvironmentNotFound, "Environment 'foo' could not be loaded from disk"
end
end
end
diff --git a/spec/unit/event_dispatch/dispatcher_spec.rb b/spec/unit/event_dispatch/dispatcher_spec.rb
index 2360b2675d..5061a9845f 100644
--- a/spec/unit/event_dispatch/dispatcher_spec.rb
+++ b/spec/unit/event_dispatch/dispatcher_spec.rb
@@ -52,8 +52,8 @@ describe Chef::EventDispatch::Dispatcher do
dispatcher.synchronized_cookbook("apache2", cookbook_version)
exception = StandardError.new("foo")
- expect(event_sink).to receive(:recipe_file_load_failed).with("/path/to/file.rb", exception)
- dispatcher.recipe_file_load_failed("/path/to/file.rb", exception)
+ expect(event_sink).to receive(:recipe_file_load_failed).with("/path/to/file.rb", exception, "myrecipe")
+ dispatcher.recipe_file_load_failed("/path/to/file.rb", exception, "myrecipe")
end
context "when an event sink has fewer arguments for an event" do
diff --git a/spec/unit/exceptions_spec.rb b/spec/unit/exceptions_spec.rb
index 940edfec2e..c892b24f28 100644
--- a/spec/unit/exceptions_spec.rb
+++ b/spec/unit/exceptions_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Exceptions do
exception_to_super_class = {
Chef::Exceptions::Application => RuntimeError,
Chef::Exceptions::Cron => RuntimeError,
- Chef::Exceptions::Env => RuntimeError,
+ Chef::Exceptions::WindowsEnv => RuntimeError,
Chef::Exceptions::Exec => RuntimeError,
Chef::Exceptions::FileNotFound => RuntimeError,
Chef::Exceptions::Package => RuntimeError,
@@ -68,6 +68,8 @@ describe Chef::Exceptions do
Chef::Exceptions::EnvironmentNotFound => RuntimeError,
Chef::Exceptions::InvalidVersionConstraint => ArgumentError,
Chef::Exceptions::IllegalVersionConstraint => NotImplementedError,
+ Chef::Exceptions::RegKeyValuesTypeMissing => ArgumentError,
+ Chef::Exceptions::RegKeyValuesDataMissing => ArgumentError,
}
exception_to_super_class.each do |exception, expected_super_class|
diff --git a/spec/unit/file_access_control_spec.rb b/spec/unit/file_access_control_spec.rb
index 4357a91148..ee806b5c3a 100644
--- a/spec/unit/file_access_control_spec.rb
+++ b/spec/unit/file_access_control_spec.rb
@@ -43,7 +43,7 @@ describe Chef::FileAccessControl do
end
describe "class methods" do
- it 'responds to #writable?' do
+ it "responds to #writable?" do
expect(Chef::FileAccessControl).to respond_to(:writable?)
end
end
@@ -67,7 +67,7 @@ describe Chef::FileAccessControl do
it "raises a Chef::Exceptions::UserIDNotFound error when Etc can't find the user's name" do
expect(Etc).to receive(:getpwnam).with("toor").and_raise(ArgumentError)
- expect { @fac.target_uid ; @provider_requirements.run(:create) }.to raise_error(Chef::Exceptions::UserIDNotFound, "cannot determine user id for 'toor', does the user exist on this system?")
+ expect { @fac.target_uid; @provider_requirements.run(:create) }.to raise_error(Chef::Exceptions::UserIDNotFound, "cannot determine user id for 'toor', does the user exist on this system?")
end
it "does not attempt to resolve the uid if the user is not specified" do
@@ -84,7 +84,7 @@ describe Chef::FileAccessControl do
it "raises an ArgumentError if the resource's owner is set to something wack" do
@resource.instance_variable_set(:@owner, :diaf)
- expect { @fac.target_uid ; @provider_requirements.run(:create) }.to raise_error(ArgumentError)
+ expect { @fac.target_uid; @provider_requirements.run(:create) }.to raise_error(ArgumentError)
end
it "uses the resource's uid for the target uid when the resource's owner is specified by an integer" do
diff --git a/spec/unit/file_content_management/tempfile_spec.rb b/spec/unit/file_content_management/tempfile_spec.rb
index a833e21fac..a04ace5f16 100644
--- a/spec/unit/file_content_management/tempfile_spec.rb
+++ b/spec/unit/file_content_management/tempfile_spec.rb
@@ -19,38 +19,59 @@
require "spec_helper"
describe Chef::FileContentManagement::Tempfile do
- let(:resource) {
- r = Chef::Resource::File.new("new_file")
- r.path "/foo/bar/new_file"
- r
- }
- subject { described_class.new(resource) }
+ def tempfile_object_for_path(path)
+ r = Chef::Resource::File.new("decorative name that should not matter")
+ r.path path
+ Chef::FileContentManagement::Tempfile.new(r)
+ end
describe "#tempfile_basename" do
it "should return a dotfile", :unix_only do
+ subject = tempfile_object_for_path("/foo/bar/new_file")
expect(subject.send(:tempfile_basename)).to eql(".chef-new_file")
end
it "should return a file", :windows_only do
+ subject = tempfile_object_for_path("/foo/bar/new_file")
+ expect(subject.send(:tempfile_basename)).to eql("chef-new_file")
+ end
+
+ it "should strip the extension", :unix_only do
+ subject = tempfile_object_for_path("/foo/bar/new_file.html.erb")
+ expect(subject.send(:tempfile_basename)).to eql(".chef-new_file")
+ end
+
+ it "should strip the extension", :windows_only do
+ subject = tempfile_object_for_path("/foo/bar/new_file.html.erb")
expect(subject.send(:tempfile_basename)).to eql("chef-new_file")
end
end
+ describe "#tempfile_extension" do
+ it "should preserve the file extension" do
+ subject = tempfile_object_for_path("/foo/bar/new_file.html.erb")
+ expect(subject.send(:tempfile_extension)).to eql(".html.erb")
+ end
+ end
+
describe "#tempfile_dirnames" do
it "should select a temp dir" do
+ subject = tempfile_object_for_path("/foo/bar/new_file")
Chef::Config[:file_staging_uses_destdir] = false
expect(Dir).to receive(:tmpdir).and_return("/tmp/dir")
expect(subject.send(:tempfile_dirnames)).to eql(%w{ /tmp/dir })
end
it "should select the destdir" do
+ subject = tempfile_object_for_path("/foo/bar/new_file")
Chef::Config[:file_staging_uses_destdir] = true
expect(subject.send(:tempfile_dirnames)).to eql(%w{ /foo/bar })
end
it "should select the destdir and a temp dir" do
+ subject = tempfile_object_for_path("/foo/bar/new_file")
Chef::Config[:file_staging_uses_destdir] = :auto
expect(Dir).to receive(:tmpdir).and_return("/tmp/dir")
expect(subject.send(:tempfile_dirnames)).to eql(%w{ /foo/bar /tmp/dir })
@@ -67,18 +88,27 @@ describe Chef::FileContentManagement::Tempfile do
end
it "should create a temporary file" do
+ subject = tempfile_object_for_path("/foo/bar/new_file")
expect(subject.send(:tempfile_open)).to be_a(Tempfile)
end
+ it "should preserve the extension in the tempfile path" do
+ subject = tempfile_object_for_path("/foo/bar/new_file.html.erb")
+ tempfile = subject.send(:tempfile_open)
+ expect(tempfile.path).to match(/chef-new_file.*\.html\.erb$/)
+ end
+
it "should pick the destdir preferrentially" do
- expect(Tempfile).to receive(:open).with(tempname, "/foo/bar").and_return(tempfile)
+ subject = tempfile_object_for_path("/foo/bar/new_file")
+ expect(Tempfile).to receive(:open).with([tempname, ""], "/foo/bar").and_return(tempfile)
subject.send(:tempfile_open)
end
it "should use ENV['TMP'] otherwise" do
+ subject = tempfile_object_for_path("/foo/bar/new_file")
expect(Dir).to receive(:tmpdir).and_return("/tmp/dir")
- expect(Tempfile).to receive(:open).with(tempname, "/foo/bar").and_raise(SystemCallError, "foo")
- expect(Tempfile).to receive(:open).with(tempname, "/tmp/dir").and_return(tempfile)
+ expect(Tempfile).to receive(:open).with([tempname, ""], "/foo/bar").and_raise(SystemCallError, "foo")
+ expect(Tempfile).to receive(:open).with([tempname, ""], "/tmp/dir").and_return(tempfile)
subject.send(:tempfile_open)
end
end
diff --git a/spec/unit/formatters/base_spec.rb b/spec/unit/formatters/base_spec.rb
index 30c7757e5a..19182554f9 100644
--- a/spec/unit/formatters/base_spec.rb
+++ b/spec/unit/formatters/base_spec.rb
@@ -23,6 +23,14 @@ describe Chef::Formatters::Base do
let(:out) { StringIO.new }
let(:err) { StringIO.new }
let(:formatter) { Chef::Formatters::Base.new(out, err) }
+ let(:exception) do
+ # An exception with a real backtrace.
+ begin
+ raise EOFError
+ rescue EOFError => exc
+ end
+ exc
+ end
it "starts with an indentation of zero" do
expect(formatter.output.indent).to eql(0)
@@ -45,27 +53,48 @@ describe Chef::Formatters::Base do
end
it "humanizes EOFError exceptions for #registration_failed" do
- formatter.registration_failed("foo.example.com", EOFError.new, double("Chef::Config"))
+ formatter.registration_failed("foo.example.com", exception, double("Chef::Config"))
expect(out.string).to match(/Received an EOF on transport socket/)
end
it "humanizes EOFError exceptions for #node_load_failed" do
- formatter.node_load_failed("foo.example.com", EOFError.new, double("Chef::Config"))
+ formatter.node_load_failed("foo.example.com", exception, double("Chef::Config"))
expect(out.string).to match(/Received an EOF on transport socket/)
end
it "humanizes EOFError exceptions for #run_list_expand_failed" do
- formatter.run_list_expand_failed(double("Chef::Node"), EOFError.new)
+ formatter.run_list_expand_failed(double("Chef::Node"), exception)
expect(out.string).to match(/Received an EOF on transport socket/)
end
it "humanizes EOFError exceptions for #cookbook_resolution_failed" do
- formatter.run_list_expand_failed(double("Expanded Run List"), EOFError.new)
+ formatter.run_list_expand_failed(double("Expanded Run List"), exception)
expect(out.string).to match(/Received an EOF on transport socket/)
end
it "humanizes EOFError exceptions for #cookbook_sync_failed" do
- formatter.cookbook_sync_failed("foo.example.com", EOFError.new)
+ formatter.cookbook_sync_failed("foo.example.com", exception)
expect(out.string).to match(/Received an EOF on transport socket/)
end
+
+ it "outputs error information for failed resources with ignore_failure true" do
+ resource = Chef::Resource::RubyBlock.new("test")
+ resource.ignore_failure(true)
+ formatter.resource_failed(resource, :run, exception)
+ expect(out.string).to match(/Error executing action `run` on resource 'ruby_block\[test\]'/)
+ end
+
+ it "does not output error information for failed resources with ignore_failure :quiet" do
+ resource = Chef::Resource::RubyBlock.new("test")
+ resource.ignore_failure(:quiet)
+ formatter.resource_failed(resource, :run, exception)
+ expect(out.string).to eq("")
+ end
+
+ it "does not output error information for failed resources with ignore_failure 'quiet'" do
+ resource = Chef::Resource::RubyBlock.new("test")
+ resource.ignore_failure("quiet")
+ formatter.resource_failed(resource, :run, exception)
+ expect(out.string).to eq("")
+ end
end
diff --git a/spec/unit/formatters/error_description_spec.rb b/spec/unit/formatters/error_description_spec.rb
index 8b088308fa..cf6372ed49 100644
--- a/spec/unit/formatters/error_description_spec.rb
+++ b/spec/unit/formatters/error_description_spec.rb
@@ -49,7 +49,21 @@ describe Chef::Formatters::ErrorDescription do
describe "#display" do
before do
- stub_const("RUBY_PLATFORM", "ruby-foo-9000")
+ stub_const("Chef::VERSION", "1.2.3")
+ stub_const("RUBY_DESCRIPTION", "ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]")
+ allow(subject).to receive(:caller) { Kernel.caller + ["/test/bin/chef-client:1:in `<main>'"] }
+ allow(File).to receive(:realpath).and_call_original
+ allow(File).to receive(:realpath).with("/test/bin/chef-client").and_return("/test/bin/chef-client")
+ end
+
+ around do |ex|
+ old_program_name = $PROGRAM_NAME
+ begin
+ $PROGRAM_NAME = "chef-client"
+ ex.run
+ ensure
+ $PROGRAM_NAME = old_program_name
+ end
end
context "when no sections have been added" do
@@ -60,9 +74,12 @@ describe Chef::Formatters::ErrorDescription do
test title
================================================================================
-Platform:
----------
-ruby-foo-9000
+System Info:
+------------
+chef_version=1.2.3
+ruby=ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]
+program_name=chef-client
+executable=/test/bin/chef-client
END
end
@@ -84,9 +101,37 @@ test heading
------------
test text
-Platform:
----------
-ruby-foo-9000
+System Info:
+------------
+chef_version=1.2.3
+ruby=ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]
+program_name=chef-client
+executable=/test/bin/chef-client
+
+ END
+ end
+
+ end
+
+ context "when node object is available" do
+ it "should output the expected sections" do
+ # This can't be in a before block because the spec-wide helper calls a
+ # reset on global values.
+ Chef.set_node({ "platform" => "openvms", "platform_version" => "8.4-2L1" })
+ subject.display(out)
+ expect(out.out.string).to eq <<-END
+================================================================================
+test title
+================================================================================
+
+System Info:
+------------
+chef_version=1.2.3
+platform=openvms
+platform_version=8.4-2L1
+ruby=ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]
+program_name=chef-client
+executable=/test/bin/chef-client
END
end
diff --git a/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb b/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb
index 97fa92ca52..0e5104a0db 100644
--- a/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb
+++ b/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb
@@ -32,13 +32,13 @@ describe Chef::Formatters::APIErrorFormatting do
let(:min_version) { "2" }
let(:max_version) { "5" }
let(:request_version) { "30" }
- let(:return_hash) {
+ let(:return_hash) do
{
"min_version" => min_version,
"max_version" => max_version,
"request_version" => request_version,
}
- }
+ end
before do
# mock out the header
diff --git a/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb b/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
index e76e69d2cd..746b343e9c 100644
--- a/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
+++ b/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
@@ -105,9 +105,9 @@ describe Chef::GuardInterpreter::ResourceGuardInterpreter do
parent_resource
end
- let(:shell_out) {
+ let(:shell_out) do
instance_double(Mixlib::ShellOut, :live_stream => true, :run_command => true, :error! => nil)
- }
+ end
before do
# TODO for some reason Windows is failing on executing a ruby script
diff --git a/spec/unit/handler_spec.rb b/spec/unit/handler_spec.rb
index 65c3ddc4cb..09dd99d1ee 100644
--- a/spec/unit/handler_spec.rb
+++ b/spec/unit/handler_spec.rb
@@ -37,7 +37,7 @@ describe Chef::Handler do
@run_status.exception = @exception
@run_context = Chef::RunContext.new(@node, {}, @events)
@all_resources = [Chef::Resource::Cat.new("lolz"), Chef::Resource::ZenMaster.new("tzu")]
- @all_resources.first.updated = true
+ @all_resources.first.updated_by_last_action true
@run_context.resource_collection.all_resources.replace(@all_resources)
@run_status.run_context = @run_context
@start_time = Time.now
@@ -118,7 +118,7 @@ describe Chef::Handler do
before do
@run_context = Chef::RunContext.new(@node, {}, @events)
@all_resources = [Chef::Resource::Cat.new("foo"), Chef::Resource::ZenMaster.new("moo")]
- @all_resources.first.updated = true
+ @all_resources.first.updated_by_last_action true
@run_context.resource_collection.all_resources.replace(@all_resources)
@run_status.run_context = @run_context
@start_time = Time.now
@@ -212,4 +212,91 @@ describe Chef::Handler do
end
end
+ describe "library report handler" do
+ before do
+ # we need to lazily declare this after we have reset Chef::Config in the default rspec before handler
+ class MyTestHandler < Chef::Handler
+ handler_for :report, :exception, :start
+
+ class << self
+ attr_accessor :ran_report
+ end
+
+ def report
+ self.class.ran_report = true
+ end
+ end
+ end
+
+ it "gets added to Chef::Config[:report_handlers]" do
+ expect(Chef::Config[:report_handlers].include?(MyTestHandler)).to be true
+ end
+
+ it "gets added to Chef::Config[:exception_handlers]" do
+ expect(Chef::Config[:exception_handlers].include?(MyTestHandler)).to be true
+ end
+
+ it "gets added to Chef::Config[:start_handlers]" do
+ expect(Chef::Config[:start_handlers].include?(MyTestHandler)).to be true
+ end
+
+ it "runs the report handler" do
+ Chef::Handler.run_report_handlers(@run_status)
+ expect(MyTestHandler.ran_report).to be true
+ end
+
+ it "runs the exception handler" do
+ Chef::Handler.run_exception_handlers(@run_status)
+ expect(MyTestHandler.ran_report).to be true
+ end
+
+ it "runs the start handler" do
+ Chef::Handler.run_start_handlers(@run_status)
+ expect(MyTestHandler.ran_report).to be true
+ end
+ end
+
+ describe "library singleton report handler" do
+ before do
+ # we need to lazily declare this after we have reset Chef::Config in the default rspec before handler
+ class MyTestHandler < Chef::Handler
+ handler_for :report, :exception, :start
+
+ include Singleton
+
+ attr_accessor :ran_report
+
+ def report
+ self.ran_report = true
+ end
+ end
+ end
+
+ it "gets added to Chef::Config[:report_handlers]" do
+ expect(Chef::Config[:report_handlers].include?(MyTestHandler)).to be true
+ end
+
+ it "gets added to Chef::Config[:exception_handlers]" do
+ expect(Chef::Config[:exception_handlers].include?(MyTestHandler)).to be true
+ end
+
+ it "gets added to Chef::Config[:start_handlers]" do
+ expect(Chef::Config[:start_handlers].include?(MyTestHandler)).to be true
+ end
+
+ it "runs the report handler" do
+ Chef::Handler.run_report_handlers(@run_status)
+ expect(MyTestHandler.instance.ran_report).to be true
+ end
+
+ it "runs the exception handler" do
+ Chef::Handler.run_exception_handlers(@run_status)
+ expect(MyTestHandler.instance.ran_report).to be true
+ end
+
+ it "runs the start handler" do
+ Chef::Handler.run_start_handlers(@run_status)
+ expect(MyTestHandler.instance.ran_report).to be true
+ end
+ end
end
diff --git a/spec/unit/http/api_versions_spec.rb b/spec/unit/http/api_versions_spec.rb
new file mode 100644
index 0000000000..2ccb847acc
--- /dev/null
+++ b/spec/unit/http/api_versions_spec.rb
@@ -0,0 +1,82 @@
+#
+# Copyright:: Copyright 2017-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::HTTP::APIVersions do
+ class TestVersionClient < Chef::HTTP
+ use Chef::HTTP::APIVersions
+ end
+
+ before do
+ Chef::ServerAPIVersions.instance.reset!
+ end
+
+ let(:method) { "GET" }
+ let(:url) { "http://localhost:60123" }
+ let(:headers) { {} }
+ let(:data) { false }
+
+ let(:request) {}
+ let(:return_value) { "200" }
+
+ # Test Variables
+ let(:response_body) { "Thanks for checking in." }
+ let(:response_headers) do
+ {
+ "x-ops-server-api-version" => { "min_version" => 0, "max_version" => 2 }.to_json,
+ }
+ end
+
+ let(:response) do
+ m = double("HttpResponse", :body => response_body)
+ allow(m).to receive(:key?).with("x-ops-server-api-version").and_return(true)
+ allow(m).to receive(:code).and_return(return_value)
+ allow(m).to receive(:[]) do |key|
+ response_headers[key]
+ end
+
+ m
+ end
+
+ let(:client) do
+ TestVersionClient.new(url, { version_class: VersionedClassVersions })
+ end
+
+ let(:middleware) do
+ client.middlewares[0]
+ end
+
+ def run_api_version_handler
+ middleware.handle_request(method, url, headers, data)
+ middleware.handle_response(response, request, return_value)
+ end
+
+ it "correctly stores server api versions" do
+ run_api_version_handler
+ expect(Chef::ServerAPIVersions.instance.min_server_version).to eq(0)
+ end
+
+ context "with an unacceptable api version" do
+ let (:return_value) { "406" }
+ it "resets the list of supported versions" do
+ Chef::ServerAPIVersions.instance.set_versions({ "min_version" => 1, "max_version" => 3 })
+ run_api_version_handler
+ expect(Chef::ServerAPIVersions.instance.min_server_version).to eq(0)
+ end
+ end
+end
diff --git a/spec/unit/http/authenticator_spec.rb b/spec/unit/http/authenticator_spec.rb
index 7fd2bdc821..5de39523cf 100644
--- a/spec/unit/http/authenticator_spec.rb
+++ b/spec/unit/http/authenticator_spec.rb
@@ -38,6 +38,26 @@ describe Chef::HTTP::Authenticator do
to include({ "X-Ops-Server-API-Version" => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION })
end
+ context "when version_class is provided" do
+ class V0Class; extend Chef::Mixin::VersionedAPI; minimum_api_version 0; end
+ class V2Class; extend Chef::Mixin::VersionedAPI; minimum_api_version 2; end
+
+ class AuthFactoryClass
+ extend Chef::Mixin::VersionedAPIFactory
+ add_versioned_api_class V0Class
+ add_versioned_api_class V2Class
+ end
+
+ let(:class_instance) { Chef::HTTP::Authenticator.new({ version_class: AuthFactoryClass }) }
+
+ it "uses it to select the correct http version" do
+ Chef::ServerAPIVersions.instance.reset!
+ expect(AuthFactoryClass).to receive(:best_request_version).and_call_original
+ expect(class_instance.handle_request(method, url, headers, data)[2]).
+ to include({ "X-Ops-Server-API-Version" => "2" })
+ end
+ end
+
context "when api_version is set to something other than the default" do
let(:class_instance) { Chef::HTTP::Authenticator.new({ :api_version => "-10" }) }
diff --git a/spec/unit/http/basic_client_spec.rb b/spec/unit/http/basic_client_spec.rb
index 8cf63d4441..0def00a220 100644
--- a/spec/unit/http/basic_client_spec.rb
+++ b/spec/unit/http/basic_client_spec.rb
@@ -29,6 +29,26 @@ describe "HTTP Connection" do
end
end
+ describe "#initialize" do
+ it "calls .start when doing keepalives" do
+ basic_client = Chef::HTTP::BasicClient.new(uri, keepalives: true)
+ expect(basic_client).to receive(:configure_ssl)
+ net_http_mock = instance_double(Net::HTTP, proxy_address: nil, "proxy_port=" => nil, "read_timeout=" => nil, "open_timeout=" => nil)
+ expect(net_http_mock).to receive(:start).and_return(net_http_mock)
+ expect(Net::HTTP).to receive(:new).and_return(net_http_mock)
+ expect(basic_client.http_client).to eql(net_http_mock)
+ end
+
+ it "does not call .start when not doing keepalives" do
+ basic_client = Chef::HTTP::BasicClient.new(uri)
+ expect(basic_client).to receive(:configure_ssl)
+ net_http_mock = instance_double(Net::HTTP, proxy_address: nil, "proxy_port=" => nil, "read_timeout=" => nil, "open_timeout=" => nil)
+ expect(net_http_mock).not_to receive(:start)
+ expect(Net::HTTP).to receive(:new).and_return(net_http_mock)
+ expect(basic_client.http_client).to eql(net_http_mock)
+ end
+ end
+
describe "#build_http_client" do
it "should build an http client" do
subject.build_http_client
diff --git a/spec/unit/http/socketless_chef_zero_client_spec.rb b/spec/unit/http/socketless_chef_zero_client_spec.rb
index 637e562799..4f3aed13c5 100644
--- a/spec/unit/http/socketless_chef_zero_client_spec.rb
+++ b/spec/unit/http/socketless_chef_zero_client_spec.rb
@@ -132,7 +132,7 @@ describe Chef::HTTP::SocketlessChefZeroClient do
let(:method) { :GET }
let(:relative_url) { "clients" }
- let(:headers) { { "Accept" => "application/json" } }
+ let(:headers) { { "Accept" => "application/json", "X-Ops-Server-API-Version" => "2" } }
let(:body) { false }
let(:expected_rack_req) do
@@ -144,6 +144,7 @@ describe Chef::HTTP::SocketlessChefZeroClient do
"QUERY_STRING" => uri.query,
"SERVER_PORT" => uri.port,
"HTTP_HOST" => "localhost:#{uri.port}",
+ "HTTP_X_OPS_SERVER_API_VERSION" => "2",
"rack.url_scheme" => "chefzero",
"rack.input" => an_instance_of(StringIO),
}
diff --git a/spec/unit/http/validate_content_length_spec.rb b/spec/unit/http/validate_content_length_spec.rb
index 85f8d92f78..1f24acb94a 100644
--- a/spec/unit/http/validate_content_length_spec.rb
+++ b/spec/unit/http/validate_content_length_spec.rb
@@ -37,25 +37,25 @@ describe Chef::HTTP::ValidateContentLength do
let(:content_length_value) { 23 }
let(:streaming_length) { 23 }
let(:response_body) { "Thanks for checking in." }
- let(:response_headers) {
+ let(:response_headers) do
{
"content-length" => content_length_value,
}
- }
+ end
- let(:response) {
+ let(:response) do
m = double("HttpResponse", :body => response_body)
allow(m).to receive(:[]) do |key|
response_headers[key]
end
m
- }
+ end
- let(:middleware) {
+ let(:middleware) do
client = TestClient.new(url)
client.middlewares[0]
- }
+ end
def run_content_length_validation
stream_handler = middleware.stream_response_handler(response)
@@ -85,8 +85,8 @@ describe Chef::HTTP::ValidateContentLength do
before(:each) do
@original_log_level = Chef::Log.level
- Chef::Log.level = :debug
- allow(Chef::Log).to receive(:debug) do |message|
+ Chef::Log.level = :trace
+ allow(Chef::Log).to receive(:trace) do |message|
debug_stream.puts message
end
end
@@ -111,7 +111,7 @@ describe Chef::HTTP::ValidateContentLength do
describe "when running #{req_type} request" do
let(:request_type) { req_type.to_sym }
- it "should skip validation and log for debug" do
+ it "should skip validation and log for trace" do
run_content_length_validation
expect(debug_output).to include("HTTP server did not include a Content-Length header in response")
end
@@ -126,7 +126,7 @@ describe Chef::HTTP::ValidateContentLength do
describe "when running #{req_type} request" do
let(:request_type) { req_type.to_sym }
- it "should skip validation and log for debug" do
+ it "should skip validation and log for trace" do
run_content_length_validation
expect(debug_output).to include("HTTP server responded with a negative Content-Length header (-1), cannot identify truncated downloads.")
end
@@ -169,18 +169,18 @@ describe Chef::HTTP::ValidateContentLength do
end
describe "when Transfer-Encoding & Content-Length is set" do
- let(:response_headers) {
+ let(:response_headers) do
{
"content-length" => content_length_value,
"transfer-encoding" => "chunked",
}
- }
+ end
%w{direct streaming}.each do |req_type|
describe "when running #{req_type} request" do
let(:request_type) { req_type.to_sym }
- it "should skip validation and log for debug" do
+ it "should skip validation and log for trace" do
run_content_length_validation
expect(debug_output).to include("Transfer-Encoding header is set, skipping Content-Length check.")
end
diff --git a/spec/unit/http_spec.rb b/spec/unit/http_spec.rb
index 69e59ecaf8..bf873b8535 100644
--- a/spec/unit/http_spec.rb
+++ b/spec/unit/http_spec.rb
@@ -43,6 +43,20 @@ describe Chef::HTTP do
end
+ describe "#intialize" do
+ it "accepts a keepalive option and passes it to the http_client" do
+ http = Chef::HTTP.new(uri, keepalives: true)
+ expect(Chef::HTTP::BasicClient).to receive(:new).with(uri, ssl_policy: Chef::HTTP::APISSLPolicy, keepalives: true).and_call_original
+ expect(http.http_client).to be_a_kind_of(Chef::HTTP::BasicClient)
+ end
+
+ it "the default is not to use keepalives" do
+ http = Chef::HTTP.new(uri)
+ expect(Chef::HTTP::BasicClient).to receive(:new).with(uri, ssl_policy: Chef::HTTP::APISSLPolicy, keepalives: false).and_call_original
+ expect(http.http_client).to be_a_kind_of(Chef::HTTP::BasicClient)
+ end
+ end
+
describe "create_url" do
it "should return a correctly formatted url 1/3 CHEF-5261" do
@@ -60,7 +74,7 @@ describe Chef::HTTP do
expect(http.create_url("///api/endpoint?url=http://foo.bar")).to eql(URI.parse("http://www.getchef.com/organization/org/api/endpoint?url=http://foo.bar"))
end
- # As per: https://github.com/opscode/chef/issues/2500
+ # As per: https://github.com/chef/chef/issues/2500
it "should treat scheme part of the URI in a case-insensitive manner" do
http = Chef::HTTP.allocate # Calling Chef::HTTP::new sets @url, don't want that.
expect { http.create_url("HTTP://www1.chef.io/") }.not_to raise_error
@@ -79,6 +93,15 @@ describe Chef::HTTP do
expect { http.send(:stream_to_tempfile, uri, resp) }.to raise_error("TestError")
end
+ it "accepts a tempfile" do
+ resp = Net::HTTPOK.new("1.1", 200, "OK")
+ http = Chef::HTTP.new(uri)
+ tempfile = Tempfile.open("tempy-mctempfile")
+ expect(Tempfile).not_to receive(:open)
+ expect(resp).to receive(:read_body).and_yield("conty-mccontent")
+ http.send(:stream_to_tempfile, uri, resp, tempfile)
+ expect(IO.read(tempfile.path)).to eql("conty-mccontent")
+ end
end
describe "head" do
diff --git a/spec/unit/json_compat_spec.rb b/spec/unit/json_compat_spec.rb
index 4da29fe4ec..38c2c60b2e 100644
--- a/spec/unit/json_compat_spec.rb
+++ b/spec/unit/json_compat_spec.rb
@@ -22,28 +22,6 @@ require "chef/json_compat"
describe Chef::JSONCompat do
before { Chef::Config[:treat_deprecation_warnings_as_errors] = false }
- describe "#from_json with JSON containing an existing class" do
- let(:json) { '{"json_class": "Chef::Role"}' }
-
- it "emits a deprecation warning" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = true
- expect { Chef::JSONCompat.from_json(json) }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /Auto inflation of JSON data is deprecated. Please use Chef::Role#from_hash/
- end
-
- it "returns an instance of the class instead of a Hash" do
- expect(Chef::JSONCompat.from_json(json).class).to eq Chef::Role
- end
- end
-
- describe "#from_json with JSON containing comments" do
- let(:json) { %Q{{\n/* comment */\n// comment 2\n"json_class": "Chef::Role"}} }
-
- it "returns an instance of the class instead of a Hash" do
- expect(Chef::JSONCompat.from_json(json).class).to eq Chef::Role
- end
- end
-
describe "#parse with JSON containing comments" do
let(:json) { %Q{{\n/* comment */\n// comment 2\n"json_class": "Chef::Role"}} }
@@ -52,16 +30,6 @@ describe Chef::JSONCompat do
end
end
- describe 'with JSON containing "Chef::Sandbox" as a json_class value' do
- require "chef/sandbox" # Only needed for this test
-
- let(:json) { '{"json_class": "Chef::Sandbox", "arbitrary": "data"}' }
-
- it "returns a Hash, because Chef::Sandbox is a dummy class" do
- expect(Chef::JSONCompat.from_json(json)).to eq({ "json_class" => "Chef::Sandbox", "arbitrary" => "data" })
- end
- end
-
describe "when pretty printing an object that defines #to_json" do
class Foo
def to_json(*a)
diff --git a/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb b/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb
index 734b27216d..d822fe8f98 100644
--- a/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb
+++ b/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb
@@ -29,10 +29,10 @@ describe Chef::Knife::Bootstrap::ChefVaultHandler do
let(:client) { Chef::ApiClient.new }
- let(:chef_vault_handler) {
+ let(:chef_vault_handler) do
chef_vault_handler = Chef::Knife::Bootstrap::ChefVaultHandler.new(knife_config: knife_config, ui: ui)
chef_vault_handler
- }
+ end
context "when there's no vault option" do
it "should report its not doing anything" do
diff --git a/spec/unit/knife/bootstrap/client_builder_spec.rb b/spec/unit/knife/bootstrap/client_builder_spec.rb
index 491d0ca754..97ba0fc48e 100644
--- a/spec/unit/knife/bootstrap/client_builder_spec.rb
+++ b/spec/unit/knife/bootstrap/client_builder_spec.rb
@@ -33,12 +33,12 @@ describe Chef::Knife::Bootstrap::ClientBuilder do
let(:rest) { double("Chef::ServerAPI") }
- let(:client_builder) {
+ let(:client_builder) do
client_builder = Chef::Knife::Bootstrap::ClientBuilder.new(knife_config: knife_config, chef_config: chef_config, ui: ui)
allow(client_builder).to receive(:rest).and_return(rest)
allow(client_builder).to receive(:node_name).and_return(node_name)
client_builder
- }
+ end
context "#sanity_check!" do
let(:response_404) { OpenStruct.new(:code => "404") }
diff --git a/spec/unit/knife/bootstrap_spec.rb b/spec/unit/knife/bootstrap_spec.rb
index 5f1ba9f781..624261fb8b 100644
--- a/spec/unit/knife/bootstrap_spec.rb
+++ b/spec/unit/knife/bootstrap_spec.rb
@@ -48,6 +48,35 @@ describe Chef::Knife::Bootstrap do
expect(File.basename(knife.bootstrap_template)).to eq("chef-full")
end
+ context "when using the chef-full default template" do
+ let(:rendered_template) do
+ knife.merge_configs
+ knife.render_template
+ end
+
+ it "should render client.rb" do
+ expect(rendered_template).to match("cat > /etc/chef/client.rb <<'EOP'")
+ expect(rendered_template).to match("chef_server_url \"https://localhost:443\"")
+ expect(rendered_template).to match("validation_client_name \"chef-validator\"")
+ expect(rendered_template).to match("log_location STDOUT")
+ end
+
+ it "should render first-boot.json" do
+ expect(rendered_template).to match("cat > /etc/chef/first-boot.json <<'EOP'")
+ expect(rendered_template).to match('{"run_list":\[\]}')
+ end
+
+ context "and encrypted_data_bag_secret was provided" do
+ it "should render encrypted_data_bag_secret file" do
+ expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(true)
+ expect(knife).to receive(:read_secret).and_return("secrets")
+ expect(rendered_template).to match("cat > /etc/chef/encrypted_data_bag_secret <<'EOP'")
+ expect(rendered_template).to match('{"run_list":\[\]}')
+ expect(rendered_template).to match(%r{secrets})
+ end
+ end
+ end
+
context "with --bootstrap-vault-item" do
let(:bootstrap_cli_options) { [ "--bootstrap-vault-item", "vault1:item1", "--bootstrap-vault-item", "vault1:item2", "--bootstrap-vault-item", "vault2:item1" ] }
it "sets the knife config cli option correctly" do
@@ -55,24 +84,20 @@ describe Chef::Knife::Bootstrap do
end
end
- context "with :distro and :bootstrap_template cli options" do
- let(:bootstrap_cli_options) { [ "--bootstrap-template", "my-template", "--distro", "other-template" ] }
-
- it "should select bootstrap template" do
- expect(File.basename(knife.bootstrap_template)).to eq("my-template")
+ context "with --bootstrap-preinstall-command" do
+ command = "while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1; do\n echo 'waiting for dpkg lock';\n sleep 1;\n done;"
+ let(:bootstrap_cli_options) { [ "--bootstrap-preinstall-command", command ] }
+ let(:rendered_template) do
+ knife.merge_configs
+ knife.render_template
end
- end
-
- context "with :distro and :template_file cli options" do
- let(:bootstrap_cli_options) { [ "--distro", "my-template", "--template-file", "other-template" ] }
-
- it "should select bootstrap template" do
- expect(File.basename(knife.bootstrap_template)).to eq("other-template")
+ it "configures the preinstall command in the bootstrap template correctly" do
+ expect(rendered_template).to match(%r{command})
end
end
context "with :bootstrap_template and :template_file cli options" do
- let(:bootstrap_cli_options) { [ "--bootstrap-template", "my-template", "--template-file", "other-template" ] }
+ let(:bootstrap_cli_options) { [ "--bootstrap-template", "my-template", "other-template" ] }
it "should select bootstrap template" do
expect(File.basename(knife.bootstrap_template)).to eq("my-template")
@@ -93,7 +118,7 @@ describe Chef::Knife::Bootstrap do
let(:bootstrap_template) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test.erb")) }
it "loads the given file as the template" do
- expect(Chef::Log).to receive(:debug)
+ expect(Chef::Log).to receive(:trace)
expect(knife.find_template).to eq(File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test.erb")))
end
end
@@ -206,7 +231,7 @@ describe Chef::Knife::Bootstrap do
end
end
- ["-d", "--distro", "-t", "--bootstrap-template", "--template-file"].each do |t|
+ ["-t", "--bootstrap-template"].each do |t|
context "when #{t} option is given in the command line" do
it "sets the knife :bootstrap_template config" do
knife.parse_options([t, "blahblah"])
@@ -236,11 +261,11 @@ describe Chef::Knife::Bootstrap do
end
context "with bootstrap_attribute options" do
- let(:jsonfile) {
+ let(:jsonfile) do
file = Tempfile.new (["node", ".json"])
File.open(file.path, "w") { |f| f.puts '{"foo":{"bar":"baz"}}' }
file
- }
+ end
it "should have foo => {bar => baz} in the first_boot from cli" do
knife.parse_options(["-j", '{"foo":{"bar":"baz"}}'])
@@ -478,14 +503,20 @@ describe Chef::Knife::Bootstrap do
end
context "when client_d_dir is set" do
- let(:client_d_dir) { Chef::Util::PathHelper.cleanpath(
- File.join(File.dirname(__FILE__), "../../data/client.d_00")) }
+ let(:client_d_dir) do
+ Chef::Util::PathHelper.cleanpath(
+ File.join(File.dirname(__FILE__), "../../data/client.d_00")) end
it "creates /etc/chef/client.d" do
expect(rendered_template).to match("mkdir -p /etc/chef/client\.d")
end
context "a flat directory structure" do
+ it "escapes single-quotes" do
+ expect(rendered_template).to match("cat > /etc/chef/client.d/02-strings.rb <<'EOP'")
+ expect(rendered_template).to match("something '\\\\''/foo/bar'\\\\''")
+ end
+
it "creates a file 00-foo.rb" do
expect(rendered_template).to match("cat > /etc/chef/client.d/00-foo.rb <<'EOP'")
expect(rendered_template).to match("d6f9b976-289c-4149-baf7-81e6ffecf228")
@@ -497,8 +528,9 @@ describe Chef::Knife::Bootstrap do
end
context "a nested directory structure" do
- let(:client_d_dir) { Chef::Util::PathHelper.cleanpath(
- File.join(File.dirname(__FILE__), "../../data/client.d_01")) }
+ let(:client_d_dir) do
+ Chef::Util::PathHelper.cleanpath(
+ File.join(File.dirname(__FILE__), "../../data/client.d_01")) end
it "creates a file foo/bar.rb" do
expect(rendered_template).to match("cat > /etc/chef/client.d/foo/bar.rb <<'EOP'")
expect(rendered_template).to match("1 / 0")
@@ -555,6 +587,10 @@ describe Chef::Knife::Bootstrap do
expect(knife.bootstrap_context.first_boot).to have_key(:policy_group)
end
+ it "ensures that run_list is not set in the bootstrap context" do
+ expect(knife.bootstrap_context.first_boot).to_not have_key(:run_list)
+ end
+
end
# https://github.com/chef/chef/issues/4131
@@ -647,6 +683,7 @@ describe Chef::Knife::Bootstrap do
Chef::Config[:knife][:forward_agent] = true
Chef::Config[:knife][:ssh_identity_file] = "~/.ssh/you.rsa"
Chef::Config[:knife][:ssh_gateway] = "towel.blinkenlights.nl"
+ Chef::Config[:knife][:ssh_gateway_identity] = "~/.ssh/gateway.rsa"
Chef::Config[:knife][:host_key_verify] = true
allow(knife).to receive(:render_template).and_return("")
knife.config = {}
@@ -674,6 +711,10 @@ describe Chef::Knife::Bootstrap do
expect(knife_ssh.config[:ssh_gateway]).to eq("towel.blinkenlights.nl")
end
+ it "configures the ssh gateway identity" do
+ expect(knife_ssh.config[:ssh_gateway_identity]).to eq("~/.ssh/gateway.rsa")
+ end
+
it "configures the host key verify mode" do
expect(knife_ssh.config[:host_key_verify]).to eq(true)
end
diff --git a/spec/unit/knife/client_bulk_delete_spec.rb b/spec/unit/knife/client_bulk_delete_spec.rb
index a9b18c9086..994f4d33a4 100644
--- a/spec/unit/knife/client_bulk_delete_spec.rb
+++ b/spec/unit/knife/client_bulk_delete_spec.rb
@@ -24,7 +24,7 @@ describe Chef::Knife::ClientBulkDelete do
let(:stderr_io) { StringIO.new }
let(:stderr) { stderr_io.string }
- let(:knife) {
+ let(:knife) do
k = Chef::Knife::ClientBulkDelete.new
k.name_args = name_args
k.config = option_args
@@ -33,7 +33,7 @@ describe Chef::Knife::ClientBulkDelete do
allow(k.ui).to receive(:confirm).and_return(knife_confirm)
allow(k.ui).to receive(:confirm_without_exit).and_return(knife_confirm)
k
- }
+ end
let(:name_args) { [ "." ] }
let(:option_args) { {} }
@@ -41,7 +41,7 @@ describe Chef::Knife::ClientBulkDelete do
let(:knife_confirm) { true }
let(:nonvalidator_client_names) { %w{tim dan stephen} }
- let(:nonvalidator_clients) {
+ let(:nonvalidator_clients) do
clients = Hash.new
nonvalidator_client_names.each do |client_name|
@@ -52,10 +52,10 @@ describe Chef::Knife::ClientBulkDelete do
end
clients
- }
+ end
let(:validator_client_names) { %w{myorg-validator} }
- let(:validator_clients) {
+ let(:validator_clients) do
clients = Hash.new
validator_client_names.each do |validator_client_name|
@@ -67,12 +67,12 @@ describe Chef::Knife::ClientBulkDelete do
end
clients
- }
+ end
let(:client_names) { nonvalidator_client_names + validator_client_names }
- let(:clients) {
+ let(:clients) do
nonvalidator_clients.merge(validator_clients)
- }
+ end
before(:each) do
allow(Chef::ApiClientV1).to receive(:list).and_return(clients)
diff --git a/spec/unit/knife/client_delete_spec.rb b/spec/unit/knife/client_delete_spec.rb
index 82ef902e09..b05a487d62 100644
--- a/spec/unit/knife/client_delete_spec.rb
+++ b/spec/unit/knife/client_delete_spec.rb
@@ -34,6 +34,22 @@ describe Chef::Knife::ClientDelete do
@knife.run
end
+ context "receives multiple clients" do
+ let(:clients) { %w{ adam ben charlie } }
+
+ before(:each) do
+ @knife.name_args = clients
+ end
+
+ it "deletes all clients" do
+ clients.each do |client|
+ expect(@knife).to receive(:delete_object).with(Chef::ApiClientV1, client, "client")
+ end
+
+ @knife.run
+ end
+ end
+
it "should print usage and exit when a client name is not provided" do
@knife.name_args = []
expect(@knife).to receive(:show_usage)
diff --git a/spec/unit/knife/client_edit_spec.rb b/spec/unit/knife/client_edit_spec.rb
index 584a94014c..e7c9030883 100644
--- a/spec/unit/knife/client_edit_spec.rb
+++ b/spec/unit/knife/client_edit_spec.rb
@@ -27,7 +27,7 @@ describe Chef::Knife::ClientEdit do
end
describe "run" do
- let(:data) {
+ let(:data) do
{
"name" => "adam",
"validator" => false,
@@ -35,7 +35,7 @@ describe Chef::Knife::ClientEdit do
"chef_type" => "client",
"create_key" => true,
}
- }
+ end
it "should edit the client" do
allow(Chef::ApiClientV1).to receive(:load).with("adam").and_return(data)
diff --git a/spec/unit/knife/configure_client_spec.rb b/spec/unit/knife/configure_client_spec.rb
index 192da862d7..0f83897564 100644
--- a/spec/unit/knife/configure_client_spec.rb
+++ b/spec/unit/knife/configure_client_spec.rb
@@ -33,9 +33,9 @@ describe Chef::Knife::ConfigureClient do
it "should print usage and exit when a directory is not provided" do
expect(@knife).to receive(:show_usage)
expect(@knife.ui).to receive(:fatal).with(/must provide the directory/)
- expect {
+ expect do
@knife.run
- }.to raise_error SystemExit
+ end.to raise_error SystemExit
end
describe "when specifing a directory" do
@@ -58,8 +58,6 @@ describe Chef::Knife::ConfigureClient do
it "should write out the config file" do
allow(FileUtils).to receive(:mkdir_p)
@knife.run
- expect(@client_file.string).to match /log_level\s+\:info/
- expect(@client_file.string).to match /log_location\s+STDOUT/
expect(@client_file.string).to match /chef_server_url\s+'https\:\/\/chef\.example\.com'/
expect(@client_file.string).to match /validation_client_name\s+'chef-validator'/
end
diff --git a/spec/unit/knife/configure_spec.rb b/spec/unit/knife/configure_spec.rb
index e96115c056..27bb0d9a3f 100644
--- a/spec/unit/knife/configure_spec.rb
+++ b/spec/unit/knife/configure_spec.rb
@@ -26,8 +26,7 @@ describe Chef::Knife::Configure do
let(:ohai) do
o = {}
- allow(o).to receive(:require_plugin)
- allow(o).to receive(:load_plugins)
+ allow(o).to receive(:all_plugins).with(%w{ os hostname fqdn })
o[:fqdn] = fqdn
o
end
@@ -38,7 +37,7 @@ describe Chef::Knife::Configure do
let(:default_validator_key) { "/etc/chef-server/chef-validator.pem" }
let(:default_validator_key_win32) { File.expand_path(default_validator_key) }
- let(:default_server_url) { "https://#{fqdn}:443" }
+ let(:default_server_url) { "https://#{fqdn}/organizations/myorg" }
it "asks the user for the URL of the chef server" do
@knife.ask_user_for_config
@@ -124,55 +123,12 @@ describe Chef::Knife::Configure do
end
end
- it "asks the user for the location of a chef repo" do
- @knife.ask_user_for_config
- expect(@out.string).to match(Regexp.escape("Please enter the path to a chef repository (or leave blank):"))
- expect(@knife.chef_repo).to eq("")
- end
-
- it "asks the users for the name of the validation client" do
- @knife.ask_user_for_config
- expect(@out.string).to match(Regexp.escape("Please enter the validation clientname: [chef-validator]"))
- expect(@knife.validation_client_name).to eq("chef-validator")
- end
-
- it "should not ask the users for the name of the validation client if --validation_client_name is specified" do
- @knife.config[:validation_client_name] = "my-validator"
- @knife.ask_user_for_config
- expect(@out.string).not_to match(Regexp.escape("Please enter the validation clientname:"))
- expect(@knife.validation_client_name).to eq("my-validator")
- end
-
- it "asks the users for the location of the validation key" do
- @knife.ask_user_for_config
- expect(@out.string).to match(Regexp.escape("Please enter the location of the validation key: [#{default_validator_key}]"))
- if windows?
- expect(@knife.validation_key.capitalize).to eq(default_validator_key_win32.capitalize)
- else
- expect(@knife.validation_key).to eq(default_validator_key)
- end
- end
-
- it "should not ask the users for the location of the validation key if --validation_key is specified" do
- @knife.config[:validation_key] = "/home/you/.chef/my-validation.pem"
- @knife.ask_user_for_config
- expect(@out.string).not_to match(Regexp.escape("Please enter the location of the validation key:"))
- if windows?
- expect(@knife.validation_key).to match %r{^[A-Za-z]:/home/you/\.chef/my-validation\.pem$}
- else
- expect(@knife.validation_key).to eq("/home/you/.chef/my-validation.pem")
- end
- end
-
it "should not ask the user for anything if -i and all other properties are specified" do
@knife.config[:initial] = true
@knife.config[:chef_server_url] = "http://localhost:5000"
@knife.config[:node_name] = "testnode"
@knife.config[:admin_client_name] = "my-webui"
@knife.config[:admin_client_key] = "/home/you/.chef/my-webui.pem"
- @knife.config[:validation_client_name] = "my-validator"
- @knife.config[:validation_key] = "/home/you/.chef/my-validation.pem"
- @knife.config[:repository] = ""
@knife.config[:client_key] = "/home/you/a-new-user.pem"
allow(Etc).to receive(:getlogin).and_return("a-new-user")
@@ -184,40 +140,33 @@ describe Chef::Knife::Configure do
expect(@knife.admin_client_name).to eq("my-webui")
if windows?
expect(@knife.admin_client_key).to match %r{^[A-Za-z]:/home/you/\.chef/my-webui\.pem$}
- expect(@knife.validation_key).to match %r{^[A-Za-z]:/home/you/\.chef/my-validation\.pem$}
expect(@knife.new_client_key).to match %r{^[A-Za-z]:/home/you/a-new-user\.pem$}
else
expect(@knife.admin_client_key).to eq("/home/you/.chef/my-webui.pem")
- expect(@knife.validation_key).to eq("/home/you/.chef/my-validation.pem")
expect(@knife.new_client_key).to eq("/home/you/a-new-user.pem")
end
- expect(@knife.validation_client_name).to eq("my-validator")
- expect(@knife.chef_repo).to eq("")
end
it "writes the new data to a config file" do
- allow(File).to receive(:expand_path).with("/home/you/.chef/knife.rb").and_return("/home/you/.chef/knife.rb")
+ allow(Chef::Util::PathHelper).to receive(:home).with(".chef").and_return("/home/you/.chef")
+ allow(File).to receive(:expand_path).with("/home/you/.chef/credentials").and_return("/home/you/.chef/credentials")
allow(File).to receive(:expand_path).with("/home/you/.chef/#{Etc.getlogin}.pem").and_return("/home/you/.chef/#{Etc.getlogin}.pem")
- allow(File).to receive(:expand_path).with(default_validator_key).and_return(default_validator_key)
allow(File).to receive(:expand_path).with(default_admin_key).and_return(default_admin_key)
expect(FileUtils).to receive(:mkdir_p).with("/home/you/.chef")
config_file = StringIO.new
- expect(::File).to receive(:open).with("/home/you/.chef/knife.rb", "w").and_yield config_file
+ expect(::File).to receive(:open).with("/home/you/.chef/credentials", "w").and_yield config_file
@knife.config[:repository] = "/home/you/chef-repo"
@knife.run
- expect(config_file.string).to match(/^node_name[\s]+'#{Etc.getlogin}'$/)
- expect(config_file.string).to match(%r{^client_key[\s]+'/home/you/.chef/#{Etc.getlogin}.pem'$})
- expect(config_file.string).to match(/^validation_client_name\s+'chef-validator'$/)
- expect(config_file.string).to match(%r{^validation_key\s+'#{default_validator_key}'$})
- expect(config_file.string).to match(%r{^chef_server_url\s+'#{default_server_url}'$})
- expect(config_file.string).to match(%r{cookbook_path\s+\[ '/home/you/chef-repo/cookbooks' \]})
+ expect(config_file.string).to match(/^client_name[\s]+=[\s]+'#{Etc.getlogin}'$/)
+ expect(config_file.string).to match(%r{^client_key[\s]+=[\s]+'/home/you/.chef/#{Etc.getlogin}.pem'$})
+ expect(config_file.string).to match(%r{^chef_server_url\s+=[\s]+'#{default_server_url}'$})
end
it "creates a new client when given the --initial option" do
- expect(File).to receive(:expand_path).with("/home/you/.chef/knife.rb").and_return("/home/you/.chef/knife.rb")
+ allow(Chef::Util::PathHelper).to receive(:home).with(".chef").and_return("/home/you/.chef")
+ expect(File).to receive(:expand_path).with("/home/you/.chef/credentials").and_return("/home/you/.chef/credentials")
expect(File).to receive(:expand_path).with("/home/you/.chef/a-new-user.pem").and_return("/home/you/.chef/a-new-user.pem")
- expect(File).to receive(:expand_path).with(default_validator_key).and_return(default_validator_key)
- expect(File).to receive(:expand_path).with(default_admin_key).and_return(default_admin_key)
+ allow(File).to receive(:expand_path).with(default_admin_key).and_return(default_admin_key)
Chef::Config[:node_name] = "webmonkey.example.com"
user_command = Chef::Knife::UserCreate.new
@@ -227,7 +176,7 @@ describe Chef::Knife::Configure do
allow(Chef::Knife::UserCreate).to receive(:new).and_return(user_command)
expect(FileUtils).to receive(:mkdir_p).with("/home/you/.chef")
- expect(::File).to receive(:open).with("/home/you/.chef/knife.rb", "w")
+ expect(::File).to receive(:open).with("/home/you/.chef/credentials", "w")
@knife.config[:initial] = true
@knife.config[:user_password] = "blah"
@knife.run
diff --git a/spec/unit/knife/cookbook_create_spec.rb b/spec/unit/knife/cookbook_create_spec.rb
index 5e343f3db1..ef86a0cd2b 100644
--- a/spec/unit/knife/cookbook_create_spec.rb
+++ b/spec/unit/knife/cookbook_create_spec.rb
@@ -22,6 +22,7 @@ require "tmpdir"
describe Chef::Knife::CookbookCreate do
before(:each) do
Chef::Config[:node_name] = "webmonkey.example.com"
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
@knife = Chef::Knife::CookbookCreate.new
@knife.config = {}
@knife.name_args = ["foobar"]
@@ -33,228 +34,9 @@ describe Chef::Knife::CookbookCreate do
# Fixes CHEF-2579
it "should expand the path of the cookbook directory" do
- expect(File).to receive(:expand_path).with("~/tmp/monkeypants")
- @knife.config = { :cookbook_path => "~/tmp/monkeypants" }
- allow(@knife).to receive(:create_cookbook)
- allow(@knife).to receive(:create_readme)
- allow(@knife).to receive(:create_changelog)
- allow(@knife).to receive(:create_metadata)
+ expect(Chef::Log).to receive(:fatal).with("knife cookbook create has been removed. Please use `chef generate cookbook` from the ChefDK")
@knife.run
end
- it "should create a new cookbook with default values to copyright name, email, readme format and license if those are not supplied" do
- @dir = Dir.tmpdir
- @knife.config = { :cookbook_path => @dir }
- expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "YOUR_COMPANY_NAME", "none")
- expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md")
- expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first)
- expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "YOUR_COMPANY_NAME", "YOUR_EMAIL", "none", "md")
- @knife.run
- end
-
- it "should create a new cookbook with specified company name in the copyright section if one is specified" do
- @dir = Dir.tmpdir
- @knife.config = {
- :cookbook_path => @dir,
- :cookbook_copyright => "Chef Software, Inc.",
- }
- @knife.name_args = ["foobar"]
- expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "none")
- expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md")
- expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first)
- expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "YOUR_EMAIL", "none", "md")
- @knife.run
- end
-
- it "should create a new cookbook with specified copyright name and email if they are specified" do
- @dir = Dir.tmpdir
- @knife.config = {
- :cookbook_path => @dir,
- :cookbook_copyright => "Chef Software, Inc.",
- :cookbook_email => "test@chef.io",
- }
- @knife.name_args = ["foobar"]
- expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "none")
- expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md")
- expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first)
- expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "none", "md")
- @knife.run
- end
-
- it "should create a new cookbook with specified copyright name and email and license information (true) if they are specified" do
- @dir = Dir.tmpdir
- @knife.config = {
- :cookbook_path => @dir,
- :cookbook_copyright => "Chef Software, Inc.",
- :cookbook_email => "test@chef.io",
- :cookbook_license => "apachev2",
- }
- @knife.name_args = ["foobar"]
- expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "apachev2")
- expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md")
- expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first)
- expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "apachev2", "md")
- @knife.run
- end
-
- it "should create a new cookbook with specified copyright name and email and license information (false) if they are specified" do
- @dir = Dir.tmpdir
- @knife.config = {
- :cookbook_path => @dir,
- :cookbook_copyright => "Chef Software, Inc.",
- :cookbook_email => "test@chef.io",
- :cookbook_license => false,
- }
- @knife.name_args = ["foobar"]
- expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "none")
- expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md")
- expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first)
- expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "none", "md")
- @knife.run
- end
-
- it "should create a new cookbook with specified copyright name and email and license information ('false' as string) if they are specified" do
- @dir = Dir.tmpdir
- @knife.config = {
- :cookbook_path => @dir,
- :cookbook_copyright => "Chef Software, Inc.",
- :cookbook_email => "test@chef.io",
- :cookbook_license => "false",
- }
- @knife.name_args = ["foobar"]
- expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "none")
- expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md")
- expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first)
- expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "none", "md")
- @knife.run
- end
-
- it "should allow specifying a gpl2 license" do
- @dir = Dir.tmpdir
- @knife.config = {
- :cookbook_path => @dir,
- :cookbook_copyright => "Chef Software, Inc.",
- :cookbook_email => "test@chef.io",
- :cookbook_license => "gplv2",
- }
- @knife.name_args = ["foobar"]
- expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "gplv2")
- expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md")
- expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first)
- expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "gplv2", "md")
- @knife.run
- end
-
- it "should allow specifying a gplv3 license" do
- @dir = Dir.tmpdir
- @knife.config = {
- :cookbook_path => @dir,
- :cookbook_copyright => "Chef Software, Inc.",
- :cookbook_email => "test@chef.io",
- :cookbook_license => "gplv3",
- }
- @knife.name_args = ["foobar"]
- expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "gplv3")
- expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md")
- expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first)
- expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "gplv3", "md")
- @knife.run
- end
-
- it "should allow specifying the mit license" do
- @dir = Dir.tmpdir
- @knife.config = {
- :cookbook_path => @dir,
- :cookbook_copyright => "Chef Software, Inc.",
- :cookbook_email => "test@chef.io",
- :cookbook_license => "mit",
- }
- @knife.name_args = ["foobar"]
- expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "mit")
- expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md")
- expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first)
- expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "mit", "md")
- @knife.run
- end
-
- it "should allow specifying the rdoc readme format" do
- @dir = Dir.tmpdir
- @knife.config = {
- :cookbook_path => @dir,
- :cookbook_copyright => "Chef Software, Inc.",
- :cookbook_email => "test@chef.io",
- :cookbook_license => "mit",
- :readme_format => "rdoc",
- }
- @knife.name_args = ["foobar"]
- expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "mit")
- expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "rdoc")
- expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first)
- expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "mit", "rdoc")
- @knife.run
- end
-
- it "should allow specifying the md readme format" do
- @dir = Dir.tmpdir
- @knife.config = {
- :cookbook_path => @dir,
- :cookbook_copyright => "Chef Software, Inc.",
- :cookbook_email => "test@chef.io",
- :cookbook_license => "mit",
- :readme_format => "mkd",
- }
- @knife.name_args = ["foobar"]
- expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "mit")
- expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "mkd")
- expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first)
- expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "mit", "mkd")
- @knife.run
- end
-
- it "should allow specifying the txt readme format" do
- @dir = Dir.tmpdir
- @knife.config = {
- :cookbook_path => @dir,
- :cookbook_copyright => "Chef Software, Inc.",
- :cookbook_email => "test@chef.io",
- :cookbook_license => "mit",
- :readme_format => "txt",
- }
- @knife.name_args = ["foobar"]
- expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "mit")
- expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "txt")
- expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first)
- expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "mit", "txt")
- @knife.run
- end
-
- it "should allow specifying an arbitrary readme format" do
- @dir = Dir.tmpdir
- @knife.config = {
- :cookbook_path => @dir,
- :cookbook_copyright => "Chef Software, Inc.",
- :cookbook_email => "test@chef.io",
- :cookbook_license => "mit",
- :readme_format => "foo",
- }
- @knife.name_args = ["foobar"]
- expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "mit")
- expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "foo")
- expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first)
- expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "mit", "foo")
- @knife.run
- end
-
- context "when the cookbooks path is set to nil" do
- before do
- Chef::Config[:cookbook_path] = nil
- end
-
- it "should throw an argument error" do
- @dir = Dir.tmpdir
- expect { @knife.run }.to raise_error(ArgumentError)
- end
- end
-
end
end
diff --git a/spec/unit/knife/cookbook_download_spec.rb b/spec/unit/knife/cookbook_download_spec.rb
index 38a4974774..1fb995f71d 100644
--- a/spec/unit/knife/cookbook_download_spec.rb
+++ b/spec/unit/knife/cookbook_download_spec.rb
@@ -47,44 +47,62 @@ describe Chef::Knife::CookbookDownload do
@rest_mock = double("rest")
allow(@knife).to receive(:rest).and_return(@rest_mock)
- @manifest_data = {
- :recipes => [
- { "path" => "recipes/foo.rb",
- "url" => "http://example.org/files/foo.rb" },
- { "path" => "recipes/bar.rb",
- "url" => "http://example.org/files/bar.rb" },
- ],
- :templates => [
- { "path" => "templates/default/foo.erb",
- "url" => "http://example.org/files/foo.erb" },
- { "path" => "templates/default/bar.erb",
- "url" => "http://example.org/files/bar.erb" },
- ],
- :attributes => [
- { "path" => "attributes/default.rb",
- "url" => "http://example.org/files/default.rb" },
+ expect(Chef::CookbookVersion).to receive(:load).with("foobar", "1.0.0").
+ and_return(cookbook)
+ end
+
+ let(:manifest_data) do
+ {
+ :all_files => [
+ {
+ "path" => "recipes/foo.rb",
+ "name" => "recipes/foo.rb",
+ "url" => "http://example.org/files/foo.rb",
+ },
+ {
+ "path" => "recipes/bar.rb",
+ "name" => "recipes/bar.rb",
+ "url" => "http://example.org/files/bar.rb",
+ },
+ {
+ "path" => "templates/default/foo.erb",
+ "name" => "templates/foo.erb",
+ "url" => "http://example.org/files/foo.erb",
+ },
+ {
+ "path" => "templates/default/bar.erb",
+ "name" => "templates/bar.erb",
+ "url" => "http://example.org/files/bar.erb",
+ },
+ {
+ "path" => "attributes/default.rb",
+ "name" => "attributes/default.rb",
+ "url" => "http://example.org/files/default.rb",
+ },
],
}
+ end
- @cookbook_mock = double("cookbook")
- allow(@cookbook_mock).to receive(:version).and_return("1.0.0")
- allow(@cookbook_mock).to receive(:manifest).and_return(@manifest_data)
- expect(Chef::CookbookVersion).to receive(:load).with("foobar", "1.0.0").
- and_return(@cookbook_mock)
+ let (:cookbook) do
+ cb = Chef::CookbookVersion.new("foobar")
+ cb.version = "1.0.0"
+ cb.manifest = manifest_data
+ cb
end
- it "should determine which version if one was not explicitly specified" do
- allow(@cookbook_mock).to receive(:manifest).and_return({})
- expect(@knife).to receive(:determine_version).and_return("1.0.0")
- expect(File).to receive(:exists?).with("/var/tmp/chef/foobar-1.0.0").and_return(false)
- allow(Chef::CookbookVersion).to receive(:COOKBOOK_SEGEMENTS).and_return([])
- @knife.run
+ describe "and no version" do
+ let (:manifest_data) { { all_files: [] } }
+ it "should determine which version to download" do
+ expect(@knife).to receive(:determine_version).and_return("1.0.0")
+ expect(File).to receive(:exists?).with("/var/tmp/chef/foobar-1.0.0").and_return(false)
+ @knife.run
+ end
end
describe "and a version" do
before(:each) do
@knife.name_args << "1.0.0"
- @files = @manifest_data.values.map { |v| v.map { |i| i["path"] } }.flatten.uniq
+ @files = manifest_data.values.map { |v| v.map { |i| i["path"] } }.flatten.uniq
@files_mocks = {}
@files.map { |f| File.basename(f) }.flatten.uniq.each do |f|
@files_mocks[f] = double("#{f}_mock")
diff --git a/spec/unit/knife/cookbook_metadata_spec.rb b/spec/unit/knife/cookbook_metadata_spec.rb
index 0e3fccfa7e..c19fc5ae2d 100644
--- a/spec/unit/knife/cookbook_metadata_spec.rb
+++ b/spec/unit/knife/cookbook_metadata_spec.rb
@@ -19,63 +19,96 @@
require "spec_helper"
describe Chef::Knife::CookbookMetadata do
+ let(:knife) do
+ knife = Chef::Knife::CookbookMetadata.new
+ knife.name_args = ["foobar"]
+ knife
+ end
+
+ let(:cookbook_dir) { Dir.mktmpdir }
+
+ let(:stdout) { StringIO.new }
+
+ let(:stderr) { StringIO.new }
+
before(:each) do
- @knife = Chef::Knife::CookbookMetadata.new
- @knife.name_args = ["foobar"]
- @cookbook_dir = Dir.mktmpdir
- @json_data = '{ "version": "1.0.0" }'
- @stdout = StringIO.new
- @stderr = StringIO.new
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
- allow(@knife.ui).to receive(:stderr).and_return(@stderr)
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
+ allow(knife.ui).to receive(:stderr).and_return(stderr)
+ end
+
+ def create_metadata_rb(**kwargs)
+ name = kwargs[:name]
+ Dir.mkdir("#{cookbook_dir}/#{name}")
+ File.open("#{cookbook_dir}/#{name}/metadata.rb", "w+") do |f|
+ kwargs.each do |key, value|
+ if value.is_a?(Array)
+ f.puts "#{key} #{value.map { |v| "\"#{v}\"" }.join(", ")}"
+ else
+ f.puts "#{key} \"#{value}\""
+ end
+ end
+ end
+ end
+
+ def create_metadata_json(**kwargs)
+ name = kwargs[:name]
+ Dir.mkdir("#{cookbook_dir}/#{name}")
+ File.open("#{cookbook_dir}/#{name}/metadata.json", "w+") do |f|
+ f.write(FFI_Yajl::Encoder.encode(kwargs))
+ end
+ end
+
+ def create_invalid_json
+ Dir.mkdir("#{cookbook_dir}/foobar")
+ File.open("#{cookbook_dir}/foobar/metadata.json", "w+") do |f|
+ f.write <<-EOH
+ { "version": "1.0.0", {ImInvalid}}
+ EOH
+ end
end
describe "run" do
it "should print an error and exit if a cookbook name was not provided" do
- @knife.name_args = []
- expect(@knife.ui).to receive(:error).with(/you must specify the cookbook.+use the --all/i)
- expect { @knife.run }.to raise_error(SystemExit)
+ knife.name_args = []
+ expect(knife.ui).to receive(:error).with(/you must specify the cookbook.+use the --all/i)
+ expect { knife.run }.to raise_error(SystemExit)
end
it "should print an error and exit if an empty cookbook name was provided" do
- @knife.name_args = [""]
- expect(@knife.ui).to receive(:error).with(/you must specify the cookbook.+use the --all/i)
- expect { @knife.run }.to raise_error(SystemExit)
+ knife.name_args = [""]
+ expect(knife.ui).to receive(:error).with(/you must specify the cookbook.+use the --all/i)
+ expect { knife.run }.to raise_error(SystemExit)
end
it "should generate the metadata for the cookbook" do
- expect(@knife).to receive(:generate_metadata).with("foobar")
- @knife.run
+ expect(knife).to receive(:generate_metadata).with("foobar")
+ knife.run
end
describe "with -a or --all" do
before(:each) do
- @knife.config[:all] = true
- @foo = Chef::CookbookVersion.new("foo", "/tmp/blah")
- @foo.version = "1.0.0"
- @bar = Chef::CookbookVersion.new("bar", "/tmp/blah")
- @bar.version = "2.0.0"
- @cookbook_loader = {
- "foo" => @foo,
- "bar" => @bar,
- }
- expect(@cookbook_loader).to receive(:load_cookbooks).and_return(@cookbook_loader)
- expect(@knife).to receive(:generate_metadata).with("foo")
- expect(@knife).to receive(:generate_metadata).with("bar")
+ Chef::Config[:cookbook_path] = cookbook_dir
+ knife.config[:all] = true
+ create_metadata_rb(name: "foo", version: "1.0.0")
+ create_metadata_rb(name: "bar", version: "2.0.0")
+ expect(knife).to receive(:generate_metadata).with("foo").and_call_original
+ expect(knife).to receive(:generate_metadata).with("bar").and_call_original
end
it "should generate the metadata for each cookbook" do
- Chef::Config[:cookbook_path] = @cookbook_dir
- expect(Chef::CookbookLoader).to receive(:new).with(@cookbook_dir).and_return(@cookbook_loader)
- @knife.run
+ expect(Chef::CookbookLoader).to receive(:new).with(cookbook_dir).and_call_original
+ knife.run
+ expect(stderr.string).to match /generating metadata for foo from #{cookbook_dir}\/foo\/metadata\.rb/im
+ expect(stderr.string).to match /generating metadata for bar from #{cookbook_dir}\/bar\/metadata\.rb/im
end
- describe "and with -o or --cookbook-path" do
- it "should look in the provided path and generate cookbook metadata" do
- @knife.config[:cookbook_path] = "/opt/chef/cookbooks"
- expect(Chef::CookbookLoader).to receive(:new).with("/opt/chef/cookbooks").and_return(@cookbook_loader)
- @knife.run
- end
+ it "with -o or --cookbook_path should look in the provided path and generate cookbook metadata" do
+ Chef::Config[:cookbook_path] = "/dev/null"
+ knife.config[:cookbook_path] = cookbook_dir
+ expect(Chef::CookbookLoader).to receive(:new).with(cookbook_dir).and_call_original
+ knife.run
+ expect(stderr.string).to match /generating metadata for foo from #{cookbook_dir}\/foo\/metadata\.rb/im
+ expect(stderr.string).to match /generating metadata for bar from #{cookbook_dir}\/bar\/metadata\.rb/im
end
end
@@ -83,97 +116,67 @@ describe Chef::Knife::CookbookMetadata do
describe "generate_metadata" do
before(:each) do
- @knife.config[:cookbook_path] = @cookbook_dir
- allow(File).to receive(:expand_path).with("#{@cookbook_dir}/foobar/metadata.rb").
- and_return("#{@cookbook_dir}/foobar/metadata.rb")
+ Chef::Config[:cookbook_path] = cookbook_dir
end
it "should generate the metadata from metadata.rb if it exists" do
- expect(File).to receive(:exists?).with("#{@cookbook_dir}/foobar/metadata.rb").
- and_return(true)
- expect(@knife).to receive(:generate_metadata_from_file).with("foobar", "#{@cookbook_dir}/foobar/metadata.rb")
- @knife.run
+ create_metadata_rb(name: "foobar", version: "1.0.0")
+ expect(knife).to receive(:generate_metadata_from_file).with("foobar", "#{cookbook_dir}/foobar/metadata.rb").and_call_original
+ knife.run
+ expect(File.exist?("#{cookbook_dir}/foobar/metadata.json")).to be true
+ json = FFI_Yajl::Parser.parse(IO.read("#{cookbook_dir}/foobar/metadata.json"))
+ expect(json["name"]).to eql("foobar")
+ expect(json["version"]).to eql("1.0.0")
end
it "should validate the metadata json if metadata.rb does not exist" do
- expect(File).to receive(:exists?).with("#{@cookbook_dir}/foobar/metadata.rb").
- and_return(false)
- expect(@knife).to receive(:validate_metadata_json).with(@cookbook_dir, "foobar")
- @knife.run
+ create_metadata_json(name: "foobar", version: "1.0.0")
+ expect(knife).to receive(:validate_metadata_json).with(cookbook_dir, "foobar").and_call_original
+ knife.run
end
end
- describe "generate_metadata_from_file" do
+ describe "validation errors" do
before(:each) do
- @metadata_mock = double("metadata")
- @json_file_mock = double("json_file")
- end
-
- it "should generate the metatdata json from metatdata.rb" do
- allow(Chef::Cookbook::Metadata).to receive(:new).and_return(@metadata_mock)
- expect(@metadata_mock).to receive(:name).with("foobar")
- expect(@metadata_mock).to receive(:from_file).with("#{@cookbook_dir}/foobar/metadata.rb")
- expect(File).to receive(:open).with("#{@cookbook_dir}/foobar/metadata.json", "w").
- and_yield(@json_file_mock)
- expect(@json_file_mock).to receive(:write).with(@json_data)
- expect(Chef::JSONCompat).to receive(:to_json_pretty).with(@metadata_mock).
- and_return(@json_data)
- @knife.generate_metadata_from_file("foobar", "#{@cookbook_dir}/foobar/metadata.rb")
- expect(@stderr.string).to match /generating metadata for foobar from #{@cookbook_dir}\/foobar\/metadata\.rb/im
- end
-
- { Chef::Exceptions::ObsoleteDependencySyntax => "obsolote dependency",
- Chef::Exceptions::InvalidVersionConstraint => "invalid version constraint",
- }.each_pair do |klass, description|
- it "should print an error and exit when an #{description} syntax exception is encountered" do
- exception = klass.new("#{description} blah")
- allow(Chef::Cookbook::Metadata).to receive(:new).and_raise(exception)
- expect {
- @knife.generate_metadata_from_file("foobar", "#{@cookbook_dir}/foobar/metadata.rb")
- }.to raise_error(SystemExit)
- expect(@stderr.string).to match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im
- expect(@stderr.string).to match /in #{@cookbook_dir}\/foobar\/metadata\.rb/im
- expect(@stderr.string).to match /#{description} blah/im
- end
+ Chef::Config[:cookbook_path] = cookbook_dir
end
- end
- describe "validate_metadata_json" do
- it "should validate the metadata json" do
- expect(File).to receive(:exist?).with("#{@cookbook_dir}/foobar/metadata.json").
- and_return(true)
- expect(IO).to receive(:read).with("#{@cookbook_dir}/foobar/metadata.json").
- and_return(@json_data)
- expect(Chef::Cookbook::Metadata).to receive(:validate_json).with(@json_data)
- @knife.validate_metadata_json(@cookbook_dir, "foobar")
+ it "should fail for obsolete operators in metadata.rb" do
+ create_metadata_rb(name: "foobar", version: "1.0.0", depends: [ "foo:bar", ">> 0.2" ])
+ expect(Chef::Cookbook::Metadata).not_to receive(:validate_json)
+ expect { knife.run }.to raise_error(SystemExit)
+ expect(stderr.string).to match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im
end
- it "should not try to validate the metadata json if the file does not exist" do
- expect(File).to receive(:exist?).with("#{@cookbook_dir}/foobar/metadata.json").
- and_return(false)
- expect(IO).not_to receive(:read)
+ it "should fail for obsolete format in metadata.rb (sadly)" do
+ create_metadata_rb(name: "foobar", version: "1.0.0", depends: [ "foo:bar", "> 0.2", "< 1.0" ])
expect(Chef::Cookbook::Metadata).not_to receive(:validate_json)
- @knife.validate_metadata_json(@cookbook_dir, "foobar")
- end
-
- { Chef::Exceptions::ObsoleteDependencySyntax => "obsolote dependency",
- Chef::Exceptions::InvalidVersionConstraint => "invalid version constraint",
- }.each_pair do |klass, description|
- it "should print an error and exit when an #{description} syntax exception is encountered" do
- expect(File).to receive(:exist?).with("#{@cookbook_dir}/foobar/metadata.json").
- and_return(true)
- expect(IO).to receive(:read).with("#{@cookbook_dir}/foobar/metadata.json").
- and_return(@json_data)
- exception = klass.new("#{description} blah")
- allow(Chef::Cookbook::Metadata).to receive(:validate_json).and_raise(exception)
- expect {
- @knife.validate_metadata_json(@cookbook_dir, "foobar")
- }.to raise_error(SystemExit)
- expect(@stderr.string).to match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im
- expect(@stderr.string).to match /in #{@cookbook_dir}\/foobar\/metadata\.json/im
- expect(@stderr.string).to match /#{description} blah/im
- end
+ expect { knife.run }.to raise_error(SystemExit)
+ expect(stderr.string).to match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im
+ end
+
+ it "should fail for obsolete operators in metadata.json" do
+ create_metadata_json(name: "foobar", version: "1.0.0", dependencies: { "foo:bar" => ">> 0.2" })
+ expect { knife.run }.to raise_error(SystemExit)
+ expect(stderr.string).to match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im
end
- end
+ it "should not fail for unknown field in metadata.rb" do
+ create_metadata_rb(name: "sounders", version: "2.0.0", beats: "toronto")
+ expect(Chef::Cookbook::Metadata).not_to receive(:validate_json)
+ expect { knife.run }.not_to raise_error
+ expect(stderr.string).to eql("")
+ end
+
+ it "should not fail for unknown field in metadata.json" do
+ create_metadata_json(name: "sounders", version: "2.0.0", beats: "toronto")
+ expect { knife.run }.not_to raise_error
+ expect(stderr.string).to eql("")
+ end
+
+ it "should fail on unparsable json" do
+ create_invalid_json
+ expect { knife.run }.to raise_error(Chef::Exceptions::JSON::ParseError)
+ end
+ end
end
diff --git a/spec/unit/knife/cookbook_show_spec.rb b/spec/unit/knife/cookbook_show_spec.rb
index eeb7fef272..bd952c215f 100644
--- a/spec/unit/knife/cookbook_show_spec.rb
+++ b/spec/unit/knife/cookbook_show_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, eersion 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,30 +20,57 @@
require "spec_helper"
describe Chef::Knife::CookbookShow do
- before(:each) do
+ before do
Chef::Config[:node_name] = "webmonkey.example.com"
- @knife = Chef::Knife::CookbookShow.new
- @knife.config = {}
- @knife.name_args = [ "cookbook_name" ]
- @rest = double(Chef::ServerAPI)
- allow(@knife).to receive(:rest).and_return(@rest)
- allow(@knife).to receive(:pretty_print).and_return(true)
- allow(@knife).to receive(:output).and_return(true)
+ allow(knife).to receive(:rest).and_return(rest)
+ allow(knife).to receive(:pretty_print).and_return(true)
+ allow(knife).to receive(:output).and_return(true)
+ allow(Chef::CookbookVersion).to receive(:load).and_return(cb)
+ end
+
+ let (:knife) do
+ knife = Chef::Knife::CookbookShow.new
+ knife.config = {}
+ knife.name_args = [ "cookbook_name" ]
+ knife
+ end
+
+ let (:cb) do
+ cb = Chef::CookbookVersion.new("cookbook_name")
+ cb.manifest = manifest
+ cb
+ end
+
+ let (:rest) { double(Chef::ServerAPI) }
+
+ let (:content) { "Example recipe text" }
+
+ let (:manifest) do
+ {
+ "all_files" => [
+ {
+ :name => "recipes/default.rb",
+ :path => "recipes/default.rb",
+ :checksum => "1234",
+ :url => "http://example.org/files/default.rb",
+ },
+ ],
+ }
end
describe "run" do
describe "with 0 arguments: help" do
it "should should print usage and exit when given no arguments" do
- @knife.name_args = []
- expect(@knife).to receive(:show_usage)
- expect(@knife.ui).to receive(:fatal)
- expect { @knife.run }.to raise_error(SystemExit)
+ knife.name_args = []
+ expect(knife).to receive(:show_usage)
+ expect(knife.ui).to receive(:fatal)
+ expect { knife.run }.to raise_error(SystemExit)
end
end
describe "with 1 argument: versions" do
- before(:each) do
- @response = {
+ let (:response) do
+ {
"cookbook_name" => {
"url" => "http://url/cookbooks/cookbook_name",
"versions" => [
@@ -56,111 +83,117 @@ describe Chef::Knife::CookbookShow do
end
it "should show the raw cookbook data" do
- expect(@rest).to receive(:get).with("cookbooks/cookbook_name").and_return(@response)
- expect(@knife).to receive(:format_cookbook_list_for_display).with(@response)
- @knife.run
+ expect(rest).to receive(:get).with("cookbooks/cookbook_name").and_return(response)
+ expect(knife).to receive(:format_cookbook_list_for_display).with(response)
+ knife.run
end
it "should respect the user-supplied environment" do
- @knife.config[:environment] = "foo"
- expect(@rest).to receive(:get).with("environments/foo/cookbooks/cookbook_name").and_return(@response)
- expect(@knife).to receive(:format_cookbook_list_for_display).with(@response)
- @knife.run
+ knife.config[:environment] = "foo"
+ expect(rest).to receive(:get).with("environments/foo/cookbooks/cookbook_name").and_return(response)
+ expect(knife).to receive(:format_cookbook_list_for_display).with(response)
+ knife.run
end
end
describe "with 2 arguments: name and version" do
- before(:each) do
- @knife.name_args << "0.1.0"
- @response = { "0.1.0" => { "recipes" => { "default.rb" => "" } } }
+ before do
+ knife.name_args << "0.1.0"
+ end
+
+ let(:output) do
+ { "cookbook_name" => "cookbook_name",
+ "name" => "cookbook_name-0.0.0",
+ "frozen?" => false,
+ "version" => "0.0.0",
+ "metadata" => {
+ "name" => nil,
+ "description" => "",
+ "long_description" => "",
+ "maintainer" => "",
+ "maintainer_email" => "",
+ "license" => "All rights reserved",
+ "platforms" => {},
+ "dependencies" => {},
+ "providing" => {},
+ "attributes" => {},
+ "recipes" => {},
+ "version" => "0.0.0",
+ "source_url" => "",
+ "issues_url" => "",
+ "privacy" => false,
+ "chef_versions" => [],
+ "ohai_versions" => [],
+ "gems" => [],
+ },
+ "recipes" =>
+ [{ "name" => "recipes/default.rb",
+ "path" => "recipes/default.rb",
+ "checksum" => "1234",
+ "url" => "http://example.org/files/default.rb" }],
+ }
end
it "should show the specific part of a cookbook" do
- expect(@rest).to receive(:get).with("cookbooks/cookbook_name/0.1.0").and_return(@response)
- expect(@knife).to receive(:output).with(@response)
- @knife.run
+ expect(Chef::CookbookVersion).to receive(:load).with("cookbook_name", "0.1.0").and_return(cb)
+ expect(knife).to receive(:output).with(output)
+ knife.run
end
end
describe "with 3 arguments: name, version, and segment" do
before(:each) do
- @knife.name_args = [ "cookbook_name", "0.1.0", "recipes" ]
- @cookbook_response = Chef::CookbookVersion.new("cookbook_name")
- @manifest = {
- "recipes" => [
- {
- :name => "default.rb",
- :path => "recipes/default.rb",
- :checksum => "1234",
- :url => "http://example.org/files/default.rb",
- },
- ],
- }
- @cookbook_response.manifest = @manifest
- @response = { "name" => "default.rb", "url" => "http://example.org/files/default.rb", "checksum" => "1234", "path" => "recipes/default.rb" }
+ knife.name_args = [ "cookbook_name", "0.1.0", "recipes" ]
end
it "should print the json of the part" do
- expect(@rest).to receive(:get).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response)
- expect(@knife).to receive(:output).with(@cookbook_response.manifest["recipes"])
- @knife.run
+ expect(Chef::CookbookVersion).to receive(:load).with("cookbook_name", "0.1.0").and_return(cb)
+ expect(knife).to receive(:output).with(cb.files_for("recipes"))
+ knife.run
end
end
describe "with 4 arguments: name, version, segment and filename" do
before(:each) do
- @knife.name_args = [ "cookbook_name", "0.1.0", "recipes", "default.rb" ]
- @cookbook_response = Chef::CookbookVersion.new("cookbook_name")
- @cookbook_response.manifest = {
- "recipes" => [
- {
- :name => "default.rb",
- :path => "recipes/default.rb",
- :checksum => "1234",
- :url => "http://example.org/files/default.rb",
- },
- ],
- }
- @response = "Example recipe text"
+ knife.name_args = [ "cookbook_name", "0.1.0", "recipes", "default.rb" ]
end
it "should print the raw result of the request (likely a file!)" do
- expect(@rest).to receive(:get).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response)
- expect(@rest).to receive(:streaming_request).with("http://example.org/files/default.rb").and_return(StringIO.new(@response))
- expect(@knife).to receive(:pretty_print).with(@response)
- @knife.run
+ expect(Chef::CookbookVersion).to receive(:load).with("cookbook_name", "0.1.0").and_return(cb)
+ expect(rest).to receive(:streaming_request).with("http://example.org/files/default.rb").and_return(StringIO.new(content))
+ expect(knife).to receive(:pretty_print).with(content)
+ knife.run
end
end
describe "with 4 arguments: name, version, segment and filename -- with specificity" do
before(:each) do
- @knife.name_args = [ "cookbook_name", "0.1.0", "files", "afile.rb" ]
- @cookbook_response = Chef::CookbookVersion.new("cookbook_name")
- @cookbook_response.manifest = {
- "files" => [
+ knife.name_args = [ "cookbook_name", "0.1.0", "files", "afile.rb" ]
+ cb.manifest = {
+ "all_files" => [
{
- :name => "afile.rb",
+ :name => "files/afile.rb",
:path => "files/host-examplehost.example.org/afile.rb",
:checksum => "1111",
:specificity => "host-examplehost.example.org",
:url => "http://example.org/files/1111",
},
{
- :name => "afile.rb",
+ :name => "files/afile.rb",
:path => "files/ubuntu-9.10/afile.rb",
:checksum => "2222",
:specificity => "ubuntu-9.10",
:url => "http://example.org/files/2222",
},
{
- :name => "afile.rb",
+ :name => "files/afile.rb",
:path => "files/ubuntu/afile.rb",
:checksum => "3333",
:specificity => "ubuntu",
:url => "http://example.org/files/3333",
},
{
- :name => "afile.rb",
+ :name => "files/afile.rb",
:path => "files/default/afile.rb",
:checksum => "4444",
:specificity => "default",
@@ -169,51 +202,50 @@ describe Chef::Knife::CookbookShow do
],
}
- @response = "Example recipe text"
end
describe "with --fqdn" do
it "should pass the fqdn" do
- @knife.config[:platform] = "example_platform"
- @knife.config[:platform_version] = "1.0"
- @knife.config[:fqdn] = "examplehost.example.org"
- expect(@rest).to receive(:get).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response)
- expect(@rest).to receive(:streaming_request).with("http://example.org/files/1111").and_return(StringIO.new(@response))
- expect(@knife).to receive(:pretty_print).with(@response)
- @knife.run
+ knife.config[:platform] = "example_platform"
+ knife.config[:platform_version] = "1.0"
+ knife.config[:fqdn] = "examplehost.example.org"
+ expect(Chef::CookbookVersion).to receive(:load).with("cookbook_name", "0.1.0").and_return(cb)
+ expect(rest).to receive(:streaming_request).with("http://example.org/files/1111").and_return(StringIO.new(content))
+ expect(knife).to receive(:pretty_print).with(content)
+ knife.run
end
end
describe "and --platform" do
it "should pass the platform" do
- @knife.config[:platform] = "ubuntu"
- @knife.config[:platform_version] = "1.0"
- @knife.config[:fqdn] = "differenthost.example.org"
- expect(@rest).to receive(:get).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response)
- expect(@rest).to receive(:streaming_request).with("http://example.org/files/3333").and_return(StringIO.new(@response))
- expect(@knife).to receive(:pretty_print).with(@response)
- @knife.run
+ knife.config[:platform] = "ubuntu"
+ knife.config[:platform_version] = "1.0"
+ knife.config[:fqdn] = "differenthost.example.org"
+ expect(Chef::CookbookVersion).to receive(:load).with("cookbook_name", "0.1.0").and_return(cb)
+ expect(rest).to receive(:streaming_request).with("http://example.org/files/3333").and_return(StringIO.new(content))
+ expect(knife).to receive(:pretty_print).with(content)
+ knife.run
end
end
describe "and --platform-version" do
it "should pass the platform" do
- @knife.config[:platform] = "ubuntu"
- @knife.config[:platform_version] = "9.10"
- @knife.config[:fqdn] = "differenthost.example.org"
- expect(@rest).to receive(:get).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response)
- expect(@rest).to receive(:streaming_request).with("http://example.org/files/2222").and_return(StringIO.new(@response))
- expect(@knife).to receive(:pretty_print).with(@response)
- @knife.run
+ knife.config[:platform] = "ubuntu"
+ knife.config[:platform_version] = "9.10"
+ knife.config[:fqdn] = "differenthost.example.org"
+ expect(Chef::CookbookVersion).to receive(:load).with("cookbook_name", "0.1.0").and_return(cb)
+ expect(rest).to receive(:streaming_request).with("http://example.org/files/2222").and_return(StringIO.new(content))
+ expect(knife).to receive(:pretty_print).with(content)
+ knife.run
end
end
describe "with none of the arguments, it should use the default" do
it "should pass them all" do
- expect(@rest).to receive(:get).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response)
- expect(@rest).to receive(:streaming_request).with("http://example.org/files/4444").and_return(StringIO.new(@response))
- expect(@knife).to receive(:pretty_print).with(@response)
- @knife.run
+ expect(Chef::CookbookVersion).to receive(:load).with("cookbook_name", "0.1.0").and_return(cb)
+ expect(rest).to receive(:streaming_request).with("http://example.org/files/4444").and_return(StringIO.new(content))
+ expect(knife).to receive(:pretty_print).with(content)
+ knife.run
end
end
diff --git a/spec/unit/knife/cookbook_site_download_spec.rb b/spec/unit/knife/cookbook_site_download_spec.rb
index d283bd417f..0ab6a8a9b4 100644
--- a/spec/unit/knife/cookbook_site_download_spec.rb
+++ b/spec/unit/knife/cookbook_site_download_spec.rb
@@ -38,6 +38,7 @@ describe Chef::Knife::CookbookSiteDownload do
expect(@noauth_rest).to receive(:get).
with("#{@cookbook_api_url}/apache2").
and_return(@current_data)
+ @knife.configure_chef
end
context "when the cookbook is deprecated and not forced" do
diff --git a/spec/unit/knife/cookbook_site_install_spec.rb b/spec/unit/knife/cookbook_site_install_spec.rb
index d60443d779..d93af10761 100644
--- a/spec/unit/knife/cookbook_site_install_spec.rb
+++ b/spec/unit/knife/cookbook_site_install_spec.rb
@@ -23,16 +23,18 @@ describe Chef::Knife::CookbookSiteInstall do
let(:stdout) { StringIO.new }
let(:stderr) { StringIO.new }
let(:downloader) { Hash.new }
- let(:repo) { double(:sanity_check => true, :reset_to_default_state => true,
- :prepare_to_import => true, :finalize_updates_to => true,
- :merge_updates_from => true) }
- let(:install_path) {
+ let(:archive) { double(Mixlib::Archive, extract: true) }
+ let(:repo) do
+ double(:sanity_check => true, :reset_to_default_state => true,
+ :prepare_to_import => true, :finalize_updates_to => true,
+ :merge_updates_from => true) end
+ let(:install_path) do
if Chef::Platform.windows?
"C:/tmp/chef"
else
"/var/tmp/chef"
end
- }
+ end
before(:each) do
require "chef/knife/core/cookbook_scm_repo"
@@ -48,6 +50,7 @@ describe Chef::Knife::CookbookSiteInstall do
allow(File).to receive(:unlink)
allow(File).to receive(:rmtree)
allow(knife).to receive(:shell_out!).and_return(true)
+ allow(Mixlib::Archive).to receive(:new).and_return(archive)
# CookbookSiteDownload Stup
allow(knife).to receive(:download_cookbook_to).and_return(downloader)
diff --git a/spec/unit/knife/cookbook_site_share_spec.rb b/spec/unit/knife/cookbook_site_share_spec.rb
index 9339114d2a..823eff8b04 100644
--- a/spec/unit/knife/cookbook_site_share_spec.rb
+++ b/spec/unit/knife/cookbook_site_share_spec.rb
@@ -83,11 +83,11 @@ describe Chef::Knife::CookbookSiteShare do
@knife.run
end
- it "should print error and exit when given only 1 argument and cannot determine category" do
+ it "should use a default category when given only 1 argument and cannot determine category" do
@knife.name_args = ["cookbook_name"]
- expect(@noauth_rest).to receive(:get).with("https://supermarket.chef.io/api/v1/cookbooks/cookbook_name").and_return(@bad_category_response)
- expect(@knife.ui).to receive(:fatal)
- expect { @knife.run }.to raise_error(SystemExit)
+ expect(@noauth_rest).to receive(:get).with("https://supermarket.chef.io/api/v1/cookbooks/cookbook_name") { raise Net::HTTPServerException.new("404 Not Found", OpenStruct.new(code: "404")) }
+ expect(@knife).to receive(:do_upload)
+ expect { @knife.run }.to_not raise_error
end
it "should print error and exit when given only 1 argument and Chef::ServerAPI throws an exception" do
@@ -199,8 +199,8 @@ describe Chef::Knife::CookbookSiteShare do
response_text = Chef::JSONCompat.to_json({ :system_error => "Your call was dropped", :reason => "There's a map for that" })
allow(@upload_response).to receive(:body).and_return(response_text)
allow(@upload_response).to receive(:code).and_return(500)
- expect(@knife.ui).to receive(:error).with(/#{Regexp.escape(response_text)}/)#.ordered
- expect(@knife.ui).to receive(:error).with(/Unknown error/)#.ordered
+ expect(@knife.ui).to receive(:error).with(/#{Regexp.escape(response_text)}/) #.ordered
+ expect(@knife.ui).to receive(:error).with(/Unknown error/) #.ordered
expect { @knife.run }.to raise_error(SystemExit)
end
diff --git a/spec/unit/knife/cookbook_test_spec.rb b/spec/unit/knife/cookbook_test_spec.rb
index f8b212e271..dd5d4f096a 100644
--- a/spec/unit/knife/cookbook_test_spec.rb
+++ b/spec/unit/knife/cookbook_test_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Stephen Delano (<stephen@chef.io>)$
# Author:: Matthew Kent (<mkent@magoazul.com>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.$
+# Copyright:: Copyright 2010-2018, Chef Software Inc.$
# Copyright:: Copyright 2010-2016, Matthew Kent
# License:: Apache License, Version 2.0
#
@@ -73,7 +73,7 @@ describe Chef::Knife::CookbookTest do
@loader[cookbook.name] = cookbook
end
allow(@knife).to receive(:cookbook_loader).and_return(@loader)
- @loader.each do |key, cookbook|
+ @loader.each_value do |cookbook|
expect(@knife).to receive(:test_cookbook).with(cookbook.name)
end
@knife.run
diff --git a/spec/unit/knife/core/bootstrap_context_spec.rb b/spec/unit/knife/core/bootstrap_context_spec.rb
index 6465e18ac9..3a32155063 100644
--- a/spec/unit/knife/core/bootstrap_context_spec.rb
+++ b/spec/unit/knife/core/bootstrap_context_spec.rb
@@ -30,6 +30,8 @@ describe Chef::Knife::Core::BootstrapContext do
let(:run_list) { Chef::RunList.new("recipe[tmux]", "role[base]") }
let(:chef_config) do
{
+ :config_log_level => "info",
+ :config_log_location => "/tmp/log",
:validation_key => File.join(CHEF_SPEC_DATA, "ssl", "private_key.pem"),
:chef_server_url => "http://chef.example.com:4444",
:validation_client_name => "chef-validator-testing",
@@ -68,18 +70,15 @@ describe Chef::Knife::Core::BootstrapContext do
it "generates the config file data" do
expected = <<-EXPECTED
-log_location STDOUT
chef_server_url "http://chef.example.com:4444"
validation_client_name "chef-validator-testing"
+log_level :info
+log_location "/tmp/log"
# Using default node name (fqdn)
EXPECTED
expect(bootstrap_context.config_content).to eq expected
end
- it "does not set a default log_level" do
- expect(bootstrap_context.config_content).not_to match(/log_level/)
- end
-
describe "alternate chef-client path" do
let(:chef_config) { { :chef_client_path => "/usr/local/bin/chef-client" } }
it "runs chef-client from another path when specified" do
@@ -214,6 +213,23 @@ EXPECTED
end
end
+ describe "fips mode" do
+ before do
+ Chef::Config[:fips] = true
+ end
+
+ it "adds the chef version check" do
+ expect(bootstrap_context.config_content).to include <<-CONFIG.gsub(/^ {8}/, "")
+ fips true
+ require "chef/version"
+ chef_version = ::Chef::VERSION.split(".")
+ unless chef_version[0].to_i > 12 || (chef_version[0].to_i == 12 && chef_version[1].to_i >= 8)
+ raise "FIPS Mode requested but not supported by this client"
+ end
+ CONFIG
+ end
+ end
+
describe "verify_api_cert" do
it "isn't set in the config_content by default" do
expect(bootstrap_context.config_content).not_to include("verify_api_cert")
@@ -254,4 +270,55 @@ EXPECTED
end
end
+ describe "#config_log_location" do
+ context "when config_log_location is nil" do
+ let(:chef_config) { { :config_log_location => nil } }
+ it "sets the default config_log_location in the client.rb" do
+ expect(bootstrap_context.get_log_location).to eq "STDOUT"
+ end
+ end
+
+ context "when config_log_location is empty" do
+ let(:chef_config) { { :config_log_location => "" } }
+ it "sets the default config_log_location in the client.rb" do
+ expect(bootstrap_context.get_log_location).to eq "STDOUT"
+ end
+ end
+
+ context "when config_log_location is :win_evt" do
+ let(:chef_config) { { :config_log_location => :win_evt } }
+ it "raise error when config_log_location is :win_evt " do
+ expect { bootstrap_context.get_log_location }.to raise_error("The value :win_evt is not supported for config_log_location on Linux Platforms \n")
+ end
+ end
+
+ context "when config_log_location is :syslog" do
+ let(:chef_config) { { :config_log_location => :syslog } }
+ it "sets the config_log_location value as :syslog in the client.rb" do
+ expect(bootstrap_context.get_log_location).to eq ":syslog"
+ end
+ end
+
+ context "When config_log_location is STDOUT" do
+ let(:chef_config) { { :config_log_location => STDOUT } }
+ it "Sets the config_log_location value as STDOUT in the client.rb" do
+ expect(bootstrap_context.get_log_location).to eq "STDOUT"
+ end
+ end
+
+ context "when config_log_location is STDERR" do
+ let(:chef_config) { { :config_log_location => STDERR } }
+ it "sets the config_log_location value as STDERR in the client.rb" do
+ expect(bootstrap_context.get_log_location).to eq "STDERR"
+ end
+ end
+
+ context "when config_log_location is a path" do
+ let(:chef_config) { { :config_log_location => "/tmp/ChefLogFile" } }
+ it "sets the config_log_location path in the client.rb" do
+ expect(bootstrap_context.get_log_location).to eq "\"/tmp/ChefLogFile\""
+ end
+ end
+
+ end
end
diff --git a/spec/unit/knife/core/cookbook_scm_repo_spec.rb b/spec/unit/knife/core/cookbook_scm_repo_spec.rb
index 137bdddafb..3c16f93533 100644
--- a/spec/unit/knife/core/cookbook_scm_repo_spec.rb
+++ b/spec/unit/knife/core/cookbook_scm_repo_spec.rb
@@ -83,7 +83,7 @@ BRANCHES
it "exits when the git repo is dirty" do
@dirty_status = Mixlib::ShellOut.new
@dirty_status.stdout.replace(<<-DIRTY)
- M chef/lib/chef/knife/cookbook_site_vendor.rb
+ M chef/lib/chef/knife/cookbook_site_install.rb
DIRTY
expect(@cookbook_repo).to receive(:shell_out!).with("git status --porcelain", :cwd => @repo_path).and_return(@dirty_status)
expect { @cookbook_repo.sanity_check }.to raise_error(SystemExit)
diff --git a/spec/unit/knife/core/custom_manifest_loader_spec.rb b/spec/unit/knife/core/custom_manifest_loader_spec.rb
deleted file mode 100644
index 814ac8a027..0000000000
--- a/spec/unit/knife/core/custom_manifest_loader_spec.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-
-describe Chef::Knife::SubcommandLoader::CustomManifestLoader do
- let(:ec2_server_create_plugin) { "/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/ec2_server_create.rb" }
- let(:manifest_content) do
- { "plugins" => {
- "knife-ec2" => {
- "paths" => [
- ec2_server_create_plugin,
- ],
- },
- },
- }
- end
- let(:loader) do
- Chef::Knife::SubcommandLoader::CustomManifestLoader.new(File.join(CHEF_SPEC_DATA, "knife-site-subcommands"),
- manifest_content)
- end
-
- it "uses paths from the manifest instead of searching gems" do
- expect(Gem::Specification).not_to receive(:latest_specs).and_call_original
- expect(loader.subcommand_files).to include(ec2_server_create_plugin)
- end
-end
diff --git a/spec/unit/knife/core/gem_glob_loader_spec.rb b/spec/unit/knife/core/gem_glob_loader_spec.rb
index 69a40ebaed..689426de1b 100644
--- a/spec/unit/knife/core/gem_glob_loader_spec.rb
+++ b/spec/unit/knife/core/gem_glob_loader_spec.rb
@@ -40,7 +40,7 @@ describe Chef::Knife::SubcommandLoader::GemGlobLoader do
it "finds files installed via rubygems" do
expect(loader.find_subcommands_via_rubygems).to include("chef/knife/node_create")
- loader.find_subcommands_via_rubygems.each { |rel_path, abs_path| expect(abs_path).to match(%r{chef/knife/.+}) }
+ loader.find_subcommands_via_rubygems.each_value { |abs_path| expect(abs_path).to match(%r{chef/knife/.+}) }
end
it "finds files from latest version of installed gems" do
@@ -65,7 +65,7 @@ describe Chef::Knife::SubcommandLoader::GemGlobLoader do
it "finds files using a dirglob when rubygems is not available" do
expect(loader.find_subcommands_via_dirglob).to include("chef/knife/node_create")
- loader.find_subcommands_via_dirglob.each { |rel_path, abs_path| expect(abs_path).to match(%r{chef/knife/.+}) }
+ loader.find_subcommands_via_dirglob.each_value { |abs_path| expect(abs_path).to match(%r{chef/knife/.+}) }
end
it "finds user-specific subcommands in the user's ~/.chef directory" do
@@ -78,7 +78,7 @@ describe Chef::Knife::SubcommandLoader::GemGlobLoader do
expect(loader.site_subcommands).to include(expected_command)
end
- # https://github.com/opscode/chef-dk/issues/227
+ # https://github.com/chef/chef-dk/issues/227
#
# `knife` in ChefDK isn't from a gem install, it's directly run from a clone
# of the source, but there can be one or more versions of chef also installed
diff --git a/spec/unit/knife/core/hashed_command_loader_spec.rb b/spec/unit/knife/core/hashed_command_loader_spec.rb
index 081f9deae5..53bd81f4f7 100644
--- a/spec/unit/knife/core/hashed_command_loader_spec.rb
+++ b/spec/unit/knife/core/hashed_command_loader_spec.rb
@@ -22,7 +22,7 @@ describe Chef::Knife::SubcommandLoader::HashedCommandLoader do
allow(ChefConfig).to receive(:windows?) { false }
end
- let(:plugin_manifest) {
+ let(:plugin_manifest) do
{
"_autogenerated_command_paths" => {
"plugins_paths" => {
@@ -39,11 +39,12 @@ describe Chef::Knife::SubcommandLoader::HashedCommandLoader do
},
},
}
- }
+ end
- let(:loader) { Chef::Knife::SubcommandLoader::HashedCommandLoader.new(
+ let(:loader) do
+ Chef::Knife::SubcommandLoader::HashedCommandLoader.new(
File.join(CHEF_SPEC_DATA, "knife-site-subcommands"),
- plugin_manifest)}
+ plugin_manifest) end
describe "#list_commands" do
before do
diff --git a/spec/unit/knife/core/node_editor_spec.rb b/spec/unit/knife/core/node_editor_spec.rb
index 19af419632..a3dd63177d 100644
--- a/spec/unit/knife/core/node_editor_spec.rb
+++ b/spec/unit/knife/core/node_editor_spec.rb
@@ -41,7 +41,7 @@ describe Chef::Knife::NodeEditor do
subject { described_class.new(node, ui, config) }
- describe '#view' do
+ describe "#view" do
it "returns a Hash with only the name, chef_environment, normal, " +
"policy_name, policy_group, and run_list properties" do
expected = node_data.select do |key,|
@@ -55,13 +55,13 @@ describe Chef::Knife::NodeEditor do
context "when config[:all_attributes] == true" do
let(:config) { base_config.merge(all_attributes: true) }
- it 'returns a Hash with all of the node\'s properties' do
+ it "returns a Hash with all of the node's properties" do
expect(subject.view).to eq(node_data)
end
end
end
- describe '#apply_updates' do
+ describe "#apply_updates" do
context "when the node name is changed" do
before(:each) do
allow(ui).to receive(:warn)
@@ -100,10 +100,10 @@ describe Chef::Knife::NodeEditor do
expect(updated_node).to be_a(Chef::Node)
# Expected to have been changed
- expect(updated_node.chef_environment).to eql(updated_data["chef_environment"])
expect(updated_node.normal_attrs).to eql(updated_data["normal"])
expect(updated_node.policy_name).to eql(updated_data["policy_name"])
expect(updated_node.policy_group).to eql(updated_data["policy_group"])
+ expect(updated_node.chef_environment).to eql(updated_data["policy_group"])
expect(updated_node.run_list.map(&:to_s)).to eql(updated_data["run_list"])
# Expected not to have changed
@@ -131,7 +131,7 @@ describe Chef::Knife::NodeEditor do
updated_node = subject.apply_updates(updated_data)
expect(updated_node).to be_a(Chef::Node)
- expect(updated_node.chef_environment).to eql(updated_data["chef_environment"])
+ expect(updated_node.chef_environment).to eql(updated_data["policy_group"])
expect(updated_node.automatic_attrs).to eql(updated_data["automatic"])
expect(updated_node.normal_attrs).to eql(updated_data["normal"])
expect(updated_node.default_attrs).to eql(updated_data["default"])
@@ -143,7 +143,7 @@ describe Chef::Knife::NodeEditor do
end
end
- describe '#updated?' do
+ describe "#updated?" do
context "before the node has been edited" do
it "returns false" do
expect(subject.updated?).to be false
@@ -173,7 +173,7 @@ describe Chef::Knife::NodeEditor do
end
it "returns an array of the changed property names" do
- expect(subject.updated?).to eql %w{ normal policy_name policy_group run_list }
+ expect(subject.updated?).to eql %w{ chef_environment normal policy_name policy_group run_list }
end
end
@@ -188,9 +188,9 @@ describe Chef::Knife::NodeEditor do
subject.edit_node
end
- it 'returns an array of property names that doesn\'t include ' +
+ it "returns an array of property names that doesn't include " +
"the non-editable properties" do
- expect(subject.updated?).to eql %w{ normal policy_name policy_group run_list }
+ expect(subject.updated?).to eql %w{ chef_environment normal policy_name policy_group run_list }
end
end
end
diff --git a/spec/unit/knife/core/subcommand_loader_spec.rb b/spec/unit/knife/core/subcommand_loader_spec.rb
index b235102a0b..5db0bb73e5 100644
--- a/spec/unit/knife/core/subcommand_loader_spec.rb
+++ b/spec/unit/knife/core/subcommand_loader_spec.rb
@@ -43,12 +43,6 @@ describe Chef::Knife::SubcommandLoader do
allow(File).to receive(:read).with(File.join(home, ".chef", "plugin_manifest.json")).and_return("{ \"_autogenerated_command_paths\": {}}")
expect(Chef::Knife::SubcommandLoader.for_config(config_dir)).to be_a Chef::Knife::SubcommandLoader::HashedCommandLoader
end
-
- it "creates a CustomManifestLoader with then manifest has a key other than _autogenerated_command_paths" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- allow(File).to receive(:read).with(File.join(home, ".chef", "plugin_manifest.json")).and_return("{ \"plugins\": {}}")
- expect(Chef::Knife::SubcommandLoader.for_config(config_dir)).to be_a Chef::Knife::SubcommandLoader::CustomManifestLoader
- end
end
context "when ~/.chef/plugin_manifest.json does not exist" do
diff --git a/spec/unit/knife/core/ui_spec.rb b/spec/unit/knife/core/ui_spec.rb
index 0b9547fc1f..0b986258b7 100644
--- a/spec/unit/knife/core/ui_spec.rb
+++ b/spec/unit/knife/core/ui_spec.rb
@@ -3,7 +3,7 @@
# Author:: Tim Hinderliter (<tim@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,21 +28,29 @@ describe Chef::Knife::UI do
:verbosity => 0,
:yes => nil,
:format => "summary",
+ :field_separator => ".",
}
@ui = Chef::Knife::UI.new(@out, @err, @in, @config)
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ end
+
+ class TestObject < OpenStruct
+ def self.from_hash(hsh)
+ new(hsh)
+ end
end
describe "edit" do
ruby_for_json = { "foo" => "bar" }
+ ruby_from_json = TestObject.from_hash(ruby_for_json)
json_from_ruby = "{\n \"foo\": \"bar\"\n}"
json_from_editor = "{\n \"bar\": \"foo\"\n}"
- ruby_from_editor = { "bar" => "foo" }
+ ruby_from_editor = TestObject.from_hash({ "bar" => "foo" })
my_editor = "veeeye"
temp_path = "/tmp/bar/baz"
- let(:subject) { @ui.edit_data(ruby_for_json, parse_output) }
+ let(:subject) { @ui.edit_data(ruby_for_json, parse_output, object_class: klass) }
let(:parse_output) { false }
+ let(:klass) { nil }
context "when editing is disabled" do
before do
@@ -56,17 +64,18 @@ describe Chef::Knife::UI do
end
context "when parse_output is true" do
let(:parse_output) { true }
+ let(:klass) { TestObject }
it "returns a ruby object" do
- expect(subject).to eql(ruby_for_json)
+ expect(subject).to eql(ruby_from_json)
end
-
- it "gives a deprecation error" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = true
- expect { subject }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /Auto inflation of JSON data is deprecated./
+ context "but no object class is provided" do
+ let(:klass) { nil }
+ it "raises an error" do
+ expect { subject }.to raise_error ArgumentError,
+ /Please pass in the object class to hydrate or use #edit_hash/
+ end
end
end
-
end
context "when editing is enabled" do
@@ -93,6 +102,7 @@ describe Chef::Knife::UI do
end
context "when parse_output is true" do
let(:parse_output) { true }
+ let(:klass) { TestObject }
it "returns an edited ruby object" do
expect(subject).to eql(ruby_from_editor)
end
@@ -144,6 +154,7 @@ describe Chef::Knife::UI do
context "when parse_output is true" do
let(:parse_output) { true }
+ let(:klass) { TestObject }
it "returns an edited ruby object" do
expect(subject).to eql(ruby_from_editor)
end
@@ -377,18 +388,48 @@ EOM
end
it "should return the name attribute" do
- allow_any_instance_of(Chef::Node).to receive(:name).and_return("chef.localdomain")
input = Chef::Node.new
+ input.name("chef.localdomain")
@ui.config[:attribute] = "name"
expect(@ui.format_for_display(input)).to eq( { "chef.localdomain" => { "name" => "chef.localdomain" } })
end
+ it "should return a 'class' attribute and not the node.class" do
+ input = Chef::Node.new
+ input.default["class"] = "classy!"
+ @ui.config[:attribute] = "class"
+ expect(@ui.format_for_display(input)).to eq( { nil => { "class" => "classy!" } } )
+ end
+
+ it "should return the chef_environment attribute" do
+ input = Chef::Node.new
+ input.chef_environment = "production-partner-load-integration-preview-testing"
+ @ui.config[:attribute] = "chef_environment"
+ expect(@ui.format_for_display(input)).to eq( { nil => { "chef_environment" => "production-partner-load-integration-preview-testing" } } )
+ end
+
+ it "works with arrays" do
+ input = Chef::Node.new
+ input.default["array"] = %w{zero one two}
+ @ui.config[:attribute] = "array.1"
+ expect(@ui.format_for_display(input)).to eq( { nil => { "array.1" => "one" } } )
+ end
+
it "returns nil when given an attribute path that isn't a name or attribute" do
input = { "keys" => { "keys" => "values" }, "hi" => "ho", "id" => "sample-data-bag-item" }
non_existing_path = "nope.nada.nothingtoseehere"
@ui.config[:attribute] = non_existing_path
expect(@ui.format_for_display(input)).to eq({ "sample-data-bag-item" => { non_existing_path => nil } })
end
+
+ describe "when --field-separator is passed" do
+ it "honors that separator" do
+ input = { "keys" => { "with spaces" => { "open" => { "doors" => { "with many.dots" => "when asked" } } } } }
+ @ui.config[:field_separator] = ";"
+ @ui.config[:attribute] = "keys;with spaces;open;doors;with many.dots"
+ expect(@ui.format_for_display(input)).to eq({ nil => { "keys;with spaces;open;doors;with many.dots" => "when asked" } })
+ end
+ end
end
describe "with --run-list passed" do
@@ -500,9 +541,9 @@ EOM
shared_examples_for "confirm with negative answer" do
it "confirm should exit 3" do
- expect {
+ expect do
run_confirm
- }.to raise_error(SystemExit) { |e| expect(e.status).to eq(3) }
+ end.to raise_error(SystemExit) { |e| expect(e.status).to eq(3) }
end
it "confirm_without_exit should return false" do
diff --git a/spec/unit/knife/data_bag_create_spec.rb b/spec/unit/knife/data_bag_create_spec.rb
index b852c30401..169c6e1845 100644
--- a/spec/unit/knife/data_bag_create_spec.rb
+++ b/spec/unit/knife/data_bag_create_spec.rb
@@ -46,64 +46,130 @@ describe Chef::Knife::DataBagCreate do
allow(knife).to receive(:config).and_return(config)
end
- it "tries to create a data bag with an invalid name when given one argument" do
- knife.name_args = ["invalid&char"]
- expect(Chef::DataBag).to receive(:validate_name!).with(knife.name_args[0]).and_raise(Chef::Exceptions::InvalidDataBagName)
- expect { knife.run }.to exit_with_code(1)
+ context "when data_bag already exists" do
+ it "doesn't create a data bag" do
+ expect(knife).to receive(:create_object).and_yield(raw_hash)
+ expect(rest).to receive(:get).with("data/#{bag_name}")
+ expect(rest).to_not receive(:post).with("data", { "name" => bag_name })
+ expect(knife.ui).to receive(:info).with("Data bag #{bag_name} already exists")
+
+ knife.run
+ end
end
- context "when given one argument" do
+ context "when data_bag doesn't exist" do
before do
- knife.name_args = [bag_name]
+ # Data bag doesn't exist by default so we mock the GET request to return 404
+ exception = double("404 error", :code => "404")
+ allow(rest).to receive(:get)
+ .with("data/#{bag_name}")
+ .and_raise(Net::HTTPServerException.new("404", exception))
end
- it "creates a data bag" do
- expect(rest).to receive(:post).with("data", { "name" => bag_name })
- expect(knife.ui).to receive(:info).with("Created data_bag[#{bag_name}]")
+ it "tries to create a data bag with an invalid name when given one argument" do
+ knife.name_args = ["invalid&char"]
+ expect(Chef::DataBag).to receive(:validate_name!).with(knife.name_args[0]).and_raise(Chef::Exceptions::InvalidDataBagName)
+ expect { knife.run }.to exit_with_code(1)
+ end
- knife.run
+ it "won't create a data bag with a reserved name for search" do
+ %w{node role client environment}.each do |name|
+ knife.name_args = [name]
+ expect(Chef::DataBag).to receive(:validate_name!).with(knife.name_args[0]).and_raise(Chef::Exceptions::InvalidDataBagName)
+ expect { knife.run }.to exit_with_code(1)
+ end
end
- end
- context "no secret is specified for encryption" do
- let(:item) do
- item = Chef::DataBagItem.from_hash(raw_hash)
- item.data_bag(bag_name)
- item
+ context "when part of the name is a reserved name" do
+ before do
+ exception = double("404 error", :code => "404")
+ %w{node role client environment}.each do |name|
+ allow(rest).to receive(:get)
+ .with("data/sudoing_#{name}_admins")
+ .and_raise(Net::HTTPServerException.new("404", exception))
+ end
+ end
+
+ it "will create a data bag containing a reserved word" do
+ %w{node role client environment}.each do |name|
+ knife.name_args = ["sudoing_#{name}_admins"]
+ expect(rest).to receive(:post).with("data", { "name" => knife.name_args[0] })
+ expect(knife.ui).to receive(:info).with("Created data_bag[#{knife.name_args[0]}]")
+
+ knife.run
+ end
+ end
end
- it "creates a data bag item" do
- expect(knife).to receive(:create_object).and_yield(raw_hash)
- expect(knife).to receive(:encryption_secret_provided?).and_return(false)
- expect(rest).to receive(:post).with("data", { "name" => bag_name }).ordered
- expect(rest).to receive(:post).with("data/#{bag_name}", item).ordered
+ context "when given one argument" do
+ before do
+ knife.name_args = [bag_name]
+ end
- knife.run
+ it "creates a data bag" do
+ expect(rest).to receive(:post).with("data", { "name" => bag_name })
+ expect(knife.ui).to receive(:info).with("Created data_bag[#{bag_name}]")
+
+ knife.run
+ end
end
- end
- context "a secret is specified for encryption" do
- let(:encoded_data) { Chef::EncryptedDataBagItem.encrypt_data_bag_item(raw_hash, secret) }
+ context "when given a data bag name partially matching a reserved name for search" do
+ %w{xnode rolex xenvironmentx xclientx}.each do |name|
+ let(:bag_name) { name }
+
+ before do
+ knife.name_args = [bag_name]
+ end
+
+ it "creates a data bag named '#{name}'" do
+ expect(rest).to receive(:post).with("data", { "name" => bag_name })
+ expect(knife.ui).to receive(:info).with("Created data_bag[#{bag_name}]")
- let(:item) do
- item = Chef::DataBagItem.from_hash(encoded_data)
- item.data_bag(bag_name)
- item
+ knife.run
+ end
+ end
end
- it "creates an encrypted data bag item" do
- expect(knife).to receive(:create_object).and_yield(raw_hash)
- expect(knife).to receive(:encryption_secret_provided?).and_return(true)
- expect(knife).to receive(:read_secret).and_return(secret)
- expect(Chef::EncryptedDataBagItem)
- .to receive(:encrypt_data_bag_item)
- .with(raw_hash, secret)
- .and_return(encoded_data)
- expect(rest).to receive(:post).with("data", { "name" => bag_name }).ordered
- expect(rest).to receive(:post).with("data/#{bag_name}", item).ordered
+ context "no secret is specified for encryption" do
+ let(:item) do
+ item = Chef::DataBagItem.from_hash(raw_hash)
+ item.data_bag(bag_name)
+ item
+ end
+
+ it "creates a data bag item" do
+ expect(knife).to receive(:create_object).and_yield(raw_hash)
+ expect(knife).to receive(:encryption_secret_provided?).and_return(false)
+ expect(rest).to receive(:post).with("data", { "name" => bag_name }).ordered
+ expect(rest).to receive(:post).with("data/#{bag_name}", item).ordered
+
+ knife.run
+ end
+ end
- knife.run
+ context "a secret is specified for encryption" do
+ let(:encoded_data) { Chef::EncryptedDataBagItem.encrypt_data_bag_item(raw_hash, secret) }
+
+ let(:item) do
+ item = Chef::DataBagItem.from_hash(encoded_data)
+ item.data_bag(bag_name)
+ item
+ end
+
+ it "creates an encrypted data bag item" do
+ expect(knife).to receive(:create_object).and_yield(raw_hash)
+ expect(knife).to receive(:encryption_secret_provided?).and_return(true)
+ expect(knife).to receive(:read_secret).and_return(secret)
+ expect(Chef::EncryptedDataBagItem)
+ .to receive(:encrypt_data_bag_item)
+ .with(raw_hash, secret)
+ .and_return(encoded_data)
+ expect(rest).to receive(:post).with("data", { "name" => bag_name }).ordered
+ expect(rest).to receive(:post).with("data/#{bag_name}", item).ordered
+
+ knife.run
+ end
end
end
-
end
diff --git a/spec/unit/knife/data_bag_from_file_spec.rb b/spec/unit/knife/data_bag_from_file_spec.rb
index 0b6f389e87..3bea392ae2 100644
--- a/spec/unit/knife/data_bag_from_file_spec.rb
+++ b/spec/unit/knife/data_bag_from_file_spec.rb
@@ -52,7 +52,7 @@ describe Chef::Knife::DataBagFromFile do
k
end
- let(:tmp_dir) { Dir.mktmpdir }
+ let(:tmp_dir) { make_canonical_temp_directory }
let(:db_folder) { File.join(tmp_dir, data_bags_path, bag_name) }
let(:db_file) { Tempfile.new(["data_bag_from_file_test", ".json"], db_folder) }
let(:db_file2) { Tempfile.new(["data_bag_from_file_test2", ".json"], db_folder) }
@@ -72,11 +72,12 @@ describe Chef::Knife::DataBagFromFile do
let(:loader) { double("Knife::Core::ObjectLoader") }
let(:data_bags_path) { "data_bags" }
- let(:plain_data) { {
+ let(:plain_data) do
+ {
"id" => "item_name",
"greeting" => "hello",
"nested" => { "a1" => [1, 2, 3], "a2" => { "b1" => true } },
- } }
+ } end
let(:enc_data) { Chef::EncryptedDataBagItem.encrypt_data_bag_item(plain_data, secret) }
let(:rest) { double("Chef::ServerAPI") }
diff --git a/spec/unit/knife/data_bag_show_spec.rb b/spec/unit/knife/data_bag_show_spec.rb
index 0672b8bb33..8dd0669993 100644
--- a/spec/unit/knife/data_bag_show_spec.rb
+++ b/spec/unit/knife/data_bag_show_spec.rb
@@ -45,8 +45,9 @@ describe Chef::Knife::DataBagShow do
let(:bag_name) { "sudoing_admins" }
let(:item_name) { "ME" }
- let(:data_bag_contents) { { "id" => "id", "baz" => "http://localhost:4000/data/bag_o_data/baz",
- "qux" => "http://localhost:4000/data/bag_o_data/qux" } }
+ let(:data_bag_contents) do
+ { "id" => "id", "baz" => "http://localhost:4000/data/bag_o_data/baz",
+ "qux" => "http://localhost:4000/data/bag_o_data/qux" } end
let(:enc_hash) { Chef::EncryptedDataBagItem.encrypt_data_bag_item(data_bag_contents, secret) }
let(:data_bag) { Chef::DataBagItem.from_hash(data_bag_contents) }
let(:data_bag_with_encoded_hash) { Chef::DataBagItem.from_hash(enc_hash) }
@@ -90,12 +91,11 @@ qux: http://localhost:4000/data/bag_o_data/qux}
context "Data bag to show is not encrypted" do
before do
allow(knife).to receive(:encrypted?).and_return(false)
- expect(knife).to receive(:read_secret).exactly(0).times
end
it "displays the data bag" do
+ expect(knife).to receive(:read_secret).exactly(0).times
expect(Chef::DataBagItem).to receive(:load).with(bag_name, item_name).and_return(data_bag)
- expect(knife.ui).to receive(:warn).with("Unencrypted data bag detected, ignoring any provided secret options.")
expected = %q{baz: http://localhost:4000/data/bag_o_data/baz
id: id
@@ -103,6 +103,21 @@ qux: http://localhost:4000/data/bag_o_data/qux}
knife.run
expect(stdout.string.strip).to eq(expected)
end
+
+ context "when a secret is given" do
+ it "displays the data bag" do
+ expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(true)
+ expect(knife).to receive(:read_secret).and_return(secret)
+ expect(Chef::DataBagItem).to receive(:load).with(bag_name, item_name).and_return(data_bag)
+ expect(knife.ui).to receive(:warn).with("Unencrypted data bag detected, ignoring any provided secret options.")
+
+ expected = %q{baz: http://localhost:4000/data/bag_o_data/baz
+id: id
+qux: http://localhost:4000/data/bag_o_data/qux}
+ knife.run
+ expect(stdout.string.strip).to eq(expected)
+ end
+ end
end
it "displays the list of items in the data bag when only one @name_arg is provided" do
diff --git a/spec/unit/knife/environment_compare_spec.rb b/spec/unit/knife/environment_compare_spec.rb
index e408532884..82960f3db3 100644
--- a/spec/unit/knife/environment_compare_spec.rb
+++ b/spec/unit/knife/environment_compare_spec.rb
@@ -61,7 +61,7 @@ describe Chef::Knife::EnvironmentCompare do
it "should display only cookbooks with version constraints" do
@knife.config[:format] = "summary"
@knife.run
- @environments.each do |item, url|
+ @environments.each_key do |item|
expect(@stdout.string).to(match /#{item}/) && expect(@stdout.string.lines.count).to(be 4)
end
end
@@ -78,7 +78,7 @@ describe Chef::Knife::EnvironmentCompare do
@knife.config[:format] = "summary"
@knife.config[:mismatch] = true
@knife.run
- @constraints.each do |item, ver|
+ @constraints.each_value do |ver|
expect(@stdout.string).to match /#{ver[1]}/
end
end
@@ -96,7 +96,7 @@ describe Chef::Knife::EnvironmentCompare do
@knife.config[:format] = "summary"
@knife.config[:all] = true
@knife.run
- @constraints.each do |item, ver|
+ @constraints.each_value do |ver|
expect(@stdout.string).to match /#{ver[1]}/
end
end
diff --git a/spec/unit/knife/index_rebuild_spec.rb b/spec/unit/knife/index_rebuild_spec.rb
deleted file mode 100644
index 97a3a69155..0000000000
--- a/spec/unit/knife/index_rebuild_spec.rb
+++ /dev/null
@@ -1,125 +0,0 @@
-#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2009-2016, Daniel DeLeo
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-
-describe Chef::Knife::IndexRebuild do
-
- let(:knife) { Chef::Knife::IndexRebuild.new }
- let(:rest_client) { double(Chef::ServerAPI) }
-
- let(:stub_rest!) do
- expect(knife).to receive(:rest).and_return(rest_client)
- end
-
- before :each do
- # This keeps the test output clean
- allow(knife.ui).to receive(:stdout).and_return(StringIO.new)
- end
-
- context "#grab_api_info" do
- let(:http_not_found_response) do
- e = Net::HTTPNotFound.new("1.1", 404, "blah")
- allow(e).to receive(:[]).with("x-ops-api-info").and_return(api_header_value)
- e
- end
-
- let(:http_server_exception) do
- Net::HTTPServerException.new("404: Not Found", http_not_found_response)
- end
-
- before(:each) do
- stub_rest!
- allow(rest_client).to receive(:get).and_raise(http_server_exception)
- end
-
- context "against a Chef 11 server" do
- let(:api_header_value) { "flavor=osc;version=11.0.0;erchef=1.2.3" }
- it "retrieves API information" do
- expect(knife.grab_api_info).to eq({ "flavor" => "osc", "version" => "11.0.0", "erchef" => "1.2.3" })
- end
- end # Chef 11
-
- context "against a Chef 10 server" do
- let(:api_header_value) { nil }
- it "finds no API information" do
- expect(knife.grab_api_info).to eq({})
- end
- end # Chef 10
- end # grab_api_info
-
- context "#unsupported_version?" do
- context "with Chef 11 API metadata" do
- it "is unsupported" do
- expect(knife.unsupported_version?({ "version" => "11.0.0", "flavor" => "osc", "erchef" => "1.2.3" })).to be_truthy
- end
-
- it "only truly relies on the version being non-nil" do
- expect(knife.unsupported_version?({ "version" => "1", "flavor" => "osc", "erchef" => "1.2.3" })).to be_truthy
- end
- end
-
- context "with Chef 10 API metadata" do
- it "is supported" do
- # Chef 10 will have no metadata
- expect(knife.unsupported_version?({})).to be_falsey
- end
- end
- end # unsupported_version?
-
- context "Simulating a 'knife index rebuild' run" do
-
- before :each do
- expect(knife).to receive(:grab_api_info).and_return(api_info)
- server_specific_stubs!
- end
-
- context "against a Chef 11 server" do
- let(:api_info) do
- { "flavor" => "osc",
- "version" => "11.0.0",
- "erchef" => "1.2.3",
- }
- end
- let(:server_specific_stubs!) do
- expect(knife).to receive(:unsupported_server_message).with(api_info)
- expect(knife).to receive(:exit).with(1)
- end
-
- it "should not be allowed" do
- knife.run
- end
- end
-
- context "against a Chef 10 server" do
- let(:api_info) { {} }
- let(:server_specific_stubs!) do
- stub_rest!
- expect(rest_client).to receive(:post).with("/search/reindex", {}).and_return("representative output")
- expect(knife).not_to receive(:unsupported_server_message)
- expect(knife).to receive(:deprecated_server_message)
- expect(knife).to receive(:nag)
- expect(knife).to receive(:output).with("representative output")
- end
- it "should be allowed" do
- knife.run
- end
- end
- end
-
-end
diff --git a/spec/unit/knife/key_create_spec.rb b/spec/unit/knife/key_create_spec.rb
index 146b6a904b..5b00c6ea31 100644
--- a/spec/unit/knife/key_create_spec.rb
+++ b/spec/unit/knife/key_create_spec.rb
@@ -73,7 +73,7 @@ describe "key create commands that inherit knife" do
end
describe Chef::Knife::KeyCreate do
- let(:public_key) {
+ let(:public_key) do
"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvPo+oNPB7uuNkws0fC02
KxSwdyqPLu0fhI1pOweNKAZeEIiEz2PkybathHWy8snSXGNxsITkf3eyvIIKa8OZ
@@ -83,15 +83,15 @@ IjSmiN/ihHtlhV/VSnBJ5PzT/lRknlrJ4kACoz7Pq9jv+aAx5ft/xE9yDa2DYs0q
Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
0wIDAQAB
-----END PUBLIC KEY-----"
- }
+ end
let(:config) { Hash.new }
let(:actor) { "charmander" }
let(:ui) { instance_double("Chef::Knife::UI") }
shared_examples_for "key create run command" do
- let(:key_create_object) {
+ let(:key_create_object) do
described_class.new(actor, actor_field_name, ui, config)
- }
+ end
context "when public_key and key_name weren't passed" do
it "raises a Chef::Exceptions::KeyCommandInputError with the proper error message" do
@@ -100,11 +100,11 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
end
context "when the command is run" do
- let(:expected_hash) {
+ let(:expected_hash) do
{
actor_field_name => "charmander",
}
- }
+ end
before do
allow(File).to receive(:read).and_return(public_key)
@@ -120,14 +120,14 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
context "when a valid hash is passed" do
let(:key_name) { "charmander-key" }
let(:valid_expiration_date) { "2020-12-24T21:00:00Z" }
- let(:expected_hash) {
+ let(:expected_hash) do
{
actor_field_name => "charmander",
"public_key" => public_key,
"expiration_date" => valid_expiration_date,
"key_name" => key_name,
}
- }
+ end
before do
key_create_object.config[:public_key] = "public_key_path"
key_create_object.config[:expiration_Date] = valid_expiration_date,
@@ -141,12 +141,12 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
end
context "when public_key is passed" do
- let(:expected_hash) {
+ let(:expected_hash) do
{
actor_field_name => "charmander",
"public_key" => public_key,
}
- }
+ end
before do
key_create_object.config[:public_key] = "public_key_path"
end
@@ -158,13 +158,13 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
end # when public_key is passed
context "when public_key isn't passed and key_name is" do
- let(:expected_hash) {
+ let(:expected_hash) do
{
actor_field_name => "charmander",
"name" => "charmander-key",
"create_key" => true,
}
- }
+ end
before do
key_create_object.config[:key_name] = "charmander-key"
end
@@ -176,13 +176,13 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
end
context "when the server returns a private key" do
- let(:expected_hash) {
+ let(:expected_hash) do
{
actor_field_name => "charmander",
"public_key" => public_key,
"private_key" => "super_private",
}
- }
+ end
before do
key_create_object.config[:public_key] = "public_key_path"
diff --git a/spec/unit/knife/key_delete_spec.rb b/spec/unit/knife/key_delete_spec.rb
index 3da5a9762a..0176f3c71e 100644
--- a/spec/unit/knife/key_delete_spec.rb
+++ b/spec/unit/knife/key_delete_spec.rb
@@ -80,9 +80,9 @@ describe Chef::Knife::KeyDelete do
let(:ui) { instance_double("Chef::Knife::UI") }
shared_examples_for "key delete run command" do
- let(:key_delete_object) {
+ let(:key_delete_object) do
described_class.new(keyname, actor, actor_field_name, ui)
- }
+ end
before do
allow_any_instance_of(Chef::Key).to receive(:destroy)
diff --git a/spec/unit/knife/key_edit_spec.rb b/spec/unit/knife/key_edit_spec.rb
index 9195e97135..244d8bdcc7 100644
--- a/spec/unit/knife/key_edit_spec.rb
+++ b/spec/unit/knife/key_edit_spec.rb
@@ -75,7 +75,7 @@ describe "key edit commands that inherit knife" do
end
describe Chef::Knife::KeyEdit do
- let(:public_key) {
+ let(:public_key) do
"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvPo+oNPB7uuNkws0fC02
KxSwdyqPLu0fhI1pOweNKAZeEIiEz2PkybathHWy8snSXGNxsITkf3eyvIIKa8OZ
@@ -85,23 +85,23 @@ IjSmiN/ihHtlhV/VSnBJ5PzT/lRknlrJ4kACoz7Pq9jv+aAx5ft/xE9yDa2DYs0q
Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
0wIDAQAB
-----END PUBLIC KEY-----"
- }
+ end
let(:config) { Hash.new }
let(:actor) { "charmander" }
let(:keyname) { "charmander-key" }
let(:ui) { instance_double("Chef::Knife::UI") }
shared_examples_for "key edit run command" do
- let(:key_edit_object) {
+ let(:key_edit_object) do
described_class.new(keyname, actor, actor_field_name, ui, config)
- }
+ end
context "when the command is run" do
- let(:expected_hash) {
+ let(:expected_hash) do
{
actor_field_name => "charmander",
}
- }
+ end
let(:new_keyname) { "charizard-key" }
before do
@@ -126,12 +126,12 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
end
context "when key_name is passed" do
- let(:expected_hash) {
+ let(:expected_hash) do
{
actor_field_name => "charmander",
"name" => new_keyname,
}
- }
+ end
before do
key_edit_object.config[:key_name] = new_keyname
allow_any_instance_of(Chef::Key).to receive(:update)
@@ -155,14 +155,14 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
end
context "when public_key, key_name, and expiration_date are passed" do
- let(:expected_hash) {
+ let(:expected_hash) do
{
actor_field_name => "charmander",
"public_key" => public_key,
"name" => new_keyname,
"expiration_date" => "infinity",
}
- }
+ end
before do
key_edit_object.config[:public_key] = "this-public-key"
key_edit_object.config[:key_name] = new_keyname
@@ -177,12 +177,12 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
end
context "when create_key is passed" do
- let(:expected_hash) {
+ let(:expected_hash) do
{
actor_field_name => "charmander",
"create_key" => true,
}
- }
+ end
before do
key_edit_object.config[:create_key] = true
@@ -196,12 +196,12 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
end
context "when public_key is passed" do
- let(:expected_hash) {
+ let(:expected_hash) do
{
actor_field_name => "charmander",
"public_key" => public_key,
}
- }
+ end
before do
allow(key_edit_object).to receive(:update_key_from_hash).and_return(Chef::Key.from_hash(expected_hash))
key_edit_object.config[:public_key] = "public_key_path"
@@ -214,13 +214,13 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
end # when public_key is passed
context "when the server returns a private key" do
- let(:expected_hash) {
+ let(:expected_hash) do
{
actor_field_name => "charmander",
"public_key" => public_key,
"private_key" => "super_private",
}
- }
+ end
before do
allow(key_edit_object).to receive(:update_key_from_hash).and_return(Chef::Key.from_hash(expected_hash))
diff --git a/spec/unit/knife/key_list_spec.rb b/spec/unit/knife/key_list_spec.rb
index 2d4f0a07bb..82fd1e4a09 100644
--- a/spec/unit/knife/key_list_spec.rb
+++ b/spec/unit/knife/key_list_spec.rb
@@ -78,9 +78,9 @@ describe Chef::Knife::KeyList do
let(:ui) { instance_double("Chef::Knife::UI") }
shared_examples_for "key list run command" do
- let(:key_list_object) {
+ let(:key_list_object) do
described_class.new(actor, list_method, ui, config)
- }
+ end
before do
allow(Chef::Key).to receive(list_method).and_return(http_response)
@@ -191,26 +191,26 @@ describe Chef::Knife::KeyList do
context "when list_method is :list_by_user" do
it_should_behave_like "key list run command" do
let(:list_method) { :list_by_user }
- let(:http_response) {
+ let(:http_response) do
[
{ "uri" => "https://api.opscode.piab/users/charmander/keys/non-expired1", "name" => "non-expired1", "expired" => false },
{ "uri" => "https://api.opscode.piab/users/charmander/keys/non-expired2", "name" => "non-expired2", "expired" => false },
{ "uri" => "https://api.opscode.piab/users/mary/keys/out-of-date", "name" => "out-of-date", "expired" => true },
]
- }
+ end
end
end
context "when list_method is :list_by_client" do
it_should_behave_like "key list run command" do
let(:list_method) { :list_by_client }
- let(:http_response) {
+ let(:http_response) do
[
{ "uri" => "https://api.opscode.piab/organizations/pokemon/clients/charmander/keys/non-expired1", "name" => "non-expired1", "expired" => false },
{ "uri" => "https://api.opscode.piab/organizations/pokemon/clients/charmander/keys/non-expired2", "name" => "non-expired2", "expired" => false },
{ "uri" => "https://api.opscode.piab/organizations/pokemon/clients/mary/keys/out-of-date", "name" => "out-of-date", "expired" => true },
]
- }
+ end
end
end
end
diff --git a/spec/unit/knife/key_show_spec.rb b/spec/unit/knife/key_show_spec.rb
index c161efbe0d..139d4f91a2 100644
--- a/spec/unit/knife/key_show_spec.rb
+++ b/spec/unit/knife/key_show_spec.rb
@@ -78,19 +78,19 @@ describe Chef::Knife::KeyShow do
let(:actor) { "charmander" }
let(:keyname) { "charmander" }
let(:ui) { instance_double("Chef::Knife::UI") }
- let(:expected_hash) {
+ let(:expected_hash) do
{
actor_field_name => "charmander",
"name" => "charmander-key",
"public_key" => "some-public-key",
"expiration_date" => "infinity",
}
- }
+ end
shared_examples_for "key show run command" do
- let(:key_show_object) {
+ let(:key_show_object) do
described_class.new(keyname, actor, load_method, ui)
- }
+ end
before do
allow(key_show_object).to receive(:display_output)
diff --git a/spec/unit/knife/knife_help.rb b/spec/unit/knife/knife_help.rb
deleted file mode 100644
index 0369951511..0000000000
--- a/spec/unit/knife/knife_help.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-#
-# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-
-describe Chef::Knife::Help do
- before(:each) do
- # Perilously use the build in list even though it is dynamic so we don't get warnings about the constant
- # HELP_TOPICS = [ "foo", "bar", "knife-kittens", "ceiling-cat", "shell" ]
- @knife = Chef::Knife::Help.new
- end
-
- it "should return a list of help topics" do
- expect(@knife.help_topics).to include("knife-status")
- end
-
- it "should run man for you" do
- @knife.name_args = [ "shell" ]
- expect(@knife).to receive(:exec).with(/^man \/.*\/shell.1$/)
- @knife.run
- end
-
- it "should suggest topics" do
- @knife.name_args = [ "list" ]
- allow(@knife.ui).to receive(:msg)
- expect(@knife.ui).to receive(:info).with("Available help topics are: ")
- expect(@knife.ui).to receive(:msg).with(/knife/)
- allow(@knife).to receive(:exec)
- expect(@knife).to receive(:exit).with(1)
- @knife.run
- end
-
- describe "find_manpage_path" do
- it "should find the man page in the gem" do
- expect(@knife.find_manpage_path("shell")).to match(/distro\/common\/man\/man1\/chef-shell.1$/)
- end
-
- it "should provide the man page name if not in the gem" do
- expect(@knife.find_manpage_path("foo")).to eq("foo")
- end
- end
-
- describe "find_manpages_for_query" do
- it "should error if it does not find a match" do
- allow(@knife.ui).to receive(:error)
- allow(@knife.ui).to receive(:info)
- allow(@knife.ui).to receive(:msg)
- expect(@knife).to receive(:exit).with(1)
- expect(@knife.ui).to receive(:error).with("No help found for 'chickens'")
- expect(@knife.ui).to receive(:msg).with(/knife/)
- @knife.find_manpages_for_query("chickens")
- end
- end
-
- describe "print_help_topics" do
- it "should print the known help topics" do
- allow(@knife.ui).to receive(:msg)
- allow(@knife.ui).to receive(:info)
- expect(@knife.ui).to receive(:msg).with(/knife/)
- @knife.print_help_topics
- end
-
- it "should shorten topics prefixed by knife-" do
- allow(@knife.ui).to receive(:msg)
- allow(@knife.ui).to receive(:info)
- expect(@knife.ui).to receive(:msg).with(/node/)
- @knife.print_help_topics
- end
-
- it "should not leave topics prefixed by knife-" do
- allow(@knife.ui).to receive(:msg)
- allow(@knife.ui).to receive(:info)
- expect(@knife.ui).not_to receive(:msg).with(/knife-node/)
- @knife.print_help_topics
- end
- end
-end
diff --git a/spec/unit/knife/node_delete_spec.rb b/spec/unit/knife/node_delete_spec.rb
index d4ef32bccf..1a6b277dbb 100644
--- a/spec/unit/knife/node_delete_spec.rb
+++ b/spec/unit/knife/node_delete_spec.rb
@@ -25,12 +25,17 @@ describe Chef::Knife::NodeDelete do
@knife.config = {
:print_after => nil,
}
- @knife.name_args = [ "adam" ]
+ @knife.name_args = %w{ adam ben }
allow(@knife).to receive(:output).and_return(true)
allow(@knife).to receive(:confirm).and_return(true)
- @node = Chef::Node.new()
- allow(@node).to receive(:destroy).and_return(true)
- allow(Chef::Node).to receive(:load).and_return(@node)
+
+ @adam_node = Chef::Node.new()
+ @ben_node = Chef::Node.new()
+ allow(@ben_node).to receive(:destroy).and_return(true)
+ allow(@adam_node).to receive(:destroy).and_return(true)
+ allow(Chef::Node).to receive(:load).with("adam").and_return(@adam_node)
+ allow(Chef::Node).to receive(:load).with("ben").and_return(@ben_node)
+
@stdout = StringIO.new
allow(@knife.ui).to receive(:stdout).and_return(@stdout)
end
@@ -41,13 +46,15 @@ describe Chef::Knife::NodeDelete do
@knife.run
end
- it "should load the node" do
- expect(Chef::Node).to receive(:load).with("adam").and_return(@node)
+ it "should load the nodes" do
+ expect(Chef::Node).to receive(:load).with("adam").and_return(@adam_node)
+ expect(Chef::Node).to receive(:load).with("ben").and_return(@ben_node)
@knife.run
end
- it "should delete the node" do
- expect(@node).to receive(:destroy).and_return(@node)
+ it "should delete the nodes" do
+ expect(@adam_node).to receive(:destroy).and_return(@adam_node)
+ expect(@ben_node).to receive(:destroy).and_return(@ben_node)
@knife.run
end
@@ -59,8 +66,10 @@ describe Chef::Knife::NodeDelete do
describe "with -p or --print-after" do
it "should pretty print the node, formatted for display" do
@knife.config[:print_after] = true
- expect(@knife).to receive(:format_for_display).with(@node).and_return("poop")
- expect(@knife).to receive(:output).with("poop")
+ expect(@knife).to receive(:format_for_display).with(@adam_node).and_return("adam")
+ expect(@knife).to receive(:format_for_display).with(@ben_node).and_return("ben")
+ expect(@knife).to receive(:output).with("adam")
+ expect(@knife).to receive(:output).with("ben")
@knife.run
end
end
diff --git a/spec/unit/knife/node_edit_spec.rb b/spec/unit/knife/node_edit_spec.rb
index dedb5c949d..e5fa9317b1 100644
--- a/spec/unit/knife/node_edit_spec.rb
+++ b/spec/unit/knife/node_edit_spec.rb
@@ -23,7 +23,7 @@ describe Chef::Knife::NodeEdit do
# helper to convert the view from Chef objects into Ruby objects representing JSON
def deserialized_json_view
- actual = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json_pretty(@knife.node_editor.send(:view)))
+ Chef::JSONCompat.from_json(Chef::JSONCompat.to_json_pretty(@knife.node_editor.send(:view)))
end
before(:each) do
@@ -45,6 +45,8 @@ describe Chef::Knife::NodeEdit do
describe "after loading the node" do
before do
+ @knife.config[:all_attributes] = false
+
allow(@knife).to receive(:node).and_return(@node)
@node.automatic_attrs = { :go => :away }
@node.default_attrs = { :hide => :me }
diff --git a/spec/unit/knife/node_environment_set_spec.rb b/spec/unit/knife/node_environment_set_spec.rb
index 03fc764fd8..7ceafdad78 100644
--- a/spec/unit/knife/node_environment_set_spec.rb
+++ b/spec/unit/knife/node_environment_set_spec.rb
@@ -52,29 +52,5 @@ describe Chef::Knife::NodeEnvironmentSet do
@knife.run
end
- describe "with no environment" do
- # Set up outputs for inspection later
- before(:each) do
- @stdout = StringIO.new
- @stderr = StringIO.new
-
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
- allow(@knife.ui).to receive(:stderr).and_return(@stderr)
- end
-
- it "should exit" do
- @knife.name_args = [ "adam" ]
- expect { @knife.run }.to raise_error SystemExit
- end
-
- it "should show the user the usage and an error" do
- @knife.name_args = [ "adam" ]
-
- begin ; @knife.run ; rescue SystemExit ; end
-
- expect(@stdout.string).to eq "USAGE: knife node environment set NODE ENVIRONMENT\n"
- expect(@stderr.string).to eq "FATAL: You must specify a node name and an environment.\n"
- end
- end
end
end
diff --git a/spec/unit/knife/node_policy_set_spec.rb b/spec/unit/knife/node_policy_set_spec.rb
new file mode 100644
index 0000000000..35306937d8
--- /dev/null
+++ b/spec/unit/knife/node_policy_set_spec.rb
@@ -0,0 +1,122 @@
+#
+# Author:: Piyush Awasthi (<piyush.awasthi@chef.io>)
+# Copyright:: Copyright 2017-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the License);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Knife::NodePolicySet do
+ let(:node) do
+ node = Chef::Node.new()
+ node.name("adam")
+ node.run_list = ["role[base]"]
+ node
+ end
+
+ let(:knife) do
+ Chef::Log.logger = Logger.new(StringIO.new)
+ Chef::Config[:knife][:bootstrap_template] = bootstrap_template unless bootstrap_template.nil?
+ knife_obj = Chef::Knife::NodePolicySet.new(bootstrap_cli_options)
+ knife_obj.merge_configs
+ allow(knife_obj.ui).to receive(:stderr).and_return(stderr)
+ allow(knife_obj).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(false)
+ knife_obj
+ end
+
+ let(:stderr) { StringIO.new }
+ let(:bootstrap_template) { nil }
+ let(:bootstrap_cli_options) { [ ] }
+
+ describe "#run" do
+ context "when node_name is not given" do
+ let(:bootstrap_cli_options) { %w{ } }
+ it "returns an error that you must specify a node name" do
+ expect { knife.send(:validate_node!) }.to raise_error(SystemExit)
+ expect(stderr.string).to include("ERROR: You must specify a node name")
+ end
+ end
+
+ context "when node is given" do
+ let(:bootstrap_cli_options) { %w{ adam staging my-app } }
+ it "should load the node" do
+ expect(Chef::Node).to receive(:load).with(bootstrap_cli_options[0]).and_return(node)
+ allow(node).to receive(:save).and_return(true)
+ knife.run
+ end
+ end
+
+ context "when node not saved" do
+ let(:bootstrap_cli_options) { %w{ adam staging my-app } }
+ it "returns an error node not updated successfully" do
+ allow(Chef::Node).to receive(:load).with(bootstrap_cli_options[0]).and_return(node)
+ allow(node).to receive(:save).and_return(false)
+ knife.run
+ expect(stderr.string.strip).to eq("Error in updating node #{node.name}")
+ end
+ end
+
+ context "when the policy is set successfully on the node" do
+ let(:bootstrap_cli_options) { %w{ adam staging my-app } }
+ it "returns node updated successfully" do
+ allow(Chef::Node).to receive(:load).with(bootstrap_cli_options[0]).and_return(node)
+ allow(node).to receive(:save).and_return(true)
+ knife.run
+ expect(stderr.string.strip).to eq("Successfully set the policy on node #{node.name}")
+ end
+ end
+ end
+
+ describe "handling policy options" do
+ context "when policy_group and policy_name is not given" do
+ let(:bootstrap_cli_options) { %w{ } }
+ it "returns an error stating that policy_name and policy_group must be given together" do
+ expect { knife.send(:validate_options!) }.to raise_error(SystemExit)
+ expect(stderr.string).to include("ERROR: Policy group and name must be specified together")
+ end
+ end
+
+ context "when only policy_name is given" do
+ let(:bootstrap_cli_options) { %w{ adam staging } }
+ it "returns an error stating that policy_name and policy_group must be given together" do
+ expect { knife.send(:validate_options!) }.to raise_error(SystemExit)
+ expect(stderr.string).to include("ERROR: Policy group and name must be specified together")
+ end
+ end
+
+ context "when only policy_group is given" do
+ let(:bootstrap_cli_options) { %w{ adam my-app } }
+ it "returns an error stating that policy_name and policy_group must be given together" do
+ expect { knife.send(:validate_options!) }.to raise_error(SystemExit)
+ expect(stderr.string).to include("ERROR: Policy group and name must be specified together")
+ end
+ end
+
+ context "when policy_name and policy_group are given with no conflicting options" do
+ let(:bootstrap_cli_options) { %w{ adam staging my-app } }
+ it "passes options validation" do
+ expect { knife.send(:validate_options!) }.to_not raise_error
+ end
+
+ it "returns value set in config" do
+ allow(Chef::Node).to receive(:load).with(bootstrap_cli_options[0]).and_return(node)
+ allow(node).to receive(:save).and_return(false)
+ knife.run
+ expect(node.policy_name).to eq("my-app")
+ expect(node.policy_group).to eq("staging")
+ end
+ end
+ end
+end
diff --git a/spec/unit/knife/node_run_list_set_spec.rb b/spec/unit/knife/node_run_list_set_spec.rb
index 1bbaa7d9a5..bd55edb997 100644
--- a/spec/unit/knife/node_run_list_set_spec.rb
+++ b/spec/unit/knife/node_run_list_set_spec.rb
@@ -111,30 +111,5 @@ describe Chef::Knife::NodeRunListSet do
end
end
- describe "with no role or recipe" do
- # Set up outputs for inspection later
- before(:each) do
- @stdout = StringIO.new
- @stderr = StringIO.new
-
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
- allow(@knife.ui).to receive(:stderr).and_return(@stderr)
- end
-
- it "should exit" do
- @knife.name_args = [ "adam" ]
- expect { @knife.run }.to raise_error SystemExit
- end
-
- it "should show the user" do
- @knife.name_args = [ "adam" ]
-
- begin ; @knife.run ; rescue SystemExit ; end
-
- expect(@stdout.string).to eq "USAGE: knife node run_list set NODE ENTRIES (options)\n"
- expect(@stderr.string).to eq "FATAL: You must supply both a node name and a run list.\n"
- end
- end
-
end
end
diff --git a/spec/unit/knife/osc_user_reregister_spec.rb b/spec/unit/knife/osc_user_reregister_spec.rb
index ae6b4be9a8..de706dfa42 100644
--- a/spec/unit/knife/osc_user_reregister_spec.rb
+++ b/spec/unit/knife/osc_user_reregister_spec.rb
@@ -19,7 +19,7 @@
require "spec_helper"
# DEPRECATION NOTE
-# This code only remains to support users still operating with
+# This code only remains to support users still operating with
# Open Source Chef Server 11 and should be removed once support
# for OSC 11 ends. New development should occur in user_reregister_spec.rb.
diff --git a/spec/unit/knife/osc_user_show_spec.rb b/spec/unit/knife/osc_user_show_spec.rb
index d6efbef870..ecdb0a9fce 100644
--- a/spec/unit/knife/osc_user_show_spec.rb
+++ b/spec/unit/knife/osc_user_show_spec.rb
@@ -19,7 +19,7 @@
require "spec_helper"
# DEPRECATION NOTE
-# This code only remains to support users still operating with
+# This code only remains to support users still operating with
# Open Source Chef Server 11 and should be removed once support
# for OSC 11 ends. New development should occur user_show_spec.rb.
diff --git a/spec/unit/knife/ssh_spec.rb b/spec/unit/knife/ssh_spec.rb
index 44a133d858..9d7fcb6f9e 100644
--- a/spec/unit/knife/ssh_spec.rb
+++ b/spec/unit/knife/ssh_spec.rb
@@ -21,57 +21,64 @@ require "net/ssh"
require "net/ssh/multi"
describe Chef::Knife::Ssh do
- before(:each) do
- Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem"
- end
+ let(:query_result) { double("chef search results") }
before do
+ Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem"
@knife = Chef::Knife::Ssh.new
@knife.merge_configs
- @node_foo = Chef::Node.new
- @node_foo.automatic_attrs[:fqdn] = "foo.example.org"
- @node_foo.automatic_attrs[:ipaddress] = "10.0.0.1"
+ @node_foo = {}
+ @node_foo["fqdn"] = "foo.example.org"
+ @node_foo["ipaddress"] = "10.0.0.1"
+ @node_foo["cloud"] = {}
+
+ @node_bar = {}
+ @node_bar["fqdn"] = "bar.example.org"
+ @node_bar["ipaddress"] = "10.0.0.2"
+ @node_bar["cloud"] = {}
- @node_bar = Chef::Node.new
- @node_bar.automatic_attrs[:fqdn] = "bar.example.org"
- @node_bar.automatic_attrs[:ipaddress] = "10.0.0.2"
end
describe "#configure_session" do
context "manual is set to false (default)" do
before do
@knife.config[:manual] = false
- @query = Chef::Search::Query.new
- end
-
- def configure_query(node_array)
- allow(@query).to receive(:search).and_return([node_array])
- allow(Chef::Search::Query).to receive(:new).and_return(@query)
+ allow(query_result).to receive(:search).with(any_args).and_yield(@node_foo).and_yield(@node_bar)
+ allow(Chef::Search::Query).to receive(:new).and_return(query_result)
end
def self.should_return_specified_attributes
it "returns an array of the attributes specified on the command line OR config file, if only one is set" do
- @knife.config[:attribute] = "ipaddress"
+ @node_bar["target"] = "10.0.0.2"
+ @node_foo["target"] = "10.0.0.1"
+ @node_bar["prefix"] = "bar"
+ @node_foo["prefix"] = "foo"
+ @knife.config[:ssh_attribute] = "ipaddress"
+ @knife.config[:prefix_attribute] = "name"
Chef::Config[:knife][:ssh_attribute] = "ipaddress" # this value will be in the config file
- configure_query([@node_foo, @node_bar])
- expect(@knife).to receive(:session_from_list).with([["10.0.0.1", nil], ["10.0.0.2", nil]])
+ Chef::Config[:knife][:prefix_attribute] = "name" # this value will be in the config file
+ expect(@knife).to receive(:session_from_list).with([["10.0.0.1", nil, "foo"], ["10.0.0.2", nil, "bar"]])
@knife.configure_session
end
it "returns an array of the attributes specified on the command line even when a config value is set" do
+ @node_bar["target"] = "10.0.0.2"
+ @node_foo["target"] = "10.0.0.1"
+ @node_bar["prefix"] = "bar"
+ @node_foo["prefix"] = "foo"
Chef::Config[:knife][:ssh_attribute] = "config_file" # this value will be in the config file
- @knife.config[:attribute] = "ipaddress" # this is the value of the command line via #configure_attribute
- configure_query([@node_foo, @node_bar])
- expect(@knife).to receive(:session_from_list).with([["10.0.0.1", nil], ["10.0.0.2", nil]])
+ Chef::Config[:knife][:prefix_attribute] = "config_file" # this value will be in the config file
+ @knife.config[:ssh_attribute] = "ipaddress" # this is the value of the command line via #configure_attribute
+ @knife.config[:prefix_attribute] = "name" # this is the value of the command line via #configure_attribute
+ expect(@knife).to receive(:session_from_list).with([["10.0.0.1", nil, "foo"], ["10.0.0.2", nil, "bar"]])
@knife.configure_session
end
end
- it "searchs for and returns an array of fqdns" do
- configure_query([@node_foo, @node_bar])
+ it "searches for and returns an array of fqdns" do
expect(@knife).to receive(:session_from_list).with([
- ["foo.example.org", nil],
- ["bar.example.org", nil],
+ ["foo.example.org", nil, nil],
+ ["bar.example.org", nil, nil],
])
@knife.configure_session
end
@@ -80,14 +87,13 @@ describe Chef::Knife::Ssh do
context "when cloud hostnames are available" do
before do
- @node_foo.automatic_attrs[:cloud][:public_hostname] = "ec2-10-0-0-1.compute-1.amazonaws.com"
- @node_bar.automatic_attrs[:cloud][:public_hostname] = "ec2-10-0-0-2.compute-1.amazonaws.com"
+ @node_foo["cloud"]["public_hostname"] = "ec2-10-0-0-1.compute-1.amazonaws.com"
+ @node_bar["cloud"]["public_hostname"] = "ec2-10-0-0-2.compute-1.amazonaws.com"
end
it "returns an array of cloud public hostnames" do
- configure_query([@node_foo, @node_bar])
expect(@knife).to receive(:session_from_list).with([
- ["ec2-10-0-0-1.compute-1.amazonaws.com", nil],
- ["ec2-10-0-0-2.compute-1.amazonaws.com", nil],
+ ["ec2-10-0-0-1.compute-1.amazonaws.com", nil, nil],
+ ["ec2-10-0-0-2.compute-1.amazonaws.com", nil, nil],
])
@knife.configure_session
end
@@ -97,15 +103,14 @@ describe Chef::Knife::Ssh do
context "when cloud hostnames are available but empty" do
before do
- @node_foo.automatic_attrs[:cloud][:public_hostname] = ""
- @node_bar.automatic_attrs[:cloud][:public_hostname] = ""
+ @node_foo["cloud"]["public_hostname"] = ""
+ @node_bar["cloud"]["public_hostname"] = ""
end
it "returns an array of fqdns" do
- configure_query([@node_foo, @node_bar])
expect(@knife).to receive(:session_from_list).with([
- ["foo.example.org", nil],
- ["bar.example.org", nil],
+ ["foo.example.org", nil, nil],
+ ["bar.example.org", nil, nil],
])
@knife.configure_session
end
@@ -114,7 +119,7 @@ describe Chef::Knife::Ssh do
end
it "should raise an error if no host are found" do
- configure_query([ ])
+ allow(query_result).to receive(:search).with(any_args)
expect(@knife.ui).to receive(:fatal)
expect(@knife).to receive(:exit).with(10)
@knife.configure_session
@@ -122,10 +127,8 @@ describe Chef::Knife::Ssh do
context "when there are some hosts found but they do not have an attribute to connect with" do
before do
- allow(@query).to receive(:search).and_return([[@node_foo, @node_bar]])
- @node_foo.automatic_attrs[:fqdn] = nil
- @node_bar.automatic_attrs[:fqdn] = nil
- allow(Chef::Search::Query).to receive(:new).and_return(@query)
+ @node_foo["fqdn"] = nil
+ @node_bar["fqdn"] = nil
end
it "should raise a specific error (CHEF-3402)" do
@@ -134,6 +137,24 @@ describe Chef::Knife::Ssh do
@knife.configure_session
end
end
+
+ context "when there are some hosts found but IPs duplicated if duplicated_fqdns option sets :fatal" do
+ before do
+ @knife.config[:duplicated_fqdns] = :fatal
+ @node_foo["fqdn"] = "foo.example.org"
+ @node_bar["fqdn"] = "foo.example.org"
+ end
+
+ it "should raise a specific error" do
+ expect(@knife.ui).to receive(:fatal).with(/^SSH node is duplicated: foo\.example\.org/)
+ expect(@knife).to receive(:exit).with(10)
+ expect(@knife).to receive(:session_from_list).with([
+ ["foo.example.org", nil, nil],
+ ["foo.example.org", nil, nil],
+ ])
+ @knife.configure_session
+ end
+ end
end
context "manual is set to true" do
@@ -149,82 +170,112 @@ describe Chef::Knife::Ssh do
end
end
+ describe "#get_prefix_attribute" do
+ # Order of precedence for prefix
+ # 1) config value (cli or knife config)
+ # 2) nil
+ before do
+ Chef::Config[:knife][:prefix_attribute] = nil
+ @knife.config[:prefix_attribute] = nil
+ @node_foo["cloud"]["public_hostname"] = "ec2-10-0-0-1.compute-1.amazonaws.com"
+ @node_bar["cloud"]["public_hostname"] = ""
+ end
+
+ it "should return nil by default" do
+ expect(@knife.get_prefix_attribute({})).to eq(nil)
+ end
+
+ it "should favor config over nil" do
+ @node_foo["prefix"] = "config"
+ expect( @knife.get_prefix_attribute(@node_foo)).to eq("config")
+ end
+ end
+
describe "#get_ssh_attribute" do
# Order of precedence for ssh target
- # 1) command line attribute
- # 2) configuration file
- # 3) cloud attribute
- # 4) fqdn
+ # 1) config value (cli or knife config)
+ # 2) cloud attribute
+ # 3) fqdn
before do
Chef::Config[:knife][:ssh_attribute] = nil
- @knife.config[:attribute] = nil
- @node_foo.automatic_attrs[:cloud][:public_hostname] = "ec2-10-0-0-1.compute-1.amazonaws.com"
- @node_bar.automatic_attrs[:cloud][:public_hostname] = ""
+ @knife.config[:ssh_attribute] = nil
+ @node_foo["cloud"]["public_hostname"] = "ec2-10-0-0-1.compute-1.amazonaws.com"
+ @node_bar["cloud"]["public_hostname"] = ""
end
it "should return fqdn by default" do
- expect(@knife.get_ssh_attribute(Chef::Node.new)).to eq("fqdn")
+ expect(@knife.get_ssh_attribute({ "fqdn" => "fqdn" })).to eq("fqdn")
end
it "should return cloud.public_hostname attribute if available" do
- expect(@knife.get_ssh_attribute(@node_foo)).to eq("cloud.public_hostname")
- end
-
- it "should favor to attribute_from_cli over config file and cloud" do
- @knife.config[:attribute] = "command_line"
- Chef::Config[:knife][:ssh_attribute] = "config_file"
- expect( @knife.get_ssh_attribute(@node_foo)).to eq("command_line")
+ expect(@knife.get_ssh_attribute(@node_foo)).to eq("ec2-10-0-0-1.compute-1.amazonaws.com")
end
- it "should favor config file over cloud and default" do
- Chef::Config[:knife][:ssh_attribute] = "config_file"
- expect( @knife.get_ssh_attribute(@node_foo)).to eq("config_file")
+ it "should favor config over cloud and default" do
+ @node_foo["target"] = "config"
+ expect( @knife.get_ssh_attribute(@node_foo)).to eq("config")
end
it "should return fqdn if cloud.hostname is empty" do
- expect( @knife.get_ssh_attribute(@node_bar)).to eq("fqdn")
+ expect( @knife.get_ssh_attribute(@node_bar)).to eq("bar.example.org")
end
end
describe "#session_from_list" do
before :each do
@knife.instance_variable_set(:@longest, 0)
- ssh_config = { :timeout => 50, :user => "locutus", :port => 23 }
- allow(Net::SSH).to receive(:configuration_for).with("the.b.org").and_return(ssh_config)
+ ssh_config = { :timeout => 50, :user => "locutus", :port => 23, :keepalive => true, :keepalive_interval => 60 }
+ allow(Net::SSH).to receive(:configuration_for).with("the.b.org", true).and_return(ssh_config)
end
it "uses the port from an ssh config file" do
- @knife.session_from_list([["the.b.org", nil]])
+ @knife.session_from_list([["the.b.org", nil, nil]])
expect(@knife.session.servers[0].port).to eq(23)
end
it "uses the port from a cloud attr" do
- @knife.session_from_list([["the.b.org", 123]])
+ @knife.session_from_list([["the.b.org", 123, nil]])
expect(@knife.session.servers[0].port).to eq(123)
end
+ it "uses the prefix from list" do
+ @knife.session_from_list([["the.b.org", nil, "b-team"]])
+ expect(@knife.session.servers[0][:prefix]).to eq("b-team")
+ end
+
+ it "defaults to a prefix of host" do
+ @knife.session_from_list([["the.b.org", nil, nil]])
+ expect(@knife.session.servers[0][:prefix]).to eq("the.b.org")
+ end
+
it "defaults to a timeout of 120 seconds" do
- @knife.session_from_list([["the.b.org", nil]])
+ @knife.session_from_list([["the.b.org", nil, nil]])
expect(@knife.session.servers[0].options[:timeout]).to eq(120)
end
it "uses the timeout from Chef Config" do
Chef::Config[:knife][:ssh_timeout] = 5
@knife.config[:ssh_timeout] = nil
- @knife.session_from_list([["the.b.org", nil]])
+ @knife.session_from_list([["the.b.org", nil, nil]])
expect(@knife.session.servers[0].options[:timeout]).to eq(5)
end
it "uses the timeout from knife config" do
@knife.config[:ssh_timeout] = 6
- @knife.session_from_list([["the.b.org", nil]])
+ @knife.session_from_list([["the.b.org", nil, nil]])
expect(@knife.session.servers[0].options[:timeout]).to eq(6)
end
it "uses the user from an ssh config file" do
- @knife.session_from_list([["the.b.org", 123]])
+ @knife.session_from_list([["the.b.org", 123, nil]])
expect(@knife.session.servers[0].user).to eq("locutus")
end
+
+ it "uses keepalive settings from an ssh config file" do
+ @knife.session_from_list([["the.b.org", 123, nil]])
+ expect(@knife.session.servers[0].options[:keepalive]).to be true
+ expect(@knife.session.servers[0].options[:keepalive_interval]).to eq 60
+ end
end
describe "#ssh_command" do
@@ -291,10 +342,27 @@ describe Chef::Knife::Ssh do
end
end
+ describe "#tmux" do
+ before do
+ ssh_config = { :timeout => 50, :user => "locutus", :port => 23, :keepalive => true, :keepalive_interval => 60 }
+ allow(Net::SSH).to receive(:configuration_for).with("foo.example.org", true).and_return(ssh_config)
+ @query = Chef::Search::Query.new
+ expect(@query).to receive(:search).and_yield(@node_foo)
+ allow(Chef::Search::Query).to receive(:new).and_return(@query)
+ allow(@knife).to receive(:exec).and_return(0)
+ end
+
+ it "filters out invalid characters from tmux session name" do
+ @knife.name_args = ["name:foo.example.org", "tmux"]
+ expect(@knife).to receive(:shell_out!).with("tmux new-session -d -s 'knife ssh name=foo-example-org' -n 'foo.example.org' 'ssh locutus@foo.example.org' ")
+ @knife.run
+ end
+ end
+
describe "#run" do
before do
@query = Chef::Search::Query.new
- expect(@query).to receive(:search).and_return([[@node_foo]])
+ expect(@query).to receive(:search).and_yield(@node_foo)
allow(Chef::Search::Query).to receive(:new).and_return(@query)
allow(@knife).to receive(:ssh_command).and_return(exit_code)
@knife.name_args = ["*:*", "false"]
diff --git a/spec/unit/knife/ssl_check_spec.rb b/spec/unit/knife/ssl_check_spec.rb
index 180d798d5b..8aa18c3abc 100644
--- a/spec/unit/knife/ssl_check_spec.rb
+++ b/spec/unit/knife/ssl_check_spec.rb
@@ -114,6 +114,22 @@ E
allow(ssl_check).to receive(:verify_cert_host).and_return(true)
end
+ context "when the trusted certificates directory is not glob escaped", :windows_only do
+ let(:trusted_certs_dir) { File.join(CHEF_SPEC_DATA.tr("/", "\\"), "trusted_certs") }
+
+ before do
+ allow(ssl_check).to receive(:trusted_certificates).and_call_original
+ allow(store).to receive(:verify).with(certificate).and_return(true)
+ end
+
+ it "escpaes the trusted certificates directory" do
+ expect(Dir).to receive(:glob)
+ .with("#{ChefConfig::PathHelper.escape_glob_dir(trusted_certs_dir)}/*.{crt,pem}")
+ .and_return([trusted_cert_file])
+ ssl_check.run
+ end
+ end
+
context "when the trusted certificates have valid X509 properties" do
before do
allow(store).to receive(:verify).with(certificate).and_return(true)
diff --git a/spec/unit/knife/ssl_fetch_spec.rb b/spec/unit/knife/ssl_fetch_spec.rb
index 8bb4810b88..bc49c40241 100644
--- a/spec/unit/knife/ssl_fetch_spec.rb
+++ b/spec/unit/knife/ssl_fetch_spec.rb
@@ -108,6 +108,24 @@ E
end
+ describe "#cn_of" do
+ let(:certificate) { double("Certificate", subject: subject) }
+
+ describe "when the certificate has a common name" do
+ let(:subject) { [["CN", "common name"]] }
+ it "returns the common name" do
+ expect(ssl_fetch.cn_of(certificate)).to eq("common name")
+ end
+ end
+
+ describe "when the certificate does not have a common name" do
+ let(:subject) { [] }
+ it "returns nil" do
+ expect(ssl_fetch.cn_of(certificate)).to eq(nil)
+ end
+ end
+ end
+
describe "fetching the remote cert chain" do
let(:name_args) { %w{https://foo.example.com:8443} }
@@ -180,5 +198,25 @@ ERROR_TEXT
end
+ describe "when the certificate does not have a CN" do
+ let(:self_signed_crt_path) { File.join(CHEF_SPEC_DATA, "trusted_certs", "example_no_cn.crt") }
+ let(:self_signed_crt) { OpenSSL::X509::Certificate.new(File.read(self_signed_crt_path)) }
+
+ before do
+ expect(ssl_fetch).to receive(:proxified_socket).with("foo.example.com", 8443).and_return(tcp_socket)
+ expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_fetch.noverify_peer_ssl_context).and_return(ssl_socket)
+ expect(ssl_socket).to receive(:connect)
+ expect(ssl_socket).to receive(:peer_cert_chain).and_return([self_signed_crt])
+ expect(Time).to receive(:new).and_return(1)
+ end
+
+ it "fetches the certificate and writes it to a file in the trusted_certs_dir" do
+ run
+ stored_cert_path = File.join(trusted_certs_dir, "foo.example.com_1.crt")
+ expect(File).to exist(stored_cert_path)
+ expect(File.read(stored_cert_path)).to eq(File.read(self_signed_crt_path))
+ end
+ end
+
end
end
diff --git a/spec/unit/knife/status_spec.rb b/spec/unit/knife/status_spec.rb
index 473598fd85..929a0f742b 100644
--- a/spec/unit/knife/status_spec.rb
+++ b/spec/unit/knife/status_spec.rb
@@ -23,6 +23,8 @@ describe Chef::Knife::Status do
node = Chef::Node.new.tap do |n|
n.automatic_attrs["fqdn"] = "foobar"
n.automatic_attrs["ohai_time"] = 1343845969
+ n.automatic_attrs["platform"] = "mac_os_x"
+ n.automatic_attrs["platform_version"] = "10.12.5"
end
allow(Time).to receive(:now).and_return(Time.at(1428573420))
@query = double("Chef::Search::Query")
@@ -34,10 +36,11 @@ describe Chef::Knife::Status do
end
describe "run" do
- let(:opts) {{ filter_result:
+ let(:opts) do
+ { filter_result:
{ name: ["name"], ipaddress: ["ipaddress"], ohai_time: ["ohai_time"],
ec2: ["ec2"], run_list: ["run_list"], platform: ["platform"],
- platform_version: ["platform_version"], chef_environment: ["chef_environment"] } }}
+ platform_version: ["platform_version"], chef_environment: ["chef_environment"] } } end
it "should default to searching for everything" do
expect(@query).to receive(:search).with(:node, "*:*", opts)
diff --git a/spec/unit/knife/user_create_spec.rb b/spec/unit/knife/user_create_spec.rb
index e708d2d1ad..07d72fd05a 100644
--- a/spec/unit/knife/user_create_spec.rb
+++ b/spec/unit/knife/user_create_spec.rb
@@ -24,13 +24,13 @@ Chef::Knife::UserCreate.load_deps
describe Chef::Knife::UserCreate do
let(:knife) { Chef::Knife::UserCreate.new }
- let(:stderr) {
+ let(:stderr) do
StringIO.new
- }
+ end
- let(:stdout) {
+ let(:stdout) do
StringIO.new
- }
+ end
before(:each) do
allow(knife.ui).to receive(:stdout).and_return(stdout)
diff --git a/spec/unit/knife_spec.rb b/spec/unit/knife_spec.rb
index ec1e59d863..adaab11d55 100644
--- a/spec/unit/knife_spec.rb
+++ b/spec/unit/knife_spec.rb
@@ -47,6 +47,7 @@ describe Chef::Knife do
allow(Chef::WorkstationConfigLoader).to receive(:new).and_return(config_loader)
allow(config_loader).to receive(:explicit_config_file=)
+ allow(config_loader).to receive(:profile=)
# Prevent gratuitous code reloading:
allow(Chef::Knife).to receive(:load_commands)
@@ -144,9 +145,9 @@ describe Chef::Knife do
end
it "finds a subcommand class based on ARGV" do
- Chef::Knife.subcommands["cookbook_site_vendor"] = :CookbookSiteVendor
+ Chef::Knife.subcommands["cookbook_site_install"] = :CookbookSiteInstall
Chef::Knife.subcommands["cookbook"] = :Cookbook
- expect(Chef::Knife.subcommand_class_from(%w{cookbook site vendor --help foo bar baz})).to eq(:CookbookSiteVendor)
+ expect(Chef::Knife.subcommand_class_from(%w{cookbook site install --help foo bar baz})).to eq(:CookbookSiteInstall)
end
it "special case sets the subcommand_loader to GemGlobLoader when running rehash" do
@@ -159,12 +160,13 @@ describe Chef::Knife do
describe "the headers include X-Remote-Request-Id" do
- let(:headers) {{ "Accept" => "application/json",
- "Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
- "X-Chef-Version" => Chef::VERSION,
- "Host" => "api.opscode.piab",
- "X-REMOTE-REQUEST-ID" => request_id,
- }}
+ let(:headers) do
+ { "Accept" => "application/json",
+ "Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
+ "X-Chef-Version" => Chef::VERSION,
+ "Host" => "api.opscode.piab",
+ "X-REMOTE-REQUEST-ID" => request_id,
+ } end
let(:request_id) { "1234" }
@@ -330,6 +332,7 @@ describe Chef::Knife do
knife.config[:config_file] = fake_config
config_loader = double("Chef::WorkstationConfigLoader", :load => true, :no_config_found? => false, :chef_config_dir => "/etc/chef", :config_location => fake_config)
allow(config_loader).to receive(:explicit_config_file=).with(fake_config).and_return(fake_config)
+ allow(config_loader).to receive(:profile=)
allow(Chef::WorkstationConfigLoader).to receive(:new).and_return(config_loader)
end
@@ -348,6 +351,37 @@ describe Chef::Knife do
expect { knife.run_with_pretty_exceptions }.to raise_error(Exception)
end
end
+
+ describe "setting arbitrary configuration with --config-option" do
+
+ let(:stdout) { StringIO.new }
+
+ let(:stderr) { StringIO.new }
+
+ let(:stdin) { StringIO.new }
+
+ let(:ui) { Chef::Knife::UI.new(stdout, stderr, stdin, disable_editing: true) }
+
+ let(:subcommand) do
+ KnifeSpecs::TestYourself.options = Chef::Application::Knife.options.merge(KnifeSpecs::TestYourself.options)
+ KnifeSpecs::TestYourself.new(%w{--config-option badly_formatted_arg}).tap do |cmd|
+ cmd.ui = ui
+ end
+ end
+
+ it "sets arbitrary configuration via --config-option" do
+ Chef::Knife.run(%w{test yourself --config-option arbitrary_config_thing=hello}, Chef::Application::Knife.options)
+ expect(Chef::Config[:arbitrary_config_thing]).to eq("hello")
+ end
+
+ it "handles errors in arbitrary configuration" do
+ expect(subcommand).to receive(:exit).with(1)
+ subcommand.configure_chef
+ expect(stderr.string).to include("ERROR: Unparsable config option \"badly_formatted_arg\"")
+ expect(stdout.string).to include(subcommand.opt_parser.to_s)
+ end
+ end
+
end
describe "when first created" do
@@ -407,6 +441,28 @@ describe Chef::Knife do
expect(stderr.string).to match(%r{Response: y u no administrator})
end
+ context "when proxy servers are set" do
+ before do
+ ENV["http_proxy"] = "xyz"
+ end
+
+ after do
+ ENV.delete("http_proxy")
+ end
+
+ it "formats proxy errors nicely" do
+ response = Net::HTTPForbidden.new("1.1", "403", "Forbidden")
+ response.instance_variable_set(:@read, true)
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "y u no administrator"))
+ allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("403 Forbidden", response))
+ allow(knife).to receive(:username).and_return("sadpanda")
+ knife.run_with_pretty_exceptions
+ expect(stderr.string).to match(%r{ERROR: You authenticated successfully to http.+ as sadpanda but you are not authorized for this action})
+ expect(stderr.string).to match(%r{ERROR: There are proxy servers configured, your Chef server may need to be added to NO_PROXY.})
+ expect(stderr.string).to match(%r{Response: y u no administrator})
+ end
+ end
+
it "formats 400s nicely" do
response = Net::HTTPBadRequest.new("1.1", "400", "Bad Request")
response.instance_variable_set(:@read, true) # I hate you, net/http.
diff --git a/spec/unit/lib_backcompat_spec.rb b/spec/unit/lib_backcompat_spec.rb
deleted file mode 100644
index c7dfee5bcd..0000000000
--- a/spec/unit/lib_backcompat_spec.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-
-describe "lib-backcompat" do
- it "require 'chef/chef_fs/file_system/chef_server_root_dir' yields the proper class" do
- require "chef/chef_fs/file_system/chef_server_root_dir"
- expect(Chef::ChefFS::FileSystem::ChefServerRootDir).to eq(Chef::ChefFS::FileSystem::ChefServer::ChefServerRootDir)
- end
- it "require 'chef/chef_fs/file_system/chef_repository_file_system_root_dir' yields the proper class" do
- require "chef/chef_fs/file_system/chef_repository_file_system_root_dir"
- expect(Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir).to eq(Chef::ChefFS::FileSystem::Repository::ChefRepositoryFileSystemRootDir)
- end
- it "require 'chef/chef_fs/file_system/acl_entry' yields the proper class" do
- require "chef/chef_fs/file_system/acl_entry"
- expect(Chef::ChefFS::FileSystem::AclEntry).to eq(Chef::ChefFS::FileSystem::ChefServer::AclEntry)
- end
-end
diff --git a/spec/unit/log/syslog_spec.rb b/spec/unit/log/syslog_spec.rb
index ebf1418576..6d56b96575 100644
--- a/spec/unit/log/syslog_spec.rb
+++ b/spec/unit/log/syslog_spec.rb
@@ -17,11 +17,9 @@
#
require "spec_helper"
-require "chef"
describe "Chef::Log::Syslog", :unix_only => true do
let(:syslog) { Chef::Log::Syslog.new }
- let(:app) { Chef::Application.new }
before do
Chef::Log.init(MonoLogger.new(syslog))
@@ -37,12 +35,12 @@ describe "Chef::Log::Syslog", :unix_only => true do
end
it "should send message with severity info to syslog." do
- expect(syslog).to receive(:info).with("*** Chef 12.4.0.dev.0 ***")
+ expect(syslog).to receive(:add).with(1, "*** Chef 12.4.0.dev.0 ***", nil)
Chef::Log.info("*** Chef 12.4.0.dev.0 ***")
end
it "should send message with severity warning to syslog." do
- expect(syslog).to receive(:warn).with("No config file found or specified on command line, using command line options.")
+ expect(syslog).to receive(:add).with(2, "No config file found or specified on command line, using command line options.", nil)
Chef::Log.warn("No config file found or specified on command line, using command line options.")
end
diff --git a/spec/unit/log/winevt_spec.rb b/spec/unit/log/winevt_spec.rb
index d5d452159b..c7cc49f40d 100644
--- a/spec/unit/log/winevt_spec.rb
+++ b/spec/unit/log/winevt_spec.rb
@@ -25,7 +25,6 @@ describe Chef::Log::WinEvt do
let(:app) { Chef::Application.new }
before do
-
Chef::Log.init(MonoLogger.new(winevt))
@old_log_level = Chef::Log.level
Chef::Log.level = :info
@@ -39,12 +38,12 @@ describe Chef::Log::WinEvt do
end
it "should send message with severity info to Windows Event Log." do
- expect(winevt).to receive(:info).with("*** Chef 12.4.0.dev.0 ***")
+ expect(winevt).to receive(:add).with(1, "*** Chef 12.4.0.dev.0 ***", nil)
Chef::Log.info("*** Chef 12.4.0.dev.0 ***")
end
it "should send message with severity warning to Windows Event Log." do
- expect(winevt).to receive(:warn).with("No config file found or specified on command line, using command line options.")
+ expect(winevt).to receive(:add).with(2, "No config file found or specified on command line, using command line options.", nil)
Chef::Log.warn("No config file found or specified on command line, using command line options.")
end
diff --git a/spec/unit/lwrp_spec.rb b/spec/unit/lwrp_spec.rb
index 6574a91f13..4423ccf64e 100644
--- a/spec/unit/lwrp_spec.rb
+++ b/spec/unit/lwrp_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,27 +28,24 @@ describe "LWRP" do
include Chef::Mixin::ConvertToClassName
before do
- @original_VERBOSE = $VERBOSE
+ @original_verbose = $VERBOSE
$VERBOSE = nil
Chef::Resource::LWRPBase.class_eval { @loaded_lwrps = {} }
end
after do
- $VERBOSE = @original_VERBOSE
+ $VERBOSE = @original_verbose
end
def get_lwrp(name)
Chef::ResourceResolver.resolve(name)
end
- def get_lwrp_provider(name)
- old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- begin
- Chef::Provider.const_get(convert_to_class_name(name.to_s))
- ensure
- Chef::Config[:treat_deprecation_warnings_as_errors] = old_treat_deprecation_warnings_as_errors
- end
+ def get_dynamic_lwrp_provider(name)
+ # need a node to do dynamic lookup, so also need a run_context and a resource instance
+ node = Chef::Node.new
+ run_context = Chef::RunContext.new(node, {}, nil)
+ Chef::Resource.new("name", run_context).lookup_provider_constant(name)
end
describe "when overriding an existing class" do
@@ -60,7 +57,7 @@ describe "LWRP" do
Object.const_set("LwrpFoo", Class.new)
file = File.expand_path( "lwrp/resources/foo.rb", CHEF_SPEC_DATA)
expect(Chef::Log).not_to receive(:info).with(/Skipping/)
- expect(Chef::Log).not_to receive(:debug).with(/anymore/)
+ expect(Chef::Log).not_to receive(:trace).with(/anymore/)
Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil)
Object.send(:remove_const, "LwrpFoo")
end
@@ -69,7 +66,7 @@ describe "LWRP" do
Object.const_set("LwrpBuckPasser", Class.new)
file = File.expand_path( "lwrp/providers/buck_passer.rb", CHEF_SPEC_DATA)
expect(Chef::Log).not_to receive(:info).with(/Skipping/)
- expect(Chef::Log).not_to receive(:debug).with(/anymore/)
+ expect(Chef::Log).not_to receive(:trace).with(/anymore/)
Chef::Provider::LWRPBase.build_from_file("lwrp", file, nil)
Object.send(:remove_const, "LwrpBuckPasser")
end
@@ -84,7 +81,7 @@ describe "LWRP" do
end
Dir[File.expand_path( "lwrp/resources/*", CHEF_SPEC_DATA)].each do |file|
- expect(Chef::Log).to receive(:debug).with(/Skipping/)
+ expect(Chef::Log).to receive(:trace).with(/Skipping/)
Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil)
end
end
@@ -95,7 +92,7 @@ describe "LWRP" do
end
Dir[File.expand_path( "lwrp/providers/*", CHEF_SPEC_DATA)].each do |file|
- expect(Chef::Log).to receive(:debug).with(/Skipping/)
+ expect(Chef::Log).to receive(:trace).with(/Skipping/)
Chef::Provider::LWRPBase.build_from_file("lwrp", file, nil)
end
end
@@ -232,9 +229,9 @@ describe "LWRP" do
end
it "allows to user to user the resource_name" do
- expect {
+ expect do
klass.resource_name(:foo)
- }.to_not raise_error
+ end.to_not raise_error
end
it "returns the set value for the resource" do
@@ -324,14 +321,7 @@ describe "LWRP" do
end
end
- def raise_if_deprecated!
- if Chef::VERSION.split(".").first.to_i > 12
- raise "This test should be removed and the associated code should be removed!"
- end
- end
-
it "amends actions when they are already defined" do
- raise_if_deprecated!
expect(child.actions).to eq([:nothing, :eat, :sleep, :drink])
end
end
@@ -393,7 +383,7 @@ describe "LWRP" do
let(:runner) { Chef::Runner.new(run_context) }
- let(:lwrp_cookbok_name) { "lwrp" }
+ let(:lwrp_cookbook_name) { "lwrp" }
before do
Chef::Provider::LWRPBase.class_eval { @loaded_lwrps = {} }
@@ -401,20 +391,19 @@ describe "LWRP" do
before(:each) do
Dir[File.expand_path(File.expand_path("../../data/lwrp/resources/*", __FILE__))].each do |file|
- Chef::Resource::LWRPBase.build_from_file(lwrp_cookbok_name, file, run_context)
+ Chef::Resource::LWRPBase.build_from_file(lwrp_cookbook_name, file, run_context)
end
Dir[File.expand_path(File.expand_path("../../data/lwrp/providers/*", __FILE__))].each do |file|
- Chef::Provider::LWRPBase.build_from_file(lwrp_cookbok_name, file, run_context)
+ Chef::Provider::LWRPBase.build_from_file(lwrp_cookbook_name, file, run_context)
end
end
it "should properly handle a new_resource reference" do
resource = get_lwrp(:lwrp_foo).new("morpheus", run_context)
resource.monkey("bob")
- resource.provider(get_lwrp_provider(:lwrp_monkey_name_printer))
-
- provider = Chef::Platform.provider_for_resource(resource, :twiddle_thumbs)
+ resource.provider(get_dynamic_lwrp_provider(:lwrp_monkey_name_printer))
+ provider = resource.provider_for_action(:twiddle_thumbs)
provider.action_twiddle_thumbs
end
@@ -428,14 +417,13 @@ describe "LWRP" do
Chef::Config[:treat_deprecation_warnings_as_errors] = @old_treat_deprecation_warnings_as_errors
end
- it "should load the provider into a properly-named class" do
- expect(Chef::Provider.const_get("LwrpBuckPasser")).to be_kind_of(Class)
- expect(Chef::Provider::LwrpBuckPasser <= Chef::Provider::LWRPBase).to be_truthy
+ it "should not load the provider into a const" do
+ expect(defined?(Chef::Provider::LwrpBuckPasser)).to be_nil
end
it "should create a method for each action" do
- expect(get_lwrp_provider(:lwrp_buck_passer).instance_methods).to include(:action_pass_buck)
- expect(get_lwrp_provider(:lwrp_thumb_twiddler).instance_methods).to include(:action_twiddle_thumbs)
+ expect(get_dynamic_lwrp_provider(:lwrp_buck_passer).instance_methods).to include(:action_pass_buck)
+ expect(get_dynamic_lwrp_provider(:lwrp_thumb_twiddler).instance_methods).to include(:action_twiddle_thumbs)
end
it "sets itself as a provider for a resource of the same name" do
@@ -443,30 +431,30 @@ describe "LWRP" do
# we bypass the per-file loading to get the file to load each time,
# which creates the LWRP class repeatedly. New things get prepended to
# the list of providers.
- expect(found_providers.first).to eq(get_lwrp_provider(:lwrp_buck_passer))
+ expect(found_providers.first).to eq(get_dynamic_lwrp_provider(:lwrp_buck_passer))
end
context "with a cookbook with an underscore in the name" do
- let(:lwrp_cookbok_name) { "l_w_r_p" }
+ let(:lwrp_cookbook_name) { "l_w_r_p" }
it "sets itself as a provider for a resource of the same name" do
found_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :l_w_r_p_buck_passer)
expect(found_providers.size).to eq(1)
- expect(found_providers.last).to eq(get_lwrp_provider(:l_w_r_p_buck_passer))
+ expect(found_providers.last).to eq(get_dynamic_lwrp_provider(:l_w_r_p_buck_passer))
end
end
context "with a cookbook with a hypen in the name" do
- let(:lwrp_cookbok_name) { "l-w-r-p" }
+ let(:lwrp_cookbook_name) { "l-w-r-p" }
it "sets itself as a provider for a resource of the same name" do
incorrect_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :'l-w-r-p_buck_passer')
expect(incorrect_providers).to eq([])
found_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :l_w_r_p_buck_passer)
- expect(found_providers.first).to eq(get_lwrp_provider(:l_w_r_p_buck_passer))
+ expect(found_providers.first).to eq(get_dynamic_lwrp_provider(:l_w_r_p_buck_passer))
end
end
end
@@ -474,7 +462,7 @@ describe "LWRP" do
it "should insert resources embedded in the provider into the middle of the resource collection" do
injector = get_lwrp(:lwrp_foo).new("morpheus", run_context)
injector.action(:pass_buck)
- injector.provider(get_lwrp_provider(:lwrp_buck_passer))
+ injector.provider(get_dynamic_lwrp_provider(:lwrp_buck_passer))
dummy = Chef::Resource::ZenMaster.new("keanu reeves", run_context)
dummy.provider(Chef::Provider::Easy)
run_context.resource_collection.insert(injector)
@@ -491,11 +479,11 @@ describe "LWRP" do
it "should insert embedded resources from multiple providers, including from the last position, properly into the resource collection" do
injector = get_lwrp(:lwrp_foo).new("morpheus", run_context)
injector.action(:pass_buck)
- injector.provider(get_lwrp_provider(:lwrp_buck_passer))
+ injector.provider(get_dynamic_lwrp_provider(:lwrp_buck_passer))
injector2 = get_lwrp(:lwrp_bar).new("tank", run_context)
injector2.action(:pass_buck)
- injector2.provider(get_lwrp_provider(:lwrp_buck_passer_2))
+ injector2.provider(get_dynamic_lwrp_provider(:lwrp_buck_passer_2))
dummy = Chef::Resource::ZenMaster.new("keanu reeves", run_context)
dummy.provider(Chef::Provider::Easy)
@@ -518,9 +506,9 @@ describe "LWRP" do
it "should properly handle a new_resource reference" do
resource = get_lwrp(:lwrp_foo).new("morpheus", run_context)
resource.monkey("bob")
- resource.provider(get_lwrp_provider(:lwrp_monkey_name_printer))
+ resource.provider(get_dynamic_lwrp_provider(:lwrp_monkey_name_printer))
- provider = Chef::Platform.provider_for_resource(resource, :twiddle_thumbs)
+ provider = resource.provider_for_action(:twiddle_thumbs)
provider.action_twiddle_thumbs
expect(provider.monkey_name).to eq("my monkey's name is 'bob'")
@@ -529,9 +517,9 @@ describe "LWRP" do
it "should properly handle an embedded Resource accessing the enclosing Provider's scope" do
resource = get_lwrp(:lwrp_foo).new("morpheus", run_context)
resource.monkey("bob")
- resource.provider(get_lwrp_provider(:lwrp_embedded_resource_accesses_providers_scope))
+ resource.provider(get_dynamic_lwrp_provider(:lwrp_embedded_resource_accesses_providers_scope))
- provider = Chef::Platform.provider_for_resource(resource, :twiddle_thumbs)
+ provider = resource.provider_for_action(:twiddle_thumbs)
#provider = @runner.build_provider(resource)
provider.action_twiddle_thumbs
@@ -549,7 +537,7 @@ describe "LWRP" do
@resource = get_lwrp(:lwrp_foo).new("morpheus", run_context)
@resource.allowed_actions << :test
@resource.action(:test)
- @resource.provider(get_lwrp_provider(:lwrp_inline_compiler))
+ @resource.provider(get_dynamic_lwrp_provider(:lwrp_inline_compiler))
end
it "does not add interior resources to the exterior resource collection" do
@@ -582,55 +570,30 @@ describe "LWRP" do
end
context "resource class created" do
- before(:context) do
- @tmpdir = Dir.mktmpdir("lwrp_test")
+ let(:test_lwrp_class) { @test_lwrp_class }
+ before(:each) do
+ @tmpparent = Dir.mktmpdir("lwrp_test")
+ @tmpdir = File.join(@tmpparent, "lwrp")
+ Dir.mkdir(@tmpdir)
resource_path = File.join(@tmpdir, "once.rb")
IO.write(resource_path, "default_action :create")
-
- @old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- Chef::Resource::LWRPBase.build_from_file("lwrp", resource_path, nil)
+ @test_lwrp_class = Chef::Resource::LWRPBase.build_from_file("lwrp", resource_path, nil)
end
- after(:context) do
+ after(:each) do
FileUtils.remove_entry @tmpdir
- Chef::Config[:treat_deprecation_warnings_as_errors] = @old_treat_deprecation_warnings_as_errors
end
- it "should load the resource into a properly-named class" do
- expect(Chef::Resource::LwrpOnce).to be_kind_of(Class)
- expect(Chef::Resource::LwrpOnce <= Chef::Resource::LWRPBase).to be_truthy
+ it "should not load the resource into a const" do
+ expect(defined?(Chef::Resource::LwrpOnce)).to be_nil
end
- it "get_lwrp(:lwrp_once).new is a Chef::Resource::LwrpOnce" do
+ it "get_lwrp(:lwrp_once).new is an instance of the LWRP class" do
lwrp = get_lwrp(:lwrp_once).new("hi")
- expect(lwrp.kind_of?(Chef::Resource::LwrpOnce)).to be_truthy
- expect(lwrp.is_a?(Chef::Resource::LwrpOnce)).to be_truthy
- expect(get_lwrp(:lwrp_once) === lwrp).to be_truthy
- expect(Chef::Resource::LwrpOnce === lwrp).to be_truthy
- end
-
- it "Chef::Resource::LwrpOnce.new is a get_lwrp(:lwrp_once)" do
- lwrp = Chef::Resource::LwrpOnce.new("hi")
- expect(lwrp.kind_of?(get_lwrp(:lwrp_once))).to be_truthy
- expect(lwrp.is_a?(get_lwrp(:lwrp_once))).to be_truthy
+ expect(lwrp.kind_of?(test_lwrp_class)).to be_truthy
+ expect(lwrp.is_a?(test_lwrp_class)).to be_truthy
expect(get_lwrp(:lwrp_once) === lwrp).to be_truthy
- expect(Chef::Resource::LwrpOnce === lwrp).to be_truthy
- end
-
- it "works even if LwrpOnce exists in the top level" do
- module ::LwrpOnce
- end
- expect(Chef::Resource::LwrpOnce).not_to eq(::LwrpOnce)
- end
-
- it "allows monkey patching of the lwrp through Chef::Resource" do
- monkey = Module.new do
- def issue_3607
- end
- end
- Chef::Resource::LwrpOnce.send(:include, monkey)
- expect { get_lwrp(:lwrp_once).new("blah").issue_3607 }.not_to raise_error
+ expect(test_lwrp_class === lwrp).to be_truthy
end
context "with a subclass of get_lwrp(:lwrp_once)" do
@@ -645,54 +608,12 @@ describe "LWRP" do
expect(subclass === lwrp).to be_truthy
expect(lwrp.class === subclass)
end
- it "subclass.new is a Chef::Resource::LwrpOnce" do
- lwrp = subclass.new("hi")
- expect(lwrp.kind_of?(Chef::Resource::LwrpOnce)).to be_truthy
- expect(lwrp.is_a?(Chef::Resource::LwrpOnce)).to be_truthy
- expect(Chef::Resource::LwrpOnce === lwrp).to be_truthy
- expect(lwrp.class === Chef::Resource::LwrpOnce)
- end
- it "subclass.new is a get_lwrp(:lwrp_once)" do
- lwrp = subclass.new("hi")
- expect(lwrp.kind_of?(get_lwrp(:lwrp_once))).to be_truthy
- expect(lwrp.is_a?(get_lwrp(:lwrp_once))).to be_truthy
- expect(get_lwrp(:lwrp_once) === lwrp).to be_truthy
- expect(lwrp.class === get_lwrp(:lwrp_once))
- end
- it "Chef::Resource::LwrpOnce.new is *not* a subclass" do
- lwrp = Chef::Resource::LwrpOnce.new("hi")
- expect(lwrp.kind_of?(subclass)).to be_falsey
- expect(lwrp.is_a?(subclass)).to be_falsey
- expect(subclass === lwrp.class).to be_falsey
- expect(subclass === Chef::Resource::LwrpOnce).to be_falsey
- end
- it "get_lwrp(:lwrp_once).new is *not* a subclass" do
- lwrp = get_lwrp(:lwrp_once).new("hi")
- expect(lwrp.kind_of?(subclass)).to be_falsey
- expect(lwrp.is_a?(subclass)).to be_falsey
- expect(subclass === lwrp.class).to be_falsey
- expect(subclass === get_lwrp(:lwrp_once)).to be_falsey
- end
- end
-
- context "with a subclass of Chef::Resource::LwrpOnce" do
- let(:subclass) do
- Class.new(Chef::Resource::LwrpOnce)
- end
-
- it "subclass.new is a subclass" do
- lwrp = subclass.new("hi")
- expect(lwrp.kind_of?(subclass)).to be_truthy
- expect(lwrp.is_a?(subclass)).to be_truthy
- expect(subclass === lwrp).to be_truthy
- expect(lwrp.class === subclass)
- end
- it "subclass.new is a Chef::Resource::LwrpOnce" do
+ it "subclass.new is an instance of the LWRP class" do
lwrp = subclass.new("hi")
- expect(lwrp.kind_of?(Chef::Resource::LwrpOnce)).to be_truthy
- expect(lwrp.is_a?(Chef::Resource::LwrpOnce)).to be_truthy
- expect(Chef::Resource::LwrpOnce === lwrp).to be_truthy
- expect(lwrp.class === Chef::Resource::LwrpOnce)
+ expect(lwrp.kind_of?(test_lwrp_class)).to be_truthy
+ expect(lwrp.is_a?(test_lwrp_class)).to be_truthy
+ expect(test_lwrp_class === lwrp).to be_truthy
+ expect(lwrp.class === test_lwrp_class)
end
it "subclass.new is a get_lwrp(:lwrp_once)" do
lwrp = subclass.new("hi")
@@ -701,13 +622,6 @@ describe "LWRP" do
expect(get_lwrp(:lwrp_once) === lwrp).to be_truthy
expect(lwrp.class === get_lwrp(:lwrp_once))
end
- it "Chef::Resource::LwrpOnce.new is *not* a subclass" do
- lwrp = Chef::Resource::LwrpOnce.new("hi")
- expect(lwrp.kind_of?(subclass)).to be_falsey
- expect(lwrp.is_a?(subclass)).to be_falsey
- expect(subclass === lwrp.class).to be_falsey
- expect(subclass === Chef::Resource::LwrpOnce).to be_falsey
- end
it "get_lwrp(:lwrp_once).new is *not* a subclass" do
lwrp = get_lwrp(:lwrp_once).new("hi")
expect(lwrp.kind_of?(subclass)).to be_falsey
@@ -732,8 +646,6 @@ describe "LWRP" do
end
class MyAwesomeProvider < Chef::Provider::LWRPBase
- use_inline_resources
-
provides :my_awesome_resource
action :create do
@@ -741,7 +653,7 @@ describe "LWRP" do
end
end
- let(:recipe) {
+ let(:recipe) do
cookbook_repo = File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "cookbooks"))
cookbook_loader = Chef::CookbookLoader.new(cookbook_repo)
cookbook_loader.load_cookbooks
@@ -750,7 +662,7 @@ describe "LWRP" do
events = Chef::EventDispatch::Dispatcher.new
run_context = Chef::RunContext.new(node, cookbook_collection, events)
Chef::Recipe.new("hjk", "test", run_context)
- }
+ end
it "lets you extend the recipe DSL" do
expect(Chef::Recipe).to receive(:include).with(MyAwesomeDSLExensionClass)
diff --git a/spec/unit/mixin/api_version_request_handling_spec.rb b/spec/unit/mixin/api_version_request_handling_spec.rb
index 13b729538c..191dee643b 100644
--- a/spec/unit/mixin/api_version_request_handling_spec.rb
+++ b/spec/unit/mixin/api_version_request_handling_spec.rb
@@ -50,12 +50,12 @@ describe Chef::Mixin::ApiVersionRequestHandling do
context "when x-ops-server-api-version header exists" do
let(:min_server_version) { 2 }
let(:max_server_version) { 4 }
- let(:return_hash) {
+ let(:return_hash) do
{
"min_version" => min_server_version,
"max_version" => max_server_version,
}
- }
+ end
before(:each) do
allow(response).to receive(:[]).with("x-ops-server-api-version").and_return(Chef::JSONCompat.to_json(return_hash))
diff --git a/spec/unit/mixin/checksum_spec.rb b/spec/unit/mixin/checksum_spec.rb
index 997dcd523e..801c8820d2 100644
--- a/spec/unit/mixin/checksum_spec.rb
+++ b/spec/unit/mixin/checksum_spec.rb
@@ -37,4 +37,18 @@ describe Chef::Mixin::Checksum do
expect(@checksum_user.checksum(@file)).to eq("09ee9c8cc70501763563bcf9c218d71b2fbf4186bf8e1e0da07f0f42c80a3394")
end
+ describe "short_cksum" do
+ context "nil provided for checksum" do
+ it "returns none" do
+ expect(@checksum_user.short_cksum(nil)).to eq("none")
+ end
+ end
+
+ context "non-nil provided for checksum" do
+ it "returns the short checksum" do
+ expect(@checksum_user.short_cksum("u7ghbxikk3i9blsimmy2y2ionmxx")).to eq("u7ghbx")
+ end
+ end
+ end
+
end
diff --git a/spec/unit/mixin/command_spec.rb b/spec/unit/mixin/command_spec.rb
deleted file mode 100644
index 0c2f6da188..0000000000
--- a/spec/unit/mixin/command_spec.rb
+++ /dev/null
@@ -1,104 +0,0 @@
-#
-# Author:: Hongli Lai (hongli@phusion.nl)
-# Copyright:: Copyright 2009-2016, Phusion
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-
-describe Chef::Mixin::Command, :volatile do
-
- if windows?
-
- skip("TODO MOVE: this is a platform specific integration test.")
-
- else
-
- describe "popen4" do
- include Chef::Mixin::Command
-
- it "should be possible to read the child process's stdout and stderr" do
- popen4("sh -c 'echo hello && echo world >&2'") do |pid, stdin, stdout, stderr|
- expect(stdout.read).to eq("hello\n")
- expect(stderr.read).to eq("world\n")
- end
- end
-
- it "should default all commands to be run in the POSIX standard C locale" do
- popen4("echo $LC_ALL") do |pid, stdin, stdout, stderr|
- expect(stdout.read.strip).to eq("C")
- end
- end
-
- it "should respect locale when specified explicitly" do
- popen4("echo $LC_ALL", :environment => { "LC_ALL" => "es" }) do |pid, stdin, stdout, stderr|
- expect(stdout.read.strip).to eq("es")
- end
- end
-
- it "should end when the child process reads from STDIN and a block is given" do
- expect {Timeout.timeout(10) do
- popen4("ruby -e 'while gets; end'", :waitlast => true) do |pid, stdin, stdout, stderr|
- (1..5).each { |i| stdin.puts "#{i}" }
- end
- end
- }.not_to raise_error
- end
-
- describe "when a process detaches but doesn't close STDOUT and STDERR [CHEF-584]" do
-
- it "returns immediately after the first child process exits" do
- expect {Timeout.timeout(10) do
- evil_forker = "exit if fork; 10.times { sleep 1}"
- popen4("ruby -e '#{evil_forker}'") do |pid, stdin, stdout, stderr|
- end
- end}.not_to raise_error
- end
-
- end
-
- end
-
- describe "run_command" do
- include Chef::Mixin::Command
-
- it "logs the command's stderr and stdout output if the command failed" do
- allow(Chef::Log).to receive(:level).and_return(:debug)
- begin
- run_command(:command => "sh -c 'echo hello; echo world >&2; false'")
- violated "Exception expected, but nothing raised."
- rescue => e
- expect(e.message).to match(/STDOUT: hello/)
- expect(e.message).to match(/STDERR: world/)
- end
- end
-
- describe "when a process detaches but doesn't close STDOUT and STDERR [CHEF-584]" do
- it "returns successfully" do
- # CHEF-2916 might have added a slight delay here, or our CI
- # infrastructure is burdened. Bumping timeout from 2 => 4 --
- # btm
- # Serdar - During Solaris tests, we've seen that processes
- # are taking a long time to exit. Bumping timeout now to 10.
- expect {Timeout.timeout(10) do
- evil_forker = "exit if fork; 10.times { sleep 1}"
- run_command(:command => "ruby -e '#{evil_forker}'")
- end}.not_to raise_error
- end
-
- end
- end
- end
-end
diff --git a/spec/unit/mixin/deprecation_spec.rb b/spec/unit/mixin/deprecation_spec.rb
index 8f22b09295..8707c6476e 100644
--- a/spec/unit/mixin/deprecation_spec.rb
+++ b/spec/unit/mixin/deprecation_spec.rb
@@ -36,7 +36,7 @@ describe Chef::Mixin do
end
it "warns when accessing the deprecated constant" do
- Chef::Mixin::DeprecatedClass
+ Chef::Mixin::DeprecatedClass # rubocop:disable Lint/Void
expect(@log_io.string).to include("This is a test deprecation")
end
end
diff --git a/spec/unit/mixin/homebrew_user_spec.rb b/spec/unit/mixin/homebrew_user_spec.rb
index de72f6b935..67d79719aa 100644
--- a/spec/unit/mixin/homebrew_user_spec.rb
+++ b/spec/unit/mixin/homebrew_user_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Joshua Timberman (<joshua@chef.io>)
#
-# Copyright 2014-2016, Chef Software, Inc <legal@chef.io>
+# Copyright 2014-2018, Chef Software, Inc <legal@chef.io>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -23,10 +23,6 @@ class ExampleHomebrewUser
end
describe Chef::Mixin::HomebrewUser do
- before(:each) do
- node.default["homebrew"]["owner"] = nil
- end
-
let(:homebrew_user) { ExampleHomebrewUser.new }
let(:node) { Chef::Node.new }
@@ -51,11 +47,11 @@ describe Chef::Mixin::HomebrewUser do
let(:user) { nil }
let(:brew_owner) { 2001 }
let(:default_brew_path) { "/usr/local/bin/brew" }
- let(:stat_double) {
+ let(:stat_double) do
d = double()
expect(d).to receive(:uid).and_return(brew_owner)
d
- }
+ end
context "debug statement prints owner name" do
diff --git a/spec/unit/mixin/openssl_helper_spec.rb b/spec/unit/mixin/openssl_helper_spec.rb
new file mode 100644
index 0000000000..6873fd8cf2
--- /dev/null
+++ b/spec/unit/mixin/openssl_helper_spec.rb
@@ -0,0 +1,252 @@
+#
+# Copyright 2009-2018, Chef Software, Inc <legal@chef.io>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "spec_helper"
+require "chef/mixin/openssl_helper"
+
+describe Chef::Mixin::OpenSSLHelper do
+ let(:instance) do
+ Class.new { include Chef::Mixin::OpenSSLHelper }.new
+ end
+
+ describe ".included" do
+ it "requires openssl" do
+ instance
+ expect(defined?(OpenSSL)).to_not be(false)
+ end
+ end
+
+ # Path helpers
+ describe "#get_key_filename" do
+ context "When the input is not a string" do
+ it "Throws a TypeError" do
+ expect do
+ instance.get_key_filename(33)
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "when the input is a string" do
+ it "Generates valid keyfile names" do
+ expect(instance.get_key_filename("/etc/temp.crt")).to match("/etc/temp.key")
+ end
+ end
+ end
+
+ # Validation helpers
+ describe "#key_length_valid?" do
+ context "When the number is less than 1024" do
+ it "returns false" do
+ expect(instance.key_length_valid?(1023)).to be_falsey
+ expect(instance.key_length_valid?(2)).to be_falsey
+ expect(instance.key_length_valid?(64)).to be_falsey
+ expect(instance.key_length_valid?(512)).to be_falsey
+ end
+ end
+
+ context "When the number is greater than 1024 but is not a power of 2" do
+ it "returns false" do
+ expect(instance.key_length_valid?(1025)).to be_falsey
+ expect(instance.key_length_valid?(6666)).to be_falsey
+ expect(instance.key_length_valid?(8191)).to be_falsey
+ end
+ end
+
+ context "When the number is a power of 2, equal to or greater than 1024" do
+ it "returns true" do
+ expect(instance.key_length_valid?(1024)).to be_truthy
+ expect(instance.key_length_valid?(2048)).to be_truthy
+ expect(instance.key_length_valid?(4096)).to be_truthy
+ expect(instance.key_length_valid?(8192)).to be_truthy
+ end
+ end
+ end
+
+ describe "#dhparam_pem_valid?" do
+ require "tempfile"
+
+ before(:each) do
+ @dhparam_file = Tempfile.new("dhparam")
+ end
+
+ context "When the dhparam.pem file does not exist" do
+ it "returns false" do
+ expect(instance.dhparam_pem_valid?("/tmp/bad_filename")).to be_falsey
+ end
+ end
+
+ context "When the dhparam.pem file does exist, but does not contain a valid dhparam key" do
+ it "Throws an OpenSSL::PKey::DHError exception" do
+ expect do
+ @dhparam_file.puts("I_am_not_a_key_I_am_a_free_man")
+ @dhparam_file.close
+ instance.dhparam_pem_valid?(@dhparam_file.path)
+ end.to raise_error(::OpenSSL::PKey::DHError)
+ end
+ end
+
+ context "When the dhparam.pem file does exist, and does contain a vaild dhparam key" do
+ it "returns true" do
+ @dhparam_file.puts(::OpenSSL::PKey::DH.new(1024).to_pem)
+ @dhparam_file.close
+ expect(instance.dhparam_pem_valid?(@dhparam_file.path)).to be_truthy
+ end
+ end
+
+ after(:each) do
+ @dhparam_file.unlink
+ end
+ end
+
+ describe "#priv_key_file_valid?" do
+ require "tempfile"
+ require "openssl" unless defined?(OpenSSL)
+
+ cipher = ::OpenSSL::Cipher.new("des3")
+
+ before(:each) do
+ @keyfile = Tempfile.new("keyfile")
+ end
+
+ context "When the key file does not exist" do
+ it "returns false" do
+ expect(instance.priv_key_file_valid?("/tmp/bad_filename")).to be_falsey
+ end
+ end
+
+ context "When the key file does exist, but does not contain a valid rsa private key" do
+ it "Throws an OpenSSL::PKey::RSAError exception" do
+ @keyfile.write("I_am_not_a_key_I_am_a_free_man")
+ @keyfile.close
+ expect(instance.priv_key_file_valid?(@keyfile.path)).to be_falsey
+ end
+ end
+
+ context "When the key file does exist, and does contain a vaild rsa private key" do
+ it "returns true" do
+ @keyfile.write(::OpenSSL::PKey::RSA.new(1024).to_pem)
+ @keyfile.close
+ expect(instance.priv_key_file_valid?(@keyfile.path)).to be_truthy
+ end
+ end
+
+ context "When a valid keyfile requires a passphrase, and an invalid passphrase is supplied" do
+ it "returns false" do
+ @keyfile.write(::OpenSSL::PKey::RSA.new(1024).to_pem(cipher, "oink"))
+ @keyfile.close
+ expect(instance.priv_key_file_valid?(@keyfile.path, "poml")).to be_falsey
+ end
+ end
+
+ context "When a valid keyfile requires a passphrase, and a valid passphrase is supplied" do
+ it "returns true" do
+ @keyfile.write(::OpenSSL::PKey::RSA.new(1024).to_pem(cipher, "oink"))
+ @keyfile.close
+ expect(instance.priv_key_file_valid?(@keyfile.path, "oink")).to be_truthy
+ end
+ end
+
+ after(:each) do
+ @keyfile.unlink
+ end
+ end
+
+ # Generators
+ describe "#gen_dhparam" do
+ context "When given an invalid key length" do
+ it "Throws an ArgumentError" do
+ expect do
+ instance.gen_dhparam(2046, 2)
+ end.to raise_error(ArgumentError)
+ end
+ end
+
+ context "When given an invalid generator id" do
+ it "Throws a TypeError" do
+ expect do
+ instance.gen_dhparam(2048, "bob")
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When a proper key length and generator id are given" do
+ it "Generates a dhparam object" do
+ expect(instance.gen_dhparam(1024, 2)).to be_kind_of(::OpenSSL::PKey::DH)
+ end
+ end
+ end
+
+ describe "#gen_rsa_priv_key" do
+ context "When given an invalid key length" do
+ it "Throws an ArgumentError" do
+ expect do
+ instance.gen_rsa_priv_key(4093)
+ end.to raise_error(ArgumentError)
+ end
+ end
+
+ context "When a proper key length is given" do
+ it "Generates an RSA key object" do
+ expect(instance.gen_rsa_priv_key(1024)).to be_kind_of(::OpenSSL::PKey::RSA)
+ end
+ end
+ end
+
+ describe "#encrypt_rsa_key" do
+ before(:all) do
+ @rsa_key = ::OpenSSL::PKey::RSA.new(1024)
+ end
+
+ context "When given anything other than an RSA key object to encrypt" do
+ it "Raises a TypeError" do
+ expect do
+ instance.encrypt_rsa_key("abcd", "efgh", "des3")
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When given anything other than a string as the passphrase" do
+ it "Raises a TypeError" do
+ expect do
+ instance.encrypt_rsa_key(@rsa_key, 1234, "des3")
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When given anything other than a string as the cipher" do
+ it "Raises a TypeError" do
+ expect do
+ instance.encrypt_rsa_key(@rsa_key, "1234", 1234)
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When given an invalid cipher string" do
+ it "Raises an ArgumentError" do
+ expect do
+ instance.encrypt_rsa_key(@rsa_key, "1234", "des3_bogus")
+ end.to raise_error(ArgumentError)
+ end
+ end
+
+ context "When given a valid RSA key and a valid passphrase string" do
+ it "Generates a valid encrypted PEM" do
+ @encrypted_key = instance.encrypt_rsa_key(@rsa_key, "oink", "des3")
+ expect(@encrypted_key).to be_kind_of(String)
+ expect(::OpenSSL::PKey::RSA.new(@encrypted_key, "oink").private?).to be_truthy
+ end
+ end
+ end
+end
diff --git a/spec/unit/mixin/params_validate_spec.rb b/spec/unit/mixin/params_validate_spec.rb
index dcee123982..7bc8a27398 100644
--- a/spec/unit/mixin/params_validate_spec.rb
+++ b/spec/unit/mixin/params_validate_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -56,7 +56,7 @@ describe Chef::Mixin::ParamsValidate do
end
it "should allow you to check what kind_of? thing an argument is with kind_of" do
- expect {
+ expect do
@vo.validate(
{ :one => "string" },
{
@@ -65,9 +65,9 @@ describe Chef::Mixin::ParamsValidate do
},
}
)
- }.not_to raise_error
+ end.not_to raise_error
- expect {
+ expect do
@vo.validate(
{ :one => "string" },
{
@@ -76,11 +76,11 @@ describe Chef::Mixin::ParamsValidate do
},
}
)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should allow you to specify an argument is required with required" do
- expect {
+ expect do
@vo.validate(
{ :one => "string" },
{
@@ -89,9 +89,9 @@ describe Chef::Mixin::ParamsValidate do
},
}
)
- }.not_to raise_error
+ end.not_to raise_error
- expect {
+ expect do
@vo.validate(
{ :two => "string" },
{
@@ -100,9 +100,9 @@ describe Chef::Mixin::ParamsValidate do
},
}
)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
- expect {
+ expect do
@vo.validate(
{ :two => "string" },
{
@@ -111,11 +111,11 @@ describe Chef::Mixin::ParamsValidate do
},
}
)
- }.not_to raise_error
+ end.not_to raise_error
end
it "should allow you to specify whether an object has a method with respond_to" do
- expect {
+ expect do
@vo.validate(
{ :one => @vo },
{
@@ -124,9 +124,9 @@ describe Chef::Mixin::ParamsValidate do
},
}
)
- }.not_to raise_error
+ end.not_to raise_error
- expect {
+ expect do
@vo.validate(
{ :one => @vo },
{
@@ -135,11 +135,11 @@ describe Chef::Mixin::ParamsValidate do
},
}
)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should allow you to specify whether an object has all the given methods with respond_to and an array" do
- expect {
+ expect do
@vo.validate(
{ :one => @vo },
{
@@ -148,9 +148,9 @@ describe Chef::Mixin::ParamsValidate do
},
}
)
- }.not_to raise_error
+ end.not_to raise_error
- expect {
+ expect do
@vo.validate(
{ :one => @vo },
{
@@ -159,7 +159,7 @@ describe Chef::Mixin::ParamsValidate do
},
}
)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should let you set a default value with default => value" do
@@ -173,7 +173,7 @@ describe Chef::Mixin::ParamsValidate do
end
it "should let you check regular expressions" do
- expect {
+ expect do
@vo.validate(
{ :one => "is good" },
{
@@ -182,9 +182,9 @@ describe Chef::Mixin::ParamsValidate do
},
}
)
- }.not_to raise_error
+ end.not_to raise_error
- expect {
+ expect do
@vo.validate(
{ :one => "is good" },
{
@@ -193,44 +193,44 @@ describe Chef::Mixin::ParamsValidate do
},
}
)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should let you specify your own callbacks" do
- expect {
+ expect do
@vo.validate(
{ :one => "is good" },
{
:one => {
:callbacks => {
- "should be equal to is good" => lambda { |a|
+ "should be equal to is good" => lambda do |a|
a == "is good"
- },
+ end,
},
},
}
)
- }.not_to raise_error
+ end.not_to raise_error
- expect {
+ expect do
@vo.validate(
{ :one => "is bad" },
{
:one => {
:callbacks => {
- "should be equal to 'is good'" => lambda { |a|
+ "should be equal to 'is good'" => lambda do |a|
a == "is good"
- },
+ end,
},
},
}
)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should let you combine checks" do
args = { :one => "is good", :two => "is bad" }
- expect {
+ expect do
@vo.validate(
args,
{
@@ -239,9 +239,9 @@ describe Chef::Mixin::ParamsValidate do
:respond_to => [ :to_s, :upcase ],
:regex => /^is good/,
:callbacks => {
- "should be your friend" => lambda { |a|
+ "should be your friend" => lambda do |a|
a == "is good"
- },
+ end,
},
:required => true,
},
@@ -252,9 +252,9 @@ describe Chef::Mixin::ParamsValidate do
:three => { :default => "neato mosquito" },
}
)
- }.not_to raise_error
+ end.not_to raise_error
expect(args[:three]).to eq("neato mosquito")
- expect {
+ expect do
@vo.validate(
args,
{
@@ -263,9 +263,9 @@ describe Chef::Mixin::ParamsValidate do
:respond_to => [ :to_s, :upcase ],
:regex => /^is good/,
:callbacks => {
- "should be your friend" => lambda { |a|
+ "should be your friend" => lambda do |a|
a == "is good"
- },
+ end,
},
:required => true,
},
@@ -276,11 +276,12 @@ describe Chef::Mixin::ParamsValidate do
:three => { :default => "neato mosquito" },
}
)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should raise an ArgumentError if the validation map has an unknown check" do
- expect { @vo.validate(
+ expect do
+ @vo.validate(
{ :one => "two" },
{
:one => {
@@ -288,17 +289,17 @@ describe Chef::Mixin::ParamsValidate do
},
}
)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should accept keys that are strings in the options" do
- expect {
+ expect do
@vo.validate({ "one" => "two" }, { :one => { :regex => /^two$/ } })
- }.not_to raise_error
+ end.not_to raise_error
end
it "should allow an array to kind_of" do
- expect {
+ expect do
@vo.validate(
{ :one => "string" },
{
@@ -307,8 +308,8 @@ describe Chef::Mixin::ParamsValidate do
},
}
)
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
@vo.validate(
{ :one => ["string"] },
{
@@ -317,8 +318,8 @@ describe Chef::Mixin::ParamsValidate do
},
}
)
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
@vo.validate(
{ :one => Hash.new },
{
@@ -327,7 +328,7 @@ describe Chef::Mixin::ParamsValidate do
},
}
)
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "asserts that a value returns false from a predicate method" do
@@ -341,6 +342,17 @@ describe Chef::Mixin::ParamsValidate do
end.to raise_error(Chef::Exceptions::ValidationFailed)
end
+ it "allows a custom validation message" do
+ expect do
+ @vo.validate({ :not_blank => "should pass" },
+ { :not_blank => { :cannot_be => [ :nil, :empty ], validation_message: "my validation message" } })
+ end.not_to raise_error
+ expect do
+ @vo.validate({ :not_blank => "" },
+ { :not_blank => { :cannot_be => [ :nil, :empty ], validation_message: "my validation message" } })
+ end.to raise_error(Chef::Exceptions::ValidationFailed, "my validation message")
+ end
+
it "should set and return a value, then return the same value" do
value = "meow"
expect(@vo.set_or_return(:test, value, {}).object_id).to eq(value.object_id)
@@ -354,15 +366,15 @@ describe Chef::Mixin::ParamsValidate do
end
it "should raise an ArgumentError when argument is nil and required is true" do
- expect {
+ expect do
@vo.set_or_return(:test, nil, { :required => true })
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should not raise an error when argument is nil and required is false" do
- expect {
+ expect do
@vo.set_or_return(:test, nil, { :required => false })
- }.not_to raise_error
+ end.not_to raise_error
end
it "should set and return @name, then return @name for foo when argument is nil" do
diff --git a/spec/unit/mixin/path_sanity_spec.rb b/spec/unit/mixin/path_sanity_spec.rb
index 675b5722be..c9ed4e2595 100644
--- a/spec/unit/mixin/path_sanity_spec.rb
+++ b/spec/unit/mixin/path_sanity_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright 2011-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -41,19 +41,19 @@ describe Chef::Mixin::PathSanity do
it "adds all useful PATHs even if environment is an empty hash" do
env = {}
@sanity.enforce_path_sanity(env)
- expect(env["PATH"]).to eq("#{@ruby_bindir}:#{@gem_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
+ expect(env["PATH"]).to eq("#{@gem_bindir}:#{@ruby_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
end
it "adds all useful PATHs that are not yet in PATH to PATH" do
env = { "PATH" => "" }
@sanity.enforce_path_sanity(env)
- expect(env["PATH"]).to eq("#{@ruby_bindir}:#{@gem_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
+ expect(env["PATH"]).to eq("#{@gem_bindir}:#{@ruby_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
end
it "does not re-add paths that already exist in PATH" do
env = { "PATH" => "/usr/bin:/sbin:/bin" }
@sanity.enforce_path_sanity(env)
- expect(env["PATH"]).to eq("/usr/bin:/sbin:/bin:#{@ruby_bindir}:#{@gem_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin")
+ expect(env["PATH"]).to eq("#{@gem_bindir}:#{@ruby_bindir}:/usr/bin:/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin")
end
it "creates path with utf-8 encoding" do
@@ -65,7 +65,7 @@ describe Chef::Mixin::PathSanity do
it "adds the current executing Ruby's bindir and Gem bindir to the PATH" do
env = { "PATH" => "" }
@sanity.enforce_path_sanity(env)
- expect(env["PATH"]).to eq("#{@ruby_bindir}:#{@gem_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
+ expect(env["PATH"]).to eq("#{@gem_bindir}:#{@ruby_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
end
it "does not create entries for Ruby/Gem bindirs if they exist in SANE_PATH or PATH" do
@@ -75,7 +75,7 @@ describe Chef::Mixin::PathSanity do
allow(RbConfig::CONFIG).to receive(:[]).with("bindir").and_return(ruby_bindir)
env = { "PATH" => gem_bindir }
@sanity.enforce_path_sanity(env)
- expect(env["PATH"]).to eq("/yo/gabba/gabba:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
+ expect(env["PATH"]).to eq("/usr/bin:/yo/gabba/gabba:/usr/local/sbin:/usr/local/bin:/usr/sbin:/sbin:/bin")
end
it "builds a valid windows path" do
@@ -86,7 +86,7 @@ describe Chef::Mixin::PathSanity do
allow(ChefConfig).to receive(:windows?).and_return(true)
env = { "PATH" => 'C:\Windows\system32;C:\mr\softie' }
@sanity.enforce_path_sanity(env)
- expect(env["PATH"]).to eq("C:\\Windows\\system32;C:\\mr\\softie;#{ruby_bindir};#{gem_bindir}")
+ expect(env["PATH"]).to eq("#{gem_bindir};#{ruby_bindir};C:\\Windows\\system32;C:\\mr\\softie")
end
end
end
diff --git a/spec/unit/mixin/powershell_exec_spec.rb b/spec/unit/mixin/powershell_exec_spec.rb
new file mode 100644
index 0000000000..72619bdc8a
--- /dev/null
+++ b/spec/unit/mixin/powershell_exec_spec.rb
@@ -0,0 +1,43 @@
+#
+# Author:: Stuart Preston (<stuart@chef.io>)
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/mixin/powershell_exec"
+
+describe Chef::Mixin::PowershellExec, :windows_only do
+ let(:powershell_mixin) { Class.new { include Chef::Mixin::PowershellExec } }
+ subject(:object) { powershell_mixin.new }
+
+ describe "#powershell_exec" do
+ it "runs a basic command and returns a Chef::PowerShell object" do
+ expect(object.powershell_exec("$PSVersionTable")).to be_kind_of(Chef::PowerShell)
+ end
+
+ it "runs a command that fails with a non-terminating error and can trap the error via .error?" do
+ execution = object.powershell_exec("this-should-error")
+ expect(execution.error?).to eql(true)
+ end
+
+ it "runs a command that fails with a non-terminating error and can list the errors" do
+ execution = object.powershell_exec("this-should-error")
+ expect(execution.errors).to be_a_kind_of(Array)
+ expect(execution.errors[0]).to be_a_kind_of(String)
+ expect(execution.errors[0]).to include("CommandNotFoundException")
+ end
+ end
+end
diff --git a/spec/unit/mixin/powershell_out_spec.rb b/spec/unit/mixin/powershell_out_spec.rb
index eae5b033f1..4ecdcb8325 100644
--- a/spec/unit/mixin/powershell_out_spec.rb
+++ b/spec/unit/mixin/powershell_out_spec.rb
@@ -18,13 +18,13 @@
require "spec_helper"
require "chef/mixin/powershell_out"
-describe Chef::Mixin::PowershellOut do
+describe Chef::Mixin::PowershellOut, :windows_only do
let(:shell_out_class) { Class.new { include Chef::Mixin::PowershellOut } }
subject(:object) { shell_out_class.new }
let(:architecture) { "something" }
- let(:flags) {
+ let(:flags) do
"-NoLogo -NonInteractive -NoProfile -ExecutionPolicy Unrestricted -InputFormat None"
- }
+ end
describe "#powershell_out" do
it "runs a command and returns the shell_out object" do
@@ -44,6 +44,18 @@ describe Chef::Mixin::PowershellOut do
).and_return(ret)
expect(object.powershell_out("Get-Process", timeout: 600)).to eql(ret)
end
+
+ context "when double quote is passed in the powershell command" do
+ it "passes if double quote is appended with single escape" do
+ result = object.powershell_out("Write-Verbose \"Some String\" -Verbose")
+ expect(result.stderr).to be == ""
+ expect(result.stdout).to be == "VERBOSE: Some String\n"
+ end
+
+ it "suppresses error if double quote is passed with double escape characters" do
+ expect { object.powershell_out("Write-Verbose \\\"Some String\\\" -Verbose") }.not_to raise_error
+ end
+ end
end
describe "#powershell_out!" do
@@ -66,5 +78,17 @@ describe Chef::Mixin::PowershellOut do
expect(mixlib_shellout).to receive(:error!)
expect(object.powershell_out!("Get-Process", timeout: 600)).to eql(mixlib_shellout)
end
+
+ context "when double quote is passed in the powershell command" do
+ it "passes if double quote is appended with single escape" do
+ result = object.powershell_out!("Write-Verbose \"Some String\" -Verbose")
+ expect(result.stderr).to be == ""
+ expect(result.stdout).to be == "VERBOSE: Some String\n"
+ end
+
+ it "raises error if double quote is passed with double escape characters" do
+ expect { object.powershell_out!("Write-Verbose \\\"Some String\\\" -Verbose") }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
+ end
+ end
end
end
diff --git a/spec/unit/mixin/powershell_type_coercions_spec.rb b/spec/unit/mixin/powershell_type_coercions_spec.rb
index 7f2ecb94e2..159a0a8d1d 100644
--- a/spec/unit/mixin/powershell_type_coercions_spec.rb
+++ b/spec/unit/mixin/powershell_type_coercions_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2015-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,12 +27,12 @@ end
describe Chef::Mixin::PowershellTypeCoercions do
let (:test_class) { Chef::PSTypeTester.new }
- describe '#translate_type' do
+ describe "#translate_type" do
it "single quotes a string" do
expect(test_class.translate_type("foo")).to eq("'foo'")
end
- ["'", '"', '#', "`"].each do |c|
+ ["'", '"', "#", "`"].each do |c|
it "base64 encodes a string that contains #{c}" do
expect(test_class.translate_type("#{c}")).to match(Base64.strict_encode64(c))
end
@@ -64,14 +64,15 @@ describe Chef::Mixin::PowershellTypeCoercions do
end
it "translates a Chef::Node::ImmutableMash like a hash" do
- test_mash = Chef::Node::ImmutableMash.new({ "a" => 1, "b" => 1.2,
- "c" => false, "d" => true })
- expect(test_class.translate_type(test_mash)).to eq("@{a=1;b=1.2;c=$false;d=$true}")
+ node = Chef::Node.new
+ node.default[:test] = { "a" => 1, "b" => 1.2, "c" => false, "d" => true }
+ expect(test_class.translate_type(node[:test])).to eq("@{a=1;b=1.2;c=$false;d=$true}")
end
it "translates a Chef::Node::ImmutableArray like an array" do
- test_array = Chef::Node::ImmutableArray.new([true, false])
- expect(test_class.translate_type(test_array)).to eq("@($true,$false)")
+ node = Chef::Node.new
+ node.default[:test] = [ true, false ]
+ expect(test_class.translate_type(node[:test])).to eq("@($true,$false)")
end
it "falls back :to_psobject if we have not defined at explicit rule" do
diff --git a/spec/unit/mixin/properties_spec.rb b/spec/unit/mixin/properties_spec.rb
index 1af0bc7abd..ee0c252381 100644
--- a/spec/unit/mixin/properties_spec.rb
+++ b/spec/unit/mixin/properties_spec.rb
@@ -11,6 +11,7 @@ module ChefMixinPropertiesSpec
property :a, "a", default: "a"
property :ab, %w{a b}, default: "a"
property :ac, %w{a c}, default: "a"
+ property :d, "d", description: "The d property", introduced: "14.0"
end
context "and a module B with properties b, ab and bc" do
@@ -30,11 +31,20 @@ module ChefMixinPropertiesSpec
end
it "A.properties has a, ab, and ac with types 'a', ['a', 'b'], and ['b', 'c']" do
- expect(A.properties.keys).to eq [ :a, :ab, :ac ]
+ expect(A.properties.keys).to eq [ :a, :ab, :ac, :d ]
expect(A.properties[:a].validation_options[:is]).to eq "a"
expect(A.properties[:ab].validation_options[:is]).to eq %w{a b}
expect(A.properties[:ac].validation_options[:is]).to eq %w{a c}
end
+
+ it "A.properties can get the description of `d`" do
+ expect(A.properties[:d].description).to eq "The d property"
+ end
+
+ it "A.properties can get the release that introduced `d`" do
+ expect(A.properties[:d].introduced).to eq "14.0"
+ end
+
it "B.properties has b, ab, and bc with types 'b', nil and ['b', 'c']" do
expect(B.properties.keys).to eq [ :b, :ab, :bc ]
expect(B.properties[:b].validation_options[:is]).to eq "b"
@@ -42,7 +52,7 @@ module ChefMixinPropertiesSpec
expect(B.properties[:bc].validation_options[:is]).to eq %w{b c}
end
it "C.properties has a, b, c, ac and bc with merged types" do
- expect(C.properties.keys).to eq [ :a, :ab, :ac, :b, :bc, :c ]
+ expect(C.properties.keys).to eq [ :a, :ab, :ac, :d, :b, :bc, :c ]
expect(C.properties[:a].validation_options[:is]).to eq "a"
expect(C.properties[:b].validation_options[:is]).to eq "b"
expect(C.properties[:c].validation_options[:is]).to eq "c"
diff --git a/spec/unit/mixin/shell_out_spec.rb b/spec/unit/mixin/shell_out_spec.rb
index 191ea920c0..9b12969026 100644
--- a/spec/unit/mixin/shell_out_spec.rb
+++ b/spec/unit/mixin/shell_out_spec.rb
@@ -21,92 +21,19 @@
#
require "spec_helper"
+require "chef/mixin/path_sanity"
describe Chef::Mixin::ShellOut do
+ include Chef::Mixin::PathSanity
+
let(:shell_out_class) { Class.new { include Chef::Mixin::ShellOut } }
subject(:shell_out_obj) { shell_out_class.new }
- describe '#run_command_compatible_options' do
- subject { shell_out_obj.run_command_compatible_options(command_args) }
- let(:command_args) { [ cmd, options ] }
- let(:cmd) { "echo '#{rand(1000)}'" }
-
- let(:output) { StringIO.new }
- let!(:capture_log_output) { Chef::Log.logger = Logger.new(output) }
- let(:assume_deprecation_log_level) { allow(Chef::Log).to receive(:level).and_return(:warn) }
-
- before do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- end
-
- context "without options" do
- let(:command_args) { [ cmd ] }
-
- it "should not edit command args" do
- is_expected.to eql(command_args)
- end
- end
-
- context "without deprecated options" do
- let(:options) { { :environment => environment } }
- let(:environment) { { "LC_ALL" => "C", "LANG" => "C", "LANGUAGE" => "C" } }
-
- it "should not edit command args" do
- is_expected.to eql(command_args)
- end
- end
-
- def self.should_emit_deprecation_warning_about(old_option, new_option)
- it "should emit a deprecation warning" do
- assume_deprecation_log_level && capture_log_output
- subject
- expect(output.string).to match /DEPRECATION:/
- expect(output.string).to match Regexp.escape(old_option.to_s)
- expect(output.string).to match Regexp.escape(new_option.to_s)
- end
- end
-
- context "with :command_log_level option" do
- let(:options) { { :command_log_level => command_log_level } }
- let(:command_log_level) { :warn }
-
- it "should convert :command_log_level to :log_level" do
- is_expected.to eql [ cmd, { :log_level => command_log_level } ]
- end
-
- should_emit_deprecation_warning_about :command_log_level, :log_level
- end
-
- context "with :command_log_prepend option" do
- let(:options) { { :command_log_prepend => command_log_prepend } }
- let(:command_log_prepend) { "PROVIDER:" }
-
- it "should convert :command_log_prepend to :log_tag" do
- is_expected.to eql [ cmd, { :log_tag => command_log_prepend } ]
- end
-
- should_emit_deprecation_warning_about :command_log_prepend, :log_tag
- end
-
- context "with 'command_log_level' option" do
- let(:options) { { "command_log_level" => command_log_level } }
- let(:command_log_level) { :warn }
-
- it "should convert 'command_log_level' to :log_level" do
- is_expected.to eql [ cmd, { :log_level => command_log_level } ]
- end
-
- should_emit_deprecation_warning_about :command_log_level, :log_level
- end
-
- context "with 'command_log_prepend' option" do
- let(:options) { { "command_log_prepend" => command_log_prepend } }
- let(:command_log_prepend) { "PROVIDER:" }
-
- it "should convert 'command_log_prepend' to :log_tag" do
- is_expected.to eql [ cmd, { :log_tag => command_log_prepend } ]
- end
- should_emit_deprecation_warning_about :command_log_prepend, :log_tag
+ def env_path
+ if Chef::Platform.windows?
+ "Path"
+ else
+ "PATH"
end
end
@@ -128,13 +55,13 @@ describe Chef::Mixin::ShellOut do
describe "when the last argument is a Hash" do
describe "and environment is an option" do
it "should not change environment language settings when they are set to nil" do
- options = { :environment => { "LC_ALL" => nil, "LANGUAGE" => nil, "LANG" => nil } }
+ options = { :environment => { "LC_ALL" => nil, "LANGUAGE" => nil, "LANG" => nil, env_path => nil } }
expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
shell_out_obj.shell_out(cmd, options)
end
it "should not change environment language settings when they are set to non-nil" do
- options = { :environment => { "LC_ALL" => "en_US.UTF-8", "LANGUAGE" => "en_US.UTF-8", "LANG" => "en_US.UTF-8" } }
+ options = { :environment => { "LC_ALL" => "en_US.UTF-8", "LANGUAGE" => "en_US.UTF-8", "LANG" => "en_US.UTF-8", env_path => "foo:bar:baz" } }
expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
shell_out_obj.shell_out(cmd, options)
end
@@ -147,6 +74,7 @@ describe Chef::Mixin::ShellOut do
"LC_ALL" => Chef::Config[:internal_locale],
"LANG" => Chef::Config[:internal_locale],
"LANGUAGE" => Chef::Config[:internal_locale],
+ env_path => sanitized_path,
},
}).and_return(true)
shell_out_obj.shell_out(cmd, options)
@@ -160,6 +88,7 @@ describe Chef::Mixin::ShellOut do
"LC_ALL" => Chef::Config[:internal_locale],
"LANG" => Chef::Config[:internal_locale],
"LANGUAGE" => Chef::Config[:internal_locale],
+ env_path => sanitized_path,
},
}).and_return(true)
shell_out_obj.shell_out(cmd, options)
@@ -169,13 +98,13 @@ describe Chef::Mixin::ShellOut do
describe "and env is an option" do
it "should not change env when langauge options are set to nil" do
- options = { :env => { "LC_ALL" => nil, "LANG" => nil, "LANGUAGE" => nil } }
+ options = { :env => { "LC_ALL" => nil, "LANG" => nil, "LANGUAGE" => nil, env_path => nil } }
expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
shell_out_obj.shell_out(cmd, options)
end
it "should not change env when language options are set to non-nil" do
- options = { :env => { "LC_ALL" => "de_DE.UTF-8", "LANG" => "de_DE.UTF-8", "LANGUAGE" => "de_DE.UTF-8" } }
+ options = { :env => { "LC_ALL" => "de_DE.UTF-8", "LANG" => "de_DE.UTF-8", "LANGUAGE" => "de_DE.UTF-8", env_path => "foo:bar:baz" } }
expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
shell_out_obj.shell_out(cmd, options)
end
@@ -188,6 +117,7 @@ describe Chef::Mixin::ShellOut do
"LC_ALL" => Chef::Config[:internal_locale],
"LANG" => Chef::Config[:internal_locale],
"LANGUAGE" => Chef::Config[:internal_locale],
+ env_path => sanitized_path,
},
}).and_return(true)
shell_out_obj.shell_out(cmd, options)
@@ -201,6 +131,7 @@ describe Chef::Mixin::ShellOut do
"LC_ALL" => Chef::Config[:internal_locale],
"LANG" => Chef::Config[:internal_locale],
"LANGUAGE" => Chef::Config[:internal_locale],
+ env_path => sanitized_path,
},
}).and_return(true)
shell_out_obj.shell_out(cmd, options)
@@ -217,6 +148,7 @@ describe Chef::Mixin::ShellOut do
"LC_ALL" => Chef::Config[:internal_locale],
"LANG" => Chef::Config[:internal_locale],
"LANGUAGE" => Chef::Config[:internal_locale],
+ env_path => sanitized_path,
},
}).and_return(true)
shell_out_obj.shell_out(cmd, options)
@@ -231,6 +163,7 @@ describe Chef::Mixin::ShellOut do
"LC_ALL" => Chef::Config[:internal_locale],
"LANG" => Chef::Config[:internal_locale],
"LANGUAGE" => Chef::Config[:internal_locale],
+ env_path => sanitized_path,
},
}).and_return(true)
shell_out_obj.shell_out(cmd)
diff --git a/spec/unit/mixin/user_context_spec.rb b/spec/unit/mixin/user_context_spec.rb
new file mode 100644
index 0000000000..3dadf6a2c3
--- /dev/null
+++ b/spec/unit/mixin/user_context_spec.rb
@@ -0,0 +1,108 @@
+#
+# Author:: Adam Edwards (<adamed@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/mixin/user_context"
+require "chef/util/windows/logon_session"
+
+describe "a class that mixes in user_context" do
+ let(:instance_with_user_context) do
+ class UserContextConsumer
+ include ::Chef::Mixin::UserContext
+ def with_context(user, domain, password, &block)
+ with_user_context(user, password, domain, &block)
+ end
+ end
+ UserContextConsumer.new
+ end
+
+ shared_examples_for "a method that requires a block" do
+ it "raises an ArgumentError exception if a block is not supplied" do
+ expect { instance_with_user_context.with_context(nil, nil, nil) }.to raise_error(ArgumentError)
+ end
+ end
+
+ context "when running on Windows" do
+ before do
+ allow(::Chef::Platform).to receive(:windows?).and_return(true)
+ allow(::Chef::Util::Windows::LogonSession).to receive(:new).and_return(logon_session)
+ end
+
+ let(:logon_session) { instance_double("::Chef::Util::Windows::LogonSession", :set_user_context => nil, :open => nil, :close => nil) }
+
+ it "does not raise an exception when the user and all parameters are nil" do
+ expect { instance_with_user_context.with_context(nil, nil, nil) {} }.not_to raise_error
+ end
+
+ context "when given valid user credentials" do
+ before do
+ expect(::Chef::Util::Windows::LogonSession).to receive(:new).and_return(logon_session)
+ end
+
+ let(:block_object) do
+ class BlockClass
+ def block_method
+ end
+ end
+ BlockClass.new
+ end
+
+ let(:block_parameter) { Proc.new { block_object.block_method } }
+
+ context "when the block doesn't raise an exception" do
+ before do
+ expect( block_object ).to receive(:block_method)
+ end
+ it "calls the supplied block" do
+ expect { instance_with_user_context.with_context("kamilah", nil, "chef4life", &block_parameter) }.not_to raise_error
+ end
+
+ it "does not raise an exception if the user, password, and domain are specified" do
+ expect { instance_with_user_context.with_context("kamilah", "xanadu", "chef4life", &block_parameter) }.not_to raise_error
+ end
+ end
+
+ context "when the block raises an exception" do
+ class UserContextTestException < RuntimeError
+ end
+ let(:block_parameter) { Proc.new { raise UserContextTextException } }
+
+ it "raises the exception raised by the block" do
+ expect { instance_with_user_context.with_context("kamilah", nil, "chef4life", &block_parameter) }.not_to raise_error(UserContextTestException)
+ end
+
+ it "closes the logon session so resources are not leaked" do
+ expect(logon_session).to receive(:close)
+ expect { instance_with_user_context.with_context("kamilah", nil, "chef4life", &block_parameter) }.not_to raise_error(UserContextTestException)
+ end
+ end
+ end
+
+ it_behaves_like "a method that requires a block"
+ end
+
+ context "when not running on Windows" do
+ before do
+ allow(::Chef::Platform).to receive(:windows?).and_return(false)
+ end
+
+ it "raises a ::Chef::Exceptions::UnsupportedPlatform exception" do
+ expect { instance_with_user_context.with_context(nil, nil, nil) {} }.to raise_error(::Chef::Exceptions::UnsupportedPlatform)
+ end
+ end
+end
diff --git a/spec/unit/mixin/versioned_api_spec.rb b/spec/unit/mixin/versioned_api_spec.rb
new file mode 100644
index 0000000000..e8b65158c4
--- /dev/null
+++ b/spec/unit/mixin/versioned_api_spec.rb
@@ -0,0 +1,128 @@
+#
+# Copyright:: Copyright 2015-2017, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/mixin/versioned_api"
+
+describe Chef::Mixin::VersionedAPI do
+ let(:dummy_class) { Class.new { extend Chef::Mixin::VersionedAPI } }
+
+ it "allows a class to declare the minimum supported API version" do
+ dummy_class.minimum_api_version 3
+ expect(dummy_class.minimum_api_version).to eq(3)
+ end
+end
+
+describe Chef::Mixin::VersionedAPIFactory do
+ class V1Class; extend Chef::Mixin::VersionedAPI; minimum_api_version 1; end
+ class V2Class; extend Chef::Mixin::VersionedAPI; minimum_api_version 2; end
+ class V3Class; extend Chef::Mixin::VersionedAPI; minimum_api_version 3; end
+
+ let(:factory_class) { Class.new { extend Chef::Mixin::VersionedAPIFactory } }
+
+ before do
+ Chef::ServerAPIVersions.instance.reset!
+ end
+
+ describe "#add_versioned_api_class" do
+ it "adds a target class" do
+ factory_class.add_versioned_api_class V1Class
+ expect(factory_class.versioned_interfaces).to eq([V1Class])
+ end
+
+ it "can be called many times" do
+ factory_class.add_versioned_api_class V1Class
+ factory_class.add_versioned_api_class V2Class
+ expect(factory_class.versioned_interfaces).to eq([V1Class, V2Class])
+ end
+ end
+
+ describe "#versioned_api_class" do
+ describe "with no known versions" do
+ it "with one class it returns that class" do
+ factory_class.add_versioned_api_class V2Class
+ expect(factory_class.versioned_api_class.minimum_api_version).to eq(2)
+ end
+
+ it "with many classes it returns the highest minimum version" do
+ factory_class.add_versioned_api_class V1Class
+ factory_class.add_versioned_api_class V2Class
+ factory_class.add_versioned_api_class V3Class
+ expect(factory_class.versioned_api_class.minimum_api_version).to eq(3)
+ end
+ end
+
+ describe "with a known version" do
+ it "with one class it returns that class" do
+ Chef::ServerAPIVersions.instance.set_versions({ "min_version" => 0, "max_version" => 2 })
+ factory_class.add_versioned_api_class V2Class
+ expect(factory_class.versioned_api_class.minimum_api_version).to eq(2)
+ end
+
+ it "with a maximum version it returns the highest possible versioned class" do
+ Chef::ServerAPIVersions.instance.set_versions({ "min_version" => 0, "max_version" => 2 })
+ factory_class.add_versioned_api_class V1Class
+ factory_class.add_versioned_api_class V2Class
+ factory_class.add_versioned_api_class V3Class
+ expect(factory_class.versioned_api_class.minimum_api_version).to eq(2)
+ end
+ end
+
+ it "with no classes it returns nil" do
+ expect(factory_class.versioned_api_class).to be_nil
+ end
+ end
+
+ describe "#best_request_version" do
+ it "returns a String" do
+ factory_class.add_versioned_api_class V2Class
+ expect(factory_class.best_request_version).to be_a(String)
+ end
+
+ it "returns the most relevant version" do
+ factory_class.add_versioned_api_class V2Class
+ factory_class.add_versioned_api_class V3Class
+ expect(factory_class.best_request_version).to eq("3")
+ end
+ end
+
+ describe "#possible_requests" do
+ it "returns the number of registered classes" do
+ factory_class.add_versioned_api_class V2Class
+ factory_class.add_versioned_api_class V3Class
+ expect(factory_class.possible_requests).to eq(2)
+ end
+ end
+
+ describe "#new" do
+ it "creates an instance of the versioned class" do
+ factory_class.add_versioned_api_class V2Class
+ expect { factory_class.new }.to_not raise_error
+ expect(factory_class.new.class).to eq(V2Class)
+ end
+ end
+
+ describe "#def_versioned_delegator" do
+ it "delegates the method to the correct class" do
+ factory_class.add_versioned_api_class V2Class
+ factory_class.def_versioned_delegator("test_method")
+ expect(V2Class).to receive(:test_method).with("test message").and_return(true)
+
+ factory_class.test_method("test message")
+ end
+ end
+end
diff --git a/spec/unit/mixin/which.rb b/spec/unit/mixin/which.rb
new file mode 100644
index 0000000000..1764b3b89f
--- /dev/null
+++ b/spec/unit/mixin/which.rb
@@ -0,0 +1,160 @@
+#
+# Copyright:: Copyright 2011-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+class TestClass
+ include Chef::Mixin::Which
+end
+
+describe Chef::Mixin::Which do
+
+ let(:test) { TestClass.new }
+
+ describe "#which" do
+ def self.test_which(description, *args, finds: nil, others: [], directory: false, &block)
+ it description do
+ # stub the ENV['PATH']
+ expect(test).to receive(:env_path).and_return(["/dir1", "/dir2" ].join(File::PATH_SEPARATOR))
+
+ # most files should not be found
+ allow(File).to receive(:executable?).and_return(false)
+ allow(File).to receive(:directory?).and_return(false)
+
+ # stub the expectation
+ expect(File).to receive(:executable?).with(finds).and_return(true) if finds
+
+ # if the file we find is a directory
+ expect(File).to receive(:directory?).with(finds).and_return(true) if finds && directory
+
+ # allow for stubbing other paths to exist that we should not find
+ others.each do |other|
+ allow(File).to receive(:executable?).with(other).and_return(true)
+ end
+
+ # setup the actual expectation on the return value
+ if finds && !directory
+ expect(test.which(*args, &block)).to eql(finds)
+ else
+ expect(test.which(*args, &block)).to eql(false)
+ end
+ end
+ end
+
+ context "simple usage" do
+ test_which("returns false when it does not find anything", "foo1")
+
+ ["/dir1", "/dir2", "/bin", "/usr/bin", "/sbin", "/usr/sbin" ].each do |dir|
+ test_which("finds `foo1` in #{dir} when it is stubbed", "foo1", finds: "#{dir}/foo1")
+ end
+
+ test_which("does not find an executable directory", "foo1", finds: "/dir1/foo1", directory: true)
+ end
+
+ context "with an array of args" do
+ test_which("finds the first arg", "foo1", "foo2", finds: "/dir2/foo1")
+
+ test_which("finds the second arg", "foo1", "foo2", finds: "/dir2/foo2")
+
+ test_which("finds the first arg when there's both", "foo1", "foo2", finds: "/dir2/foo1", others: [ "/dir1/foo2" ])
+
+ test_which("and the directory order can be reversed", "foo1", "foo2", finds: "/dir1/foo1", others: [ "/dir2/foo2" ])
+
+ test_which("or be the same", "foo1", "foo2", finds: "/dir1/foo1", others: [ "/dir1/foo2" ])
+ end
+
+ context "with a block" do
+ test_which("doesnt find it if its false", "foo1", others: [ "/dir1/foo1" ]) do |f|
+ false
+ end
+
+ test_which("finds it if its true", "foo1", finds: "/dir1/foo1") do |f|
+ true
+ end
+
+ test_which("passes in the filename as the arg", "foo1", finds: "/dir1/foo1") do |f|
+ raise "bad arg to block" unless f == "/dir1/foo1"
+ true
+ end
+
+ test_which("arrays with blocks", "foo1", "foo2", finds: "/dir2/foo1", others: [ "/dir1/foo2" ]) do |f|
+ raise "bad arg to block" unless f == "/dir2/foo1" || f == "/dir1/foo2"
+ true
+ end
+ end
+ end
+
+ describe "#where" do
+ def self.test_where(description, *args, finds: [], others: [], &block)
+ it description do
+ # stub the ENV['PATH']
+ expect(test).to receive(:env_path).and_return(["/dir1", "/dir2" ].join(File::PATH_SEPARATOR))
+
+ # most files should not be found
+ allow(File).to receive(:executable?).and_return(false)
+ allow(File).to receive(:directory?).and_return(false)
+
+ # allow for stubbing other paths to exist that we should not return
+ others.each do |other|
+ allow(File).to receive(:executable?).with(other).and_return(true)
+ end
+
+ # stub the expectation
+ finds.each do |path|
+ expect(File).to receive(:executable?).with(path).and_return(true)
+ end
+
+ # setup the actual expectation on the return value
+ expect(test.where(*args, &block)).to eql(finds)
+ end
+ end
+
+ context "simple usage" do
+ test_where("returns empty array when it doesn't find anything", "foo1")
+
+ ["/dir1", "/dir2", "/bin", "/usr/bin", "/sbin", "/usr/sbin" ].each do |dir|
+ test_where("finds `foo1` in #{dir} when it is stubbed", "foo1", finds: [ "#{dir}/foo1" ])
+ end
+
+ test_where("finds `foo1` in all directories", "foo1", finds: [ "/dir1/foo1", "/dir2/foo1" ])
+ end
+
+ context "with an array of args" do
+ test_where("finds the first arg", "foo1", "foo2", finds: [ "/dir2/foo1" ])
+
+ test_where("finds the second arg", "foo1", "foo2", finds: [ "/dir2/foo2" ])
+
+ test_where("finds foo1 before foo2", "foo1", "foo2", finds: [ "/dir2/foo1", "/dir1/foo2" ])
+
+ test_where("finds foo1 before foo2 if the dirs are reversed", "foo1", "foo2", finds: [ "/dir1/foo1", "/dir2/foo2" ])
+
+ test_where("finds them both in the same directory", "foo1", "foo2", finds: [ "/dir1/foo1", "/dir1/foo2" ])
+
+ test_where("finds foo2 first if they're reversed", "foo2", "foo1", finds: [ "/dir1/foo2", "/dir1/foo1" ])
+ end
+
+ context "with a block do" do
+ test_where("finds foo1 and foo2 if they exist and the block is true", "foo1", "foo2", finds: [ "/dir1/foo2", "/dir2/foo2" ]) do
+ true
+ end
+
+ test_where("does not finds foo1 and foo2 if they exist and the block is false", "foo1", "foo2", others: [ "/dir1/foo2", "/dir2/foo2" ]) do
+ false
+ end
+ end
+ end
+end
diff --git a/spec/unit/mixin/xml_escape_spec.rb b/spec/unit/mixin/xml_escape_spec.rb
index 903c91164e..495ad0662c 100644
--- a/spec/unit/mixin/xml_escape_spec.rb
+++ b/spec/unit/mixin/xml_escape_spec.rb
@@ -37,7 +37,7 @@ describe Chef::Mixin::XMLEscape do
end
it "does not modify ASCII strings" do
- expect(@escaper.xml_escape('foobarbaz!@#$%^*()')).to eq('foobarbaz!@#$%^*()')
+ expect(@escaper.xml_escape("foobarbaz!@\#$%^*()")).to eq("foobarbaz!@\#$%^*()")
end
it "converts invalid bytes to asterisks" do
@@ -45,10 +45,10 @@ describe Chef::Mixin::XMLEscape do
end
it "converts UTF-8 correctly" do
- expect(@escaper.xml_escape("\xC2\xA9")).to eq('&#169;')
+ expect(@escaper.xml_escape("\xC2\xA9")).to eq("&#169;")
end
it "converts win 1252 characters correctly" do
- expect(@escaper.xml_escape("#{0x80.chr}")).to eq('&#8364;')
+ expect(@escaper.xml_escape("#{0x80.chr}")).to eq("&#8364;")
end
end
diff --git a/spec/unit/node/attribute_spec.rb b/spec/unit/node/attribute_spec.rb
index 57ad3c0c25..557c469216 100644
--- a/spec/unit/node/attribute_spec.rb
+++ b/spec/unit/node/attribute_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,11 @@ require "spec_helper"
require "chef/node/attribute"
describe Chef::Node::Attribute do
+ let(:events) { instance_double(Chef::EventDispatch::Dispatcher) }
+ let(:run_context) { instance_double(Chef::RunContext, :events => events) }
+ let(:node) { instance_double(Chef::Node, :run_context => run_context) }
before(:each) do
+ allow(events).to receive(:attribute_changed)
@attribute_hash =
{ "dmi" => {},
"command" => { "ps" => "ps -ef" },
@@ -166,7 +170,7 @@ describe Chef::Node::Attribute do
},
}
@automatic_hash = { "week" => "friday" }
- @attributes = Chef::Node::Attribute.new(@attribute_hash, @default_hash, @override_hash, @automatic_hash)
+ @attributes = Chef::Node::Attribute.new(@attribute_hash, @default_hash, @override_hash, @automatic_hash, node)
end
describe "initialize" do
@@ -204,7 +208,7 @@ describe Chef::Node::Attribute do
end
describe "when debugging attributes" do
- before do
+ it "gives the value at each level of precedence for a path spec" do
@attributes.default[:foo][:bar] = "default"
@attributes.env_default[:foo][:bar] = "env_default"
@attributes.role_default[:foo][:bar] = "role_default"
@@ -215,10 +219,8 @@ describe Chef::Node::Attribute do
@attributes.env_override[:foo][:bar] = "env_override"
@attributes.force_override[:foo][:bar] = "force_override"
@attributes.automatic[:foo][:bar] = "automatic"
- end
- it "gives the value at each level of precedence for a path spec" do
- expected = [["set_unless_enabled?", false],
+ expected = [
%w{default default},
%w{env_default env_default},
%w{role_default role_default},
@@ -232,6 +234,25 @@ describe Chef::Node::Attribute do
]
expect(@attributes.debug_value(:foo, :bar)).to eq(expected)
end
+
+ it "works through arrays" do
+ @attributes.default["foo"] = [ { "bar" => "baz" } ]
+
+ expect(@attributes.debug_value(:foo, 0)).to eq(
+ [
+ ["default", { "bar" => "baz" }],
+ ["env_default", :not_present],
+ ["role_default", :not_present],
+ ["force_default", :not_present],
+ ["normal", :not_present],
+ ["override", :not_present],
+ ["role_override", :not_present],
+ ["env_override", :not_present],
+ ["force_override", :not_present],
+ ["automatic", :not_present],
+ ]
+ )
+ end
end
describe "when fetching values based on precedence" do
@@ -417,12 +438,6 @@ describe Chef::Node::Attribute do
expect(@attributes.normal["foo"]["bar"]).to eq(:baz)
end
- it "should optionally skip setting the value if one already exists" do
- @attributes.set_unless_value_present = true
- @attributes.normal["hostname"] = "bar"
- expect(@attributes["hostname"]).to eq("latte")
- end
-
it "does not support ||= when setting" do
# This is a limitation of auto-vivification.
# Users who need this behavior can use set_unless and friends
@@ -461,8 +476,7 @@ describe Chef::Node::Attribute do
expect(@attributes.default["foo"]).to eql({ "bar" => [ "fizz" ] })
end
- it "mutating strings should not mutate the attributes" do
- pending "this is a bug that should be fixed"
+ it "mutating strings should not mutate the attributes in a hash" do
@attributes.default["foo"]["bar"]["baz"] = "fizz"
hash = @attributes["foo"].to_hash
expect(hash).to eql({ "bar" => { "baz" => "fizz" } })
@@ -470,6 +484,15 @@ describe Chef::Node::Attribute do
expect(hash).to eql({ "bar" => { "baz" => "fizzbuzz" } })
expect(@attributes.default["foo"]).to eql({ "bar" => { "baz" => "fizz" } })
end
+
+ it "mutating array elements should not mutate the attributes" do
+ @attributes.default["foo"]["bar"] = [ "fizz" ]
+ hash = @attributes["foo"].to_hash
+ expect(hash).to eql({ "bar" => [ "fizz" ] })
+ hash["bar"][0] << "buzz"
+ expect(hash).to eql({ "bar" => [ "fizzbuzz" ] })
+ expect(@attributes.default["foo"]).to eql({ "bar" => [ "fizz" ] })
+ end
end
describe "dup" do
@@ -477,6 +500,24 @@ describe Chef::Node::Attribute do
@attributes.default[:foo] = %w{foo bar baz} + Array(1..3) + [nil, true, false, [ "el", 0, nil ] ]
@attributes.default[:foo].dup
end
+
+ it "mutating strings should not mutate the attributes in a hash" do
+ @attributes.default["foo"]["bar"]["baz"] = "fizz"
+ hash = @attributes["foo"].dup
+ expect(hash).to eql({ "bar" => { "baz" => "fizz" } })
+ hash["bar"]["baz"] << "buzz"
+ expect(hash).to eql({ "bar" => { "baz" => "fizzbuzz" } })
+ expect(@attributes.default["foo"]).to eql({ "bar" => { "baz" => "fizz" } })
+ end
+
+ it "mutating array elements should not mutate the attributes" do
+ @attributes.default["foo"]["bar"] = [ "fizz" ]
+ hash = @attributes["foo"].dup
+ expect(hash).to eql({ "bar" => [ "fizz" ] })
+ hash["bar"][0] << "buzz"
+ expect(hash).to eql({ "bar" => [ "fizzbuzz" ] })
+ expect(@attributes.default["foo"]).to eql({ "bar" => [ "fizz" ] })
+ end
end
describe "has_key?" do
@@ -492,10 +533,6 @@ describe Chef::Node::Attribute do
expect(@attributes.has_key?("does_not_exist_at_all")).to eq(false)
end
- it "should return true if an attribute exists but is set to nil using dot notation" do
- expect(@attributes.music.deeper.has_key?("gates_of_ishtar")).to eq(true)
- end
-
it "should return true if an attribute exists but is set to false" do
@attributes.has_key?("music")
expect(@attributes["music"].has_key?("apophis")).to eq(true)
@@ -531,17 +568,6 @@ describe Chef::Node::Attribute do
end
- describe "method_missing" do
- it "should behave like a [] lookup" do
- expect(@attributes.music.mastodon).to eq("rocks")
- end
-
- it "should allow the last method to set a value if it has an = sign on the end" do
- @attributes.normal.music.mastodon = %w{dream still shining}
- expect(@attributes.normal.music.mastodon).to eq(%w{dream still shining})
- end
- end
-
describe "keys" do
before(:each) do
@attributes = Chef::Node::Attribute.new(
@@ -564,7 +590,7 @@ describe Chef::Node::Attribute do
it "should yield each top level key" do
collect = Array.new
- @attributes.keys.each do |k|
+ @attributes.each_key do |k|
collect << k
end
expect(collect.include?("one")).to eq(true)
@@ -577,7 +603,7 @@ describe Chef::Node::Attribute do
it "should yield lower if we go deeper" do
collect = Array.new
- @attributes.one.keys.each do |k|
+ @attributes["one"].each_key do |k|
collect << k
end
expect(collect.include?("two")).to eq(true)
@@ -587,7 +613,7 @@ describe Chef::Node::Attribute do
end
it "should not raise an exception if one of the hashes has a nil value on a deep lookup" do
- expect { @attributes.place.keys { |k| } }.not_to raise_error
+ expect { @attributes["place"].keys { |k| } }.not_to raise_error
end
end
@@ -1108,25 +1134,25 @@ describe Chef::Node::Attribute do
describe "when setting a component attribute to a new value" do
it "converts the input in to a VividMash tree (default)" do
@attributes.default = {}
- @attributes.default.foo = "bar"
+ @attributes.default["foo"] = "bar"
expect(@attributes.merged_attributes[:foo]).to eq("bar")
end
it "converts the input in to a VividMash tree (normal)" do
@attributes.normal = {}
- @attributes.normal.foo = "bar"
+ @attributes.normal["foo"] = "bar"
expect(@attributes.merged_attributes[:foo]).to eq("bar")
end
it "converts the input in to a VividMash tree (override)" do
@attributes.override = {}
- @attributes.override.foo = "bar"
+ @attributes.override["foo"] = "bar"
expect(@attributes.merged_attributes[:foo]).to eq("bar")
end
it "converts the input in to a VividMash tree (automatic)" do
@attributes.automatic = {}
- @attributes.automatic.foo = "bar"
+ @attributes.automatic["foo"] = "bar"
expect(@attributes.merged_attributes[:foo]).to eq("bar")
end
end
@@ -1169,11 +1195,82 @@ describe Chef::Node::Attribute do
it "raises an error when using []=" do
expect { @attributes[:new_key] = "new value" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
end
+ end
+
+ describe "deeply converting values" do
+ it "converts values through an array" do
+ @attributes.default[:foo] = [ { bar: true } ]
+ expect(@attributes["foo"].class).to eql(Chef::Node::ImmutableArray)
+ expect(@attributes["foo"][0].class).to eql(Chef::Node::ImmutableMash)
+ expect(@attributes["foo"][0]["bar"]).to be true
+ end
+
+ it "converts values through nested arrays" do
+ @attributes.default[:foo] = [ [ { bar: true } ] ]
+ expect(@attributes["foo"].class).to eql(Chef::Node::ImmutableArray)
+ expect(@attributes["foo"][0].class).to eql(Chef::Node::ImmutableArray)
+ expect(@attributes["foo"][0][0].class).to eql(Chef::Node::ImmutableMash)
+ expect(@attributes["foo"][0][0]["bar"]).to be true
+ end
+
+ it "converts values through nested hashes" do
+ @attributes.default[:foo] = { baz: { bar: true } }
+ expect(@attributes["foo"].class).to eql(Chef::Node::ImmutableMash)
+ expect(@attributes["foo"]["baz"].class).to eql(Chef::Node::ImmutableMash)
+ expect(@attributes["foo"]["baz"]["bar"]).to be true
+ end
+ end
+
+ describe "node state" do
+ it "sets __root__ correctly" do
+ @attributes.default["foo"]["bar"]["baz"] = "quux"
+ expect(@attributes["foo"].__root__).to eql(@attributes)
+ expect(@attributes["foo"]["bar"].__root__).to eql(@attributes)
+ expect(@attributes.default["foo"].__root__).to eql(@attributes)
+ expect(@attributes.default["foo"]["bar"].__root__).to eql(@attributes)
+ end
+
+ it "sets __node__ correctly" do
+ @attributes.default["foo"]["bar"]["baz"] = "quux"
+ expect(@attributes["foo"].__node__).to eql(node)
+ expect(@attributes["foo"]["bar"].__node__).to eql(node)
+ expect(@attributes.default["foo"].__node__).to eql(node)
+ expect(@attributes.default["foo"]["bar"].__node__).to eql(node)
+ end
+
+ it "sets __path__ correctly" do
+ @attributes.default["foo"]["bar"]["baz"] = "quux"
+ expect(@attributes["foo"].__path__).to eql(["foo"])
+ expect(@attributes["foo"]["bar"].__path__).to eql(%w{foo bar})
+ expect(@attributes.default["foo"].__path__).to eql(["foo"])
+ expect(@attributes.default["foo"]["bar"].__path__).to eql(%w{foo bar})
+ end
- it "raises an error when using `attr=value`" do
- expect { @attributes.new_key = "new value" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
+ it "sets __precedence__ correctly" do
+ @attributes.default["foo"]["bar"]["baz"] = "quux"
+ expect(@attributes["foo"].__precedence__).to eql(:merged)
+ expect(@attributes["foo"]["bar"].__precedence__).to eql(:merged)
+ expect(@attributes.default["foo"].__precedence__).to eql(:default)
+ expect(@attributes.default["foo"]["bar"].__precedence__).to eql(:default)
end
+ it "notifies on attribute changes" do
+ expect(events).to receive(:attribute_changed).with(:default, ["foo"], {})
+ expect(events).to receive(:attribute_changed).with(:default, %w{foo bar}, {})
+ expect(events).to receive(:attribute_changed).with(:default, %w{foo bar baz}, "quux")
+ @attributes.default["foo"]["bar"]["baz"] = "quux"
+ end
end
+ describe "frozen immutable strings" do
+ it "strings in hashes should be frozen" do
+ @attributes.default["foo"]["bar"]["baz"] = "fizz"
+ expect { @attributes["foo"]["bar"]["baz"] << "buzz" }.to raise_error(RuntimeError, "can't modify frozen String")
+ end
+
+ it "strings in arrays should be frozen" do
+ @attributes.default["foo"]["bar"] = [ "fizz" ]
+ expect { @attributes["foo"]["bar"][0] << "buzz" }.to raise_error(RuntimeError, "can't modify frozen String")
+ end
+ end
end
diff --git a/spec/unit/node/immutable_collections_spec.rb b/spec/unit/node/immutable_collections_spec.rb
index f57ed459cd..273c3d2704 100644
--- a/spec/unit/node/immutable_collections_spec.rb
+++ b/spec/unit/node/immutable_collections_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright 2012-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,14 +21,19 @@ require "chef/node/immutable_collections"
describe Chef::Node::ImmutableMash do
before do
- @data_in = { :top => { :second_level => "some value" },
+ @data_in = { "top" => { "second_level" => "some value" },
"top_level_2" => %w{array of values},
- :top_level_3 => [{ :hash_array => 1, :hash_array_b => 2 }],
- :top_level_4 => { :level2 => { :key => "value" } },
+ "top_level_3" => [{ "hash_array" => 1, "hash_array_b" => 2 }],
+ "top_level_4" => { "level2" => { "key" => "value" } },
}
@immutable_mash = Chef::Node::ImmutableMash.new(@data_in)
end
+ it "does not have any unaudited methods" do
+ unaudited_methods = Hash.instance_methods - Object.instance_methods - Chef::Node::Mixin::ImmutablizeHash::DISALLOWED_MUTATOR_METHODS - Chef::Node::Mixin::ImmutablizeHash::ALLOWED_METHODS
+ expect(unaudited_methods).to be_empty
+ end
+
it "element references like regular hash" do
expect(@immutable_mash[:top][:second_level]).to eq("some value")
end
@@ -54,6 +59,14 @@ describe Chef::Node::ImmutableMash do
expect(@immutable_mash[:top_level_4][:level2]).to be_a(Chef::Node::ImmutableMash)
end
+ # we only ever absorb VividMashes from other precedence levels, which already have
+ # been coerced to only have string keys, so we do not need to do that work twice (performance).
+ it "does not call convert_value like Mash/VividMash" do
+ @mash = Chef::Node::ImmutableMash.new({ test: "foo", "test2" => "bar" })
+ expect(@mash[:test]).to eql("foo")
+ expect(@mash["test2"]).to eql("bar")
+ end
+
describe "to_hash" do
before do
@copy = @immutable_mash.to_hash
@@ -78,7 +91,58 @@ describe Chef::Node::ImmutableMash do
it "should allow mutation" do
expect { @copy["m"] = "m" }.not_to raise_error
end
+ end
+
+ describe "dup" do
+ before do
+ @copy = @immutable_mash.dup
+ end
+
+ it "converts an immutable mash to a new mutable hash" do
+ expect(@copy).to be_instance_of(Mash)
+ end
+
+ it "converts an immutable nested mash to a new mutable hash" do
+ expect(@copy["top_level_4"]["level2"]).to be_instance_of(Mash)
+ end
+
+ it "converts an immutable nested array to a new mutable array" do
+ expect(@copy["top_level_2"]).to be_instance_of(Array)
+ end
+
+ it "should create a mash with the same content" do
+ expect(@copy).to eq(@immutable_mash)
+ end
+
+ it "should allow mutation" do
+ expect { @copy["m"] = "m" }.not_to raise_error
+ end
+ end
+
+ describe "to_h" do
+ before do
+ @copy = @immutable_mash.to_h
+ end
+
+ it "converts an immutable mash to a new mutable hash" do
+ expect(@copy).to be_instance_of(Hash)
+ end
+
+ it "converts an immutable nested mash to a new mutable hash" do
+ expect(@copy["top_level_4"]["level2"]).to be_instance_of(Hash)
+ end
+ it "converts an immutable nested array to a new mutable array" do
+ expect(@copy["top_level_2"]).to be_instance_of(Array)
+ end
+
+ it "should create a mash with the same content" do
+ expect(@copy).to eq(@immutable_mash)
+ end
+
+ it "should allow mutation" do
+ expect { @copy["m"] = "m" }.not_to raise_error
+ end
end
[
@@ -95,6 +159,10 @@ describe Chef::Node::ImmutableMash do
:replace,
:select!,
:shift,
+ :write,
+ :write!,
+ :unlink,
+ :unlink!,
].each do |mutator|
it "doesn't allow mutation via `#{mutator}'" do
expect { @immutable_mash.send(mutator) }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
@@ -113,7 +181,7 @@ describe Chef::Node::ImmutableArray do
before do
@immutable_array = Chef::Node::ImmutableArray.new(%w{foo bar baz} + Array(1..3) + [nil, true, false, [ "el", 0, nil ] ])
- immutable_mash = Chef::Node::ImmutableMash.new({ :m => "m" })
+ immutable_mash = Chef::Node::ImmutableMash.new({ "m" => "m" })
@immutable_nested_array = Chef::Node::ImmutableArray.new(["level1", @immutable_array, immutable_mash])
end
@@ -158,6 +226,11 @@ describe Chef::Node::ImmutableArray do
end
end
+ it "does not have any unaudited methods" do
+ unaudited_methods = Array.instance_methods - Object.instance_methods - Chef::Node::Mixin::ImmutablizeArray::DISALLOWED_MUTATOR_METHODS - Chef::Node::Mixin::ImmutablizeArray::ALLOWED_METHODS
+ expect(unaudited_methods).to be_empty
+ end
+
it "can be duped even if some elements can't" do
@immutable_array.dup
end
@@ -194,4 +267,61 @@ describe Chef::Node::ImmutableArray do
end
end
+ describe "dup" do
+ before do
+ @copy = @immutable_nested_array.dup
+ end
+
+ it "converts an immutable array to a new mutable array" do
+ expect(@copy).to be_instance_of(Array)
+ end
+
+ it "converts an immutable nested array to a new mutable array" do
+ expect(@copy[1]).to be_instance_of(Array)
+ end
+
+ it "converts an immutable nested mash to a new mutable hash" do
+ expect(@copy[2]).to be_instance_of(Mash)
+ end
+
+ it "should create an array with the same content" do
+ expect(@copy).to eq(@immutable_nested_array)
+ end
+
+ it "should allow mutation" do
+ expect { @copy << "m" }.not_to raise_error
+ end
+ end
+
+ describe "to_array" do
+ before do
+ @copy = @immutable_nested_array.to_array
+ end
+
+ it "converts an immutable array to a new mutable array" do
+ expect(@copy).to be_instance_of(Array)
+ end
+
+ it "converts an immutable nested array to a new mutable array" do
+ expect(@copy[1]).to be_instance_of(Array)
+ end
+
+ it "converts an immutable nested mash to a new mutable hash" do
+ expect(@copy[2]).to be_instance_of(Hash)
+ end
+
+ it "should create an array with the same content" do
+ expect(@copy).to eq(@immutable_nested_array)
+ end
+
+ it "should allow mutation" do
+ expect { @copy << "m" }.not_to raise_error
+ end
+ end
+
+ describe "#[]" do
+ it "works with array slices" do
+ expect(@immutable_array[1, 2]).to eql(%w{bar baz})
+ end
+ end
end
diff --git a/spec/unit/node/vivid_mash_spec.rb b/spec/unit/node/vivid_mash_spec.rb
new file mode 100644
index 0000000000..4898c22380
--- /dev/null
+++ b/spec/unit/node/vivid_mash_spec.rb
@@ -0,0 +1,353 @@
+#
+# Copyright:: Copyright 2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/node/attribute_collections"
+
+describe Chef::Node::VividMash do
+ let(:root) { instance_double(Chef::Node::Attribute) }
+
+ let(:vivid) do
+ Chef::Node::VividMash.new(
+ { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil },
+ root
+ )
+ end
+
+ context "without a root node" do
+ let(:vivid) do
+ Chef::Node::VividMash.new(
+ { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }
+ )
+ end
+
+ it "sets the root to the root object" do
+ expect(vivid["one"]["two"].__root__).to eql(vivid)
+ end
+
+ it "does not send reset cache" do
+ # if we setup the expectation here then the object winds up responding to :reset_cache and then it fails...
+ # expect(vivid).not_to receive(:reset_cache)
+ # but even so we expect to blow up here with NoMethodError if we screw up and send :reset_cache to a root VividMash
+ vivid["one"]["foo"] = "bar"
+ end
+ end
+
+ context "#[]" do
+ it "works with array slices" do
+ expect(vivid["array"][1, 2]).to eql([1, 2])
+ end
+ end
+
+ context "#[]=" do
+ it "works with array slices" do
+ vivid["array"][3, 2] = [ 3, 4 ]
+ expect(vivid["array"]).to eql([0, 1, 2, 3, 4])
+ end
+
+ it "deep converts values through arrays" do
+ expect(root).to receive(:reset_cache).with("foo")
+ vivid["foo"] = [ { :bar => true } ]
+ expect(vivid["foo"].class).to eql(Chef::Node::AttrArray)
+ expect(vivid["foo"][0].class).to eql(Chef::Node::VividMash)
+ expect(vivid["foo"][0]["bar"]).to be true
+ end
+
+ it "deep converts values through nested arrays" do
+ expect(root).to receive(:reset_cache).with("foo")
+ vivid["foo"] = [ [ { :bar => true } ] ]
+ expect(vivid["foo"].class).to eql(Chef::Node::AttrArray)
+ expect(vivid["foo"][0].class).to eql(Chef::Node::AttrArray)
+ expect(vivid["foo"][0][0].class).to eql(Chef::Node::VividMash)
+ expect(vivid["foo"][0][0]["bar"]).to be true
+ end
+
+ it "deep converts values through hashes" do
+ expect(root).to receive(:reset_cache).with("foo")
+ vivid["foo"] = { baz: { :bar => true } }
+ expect(vivid["foo"]).to be_an_instance_of(Chef::Node::VividMash)
+ expect(vivid["foo"]["baz"]).to be_an_instance_of(Chef::Node::VividMash)
+ expect(vivid["foo"]["baz"]["bar"]).to be true
+ end
+ end
+
+ context "#read" do
+ before do
+ expect(root).not_to receive(:reset_cache)
+ end
+
+ it "reads hashes deeply" do
+ expect(vivid.read("one", "two", "three")).to eql("four")
+ end
+
+ it "does not trainwreck when hitting hash keys that do not exist" do
+ expect(vivid.read("one", "five", "six")).to eql(nil)
+ end
+
+ it "does not trainwreck when hitting an array with an out of bounds index" do
+ expect(vivid.read("array", 5, "one")).to eql(nil)
+ end
+
+ it "does not trainwreck when hitting an array with a string key" do
+ expect(vivid.read("array", "one", "two")).to eql(nil)
+ end
+
+ it "does not trainwreck when traversing a nil" do
+ expect(vivid.read("nil", "one", "two")).to eql(nil)
+ end
+ end
+
+ context "#exist?" do
+ before do
+ expect(root).not_to receive(:reset_cache)
+ end
+
+ it "true if there's a hash key there" do
+ expect(vivid.exist?("one", "two", "three")).to be true
+ end
+
+ it "true for intermediate hashes" do
+ expect(vivid.exist?("one")).to be true
+ end
+
+ it "true for arrays that exist" do
+ expect(vivid.exist?("array", 1)).to be true
+ end
+
+ it "true when the value of the key is nil" do
+ expect(vivid.exist?("nil")).to be true
+ end
+
+ it "false when attributes don't exist" do
+ expect(vivid.exist?("one", "five", "six")).to be false
+ end
+
+ it "false when traversing a non-container" do
+ expect(vivid.exist?("one", "two", "three", "four")).to be false
+ end
+
+ it "false when an array index does not exist" do
+ expect(vivid.exist?("array", 3)).to be false
+ end
+
+ it "false when traversing a nil" do
+ expect(vivid.exist?("nil", "foo", "bar")).to be false
+ end
+ end
+
+ context "#read!" do
+ before do
+ expect(root).not_to receive(:reset_cache)
+ end
+
+ it "reads hashes deeply" do
+ expect(vivid.read!("one", "two", "three")).to eql("four")
+ end
+
+ it "reads arrays deeply" do
+ expect(vivid.read!("array", 1)).to eql(1)
+ end
+
+ it "throws an exception when attributes do not exist" do
+ expect { vivid.read!("one", "five", "six") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+ end
+
+ it "throws an exception when traversing a non-container" do
+ expect { vivid.read!("one", "two", "three", "four") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+ end
+
+ it "throws an exception when an array element does not exist" do
+ expect { vivid.read!("array", 3) }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+ end
+ end
+
+ context "#write" do
+ it "should write into hashes" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
+ vivid.write("one", "five", "six")
+ expect(vivid["one"]["five"]).to eql("six")
+ end
+
+ it "should deeply autovivify" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
+ vivid.write("one", "five", "six", "seven", "eight", "nine", "ten")
+ expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten")
+ end
+
+ it "should raise an exception if you overwrite an array with a hash" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("array")
+ vivid.write("array", "five", "six")
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => "six" }, "nil" => nil })
+ end
+
+ it "should raise an exception if you traverse through an array with a hash" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("array")
+ vivid.write("array", "five", "six", "seven")
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => { "six" => "seven" } }, "nil" => nil })
+ end
+
+ it "should raise an exception if you overwrite a string with a hash" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
+ vivid.write("one", "two", "three", "four", "five")
+ expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => "five" } } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+ end
+
+ it "should raise an exception if you traverse through a string with a hash" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
+ vivid.write("one", "two", "three", "four", "five", "six")
+ expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => { "five" => "six" } } } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+ end
+
+ it "should raise an exception if you overwrite a nil with a hash" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("nil")
+ vivid.write("nil", "one", "two")
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => "two" } })
+ end
+
+ it "should raise an exception if you traverse through a nil with a hash" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("nil")
+ vivid.write("nil", "one", "two", "three")
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => { "two" => "three" } } })
+ end
+
+ it "writes with a block" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
+ vivid.write("one", "five") { "six" }
+ expect(vivid["one"]["five"]).to eql("six")
+ end
+ end
+
+ context "#write!" do
+ it "should write into hashes" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
+ vivid.write!("one", "five", "six")
+ expect(vivid["one"]["five"]).to eql("six")
+ end
+
+ it "should deeply autovivify" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
+ vivid.write!("one", "five", "six", "seven", "eight", "nine", "ten")
+ expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten")
+ end
+
+ it "should raise an exception if you overwrite an array with a hash" do
+ expect(root).not_to receive(:reset_cache)
+ expect { vivid.write!("array", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+ end
+
+ it "should raise an exception if you traverse through an array with a hash" do
+ expect(root).not_to receive(:reset_cache)
+ expect { vivid.write!("array", "five", "six", "seven") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+ end
+
+ it "should raise an exception if you overwrite a string with a hash" do
+ expect(root).not_to receive(:reset_cache)
+ expect { vivid.write!("one", "two", "three", "four", "five") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+ end
+
+ it "should raise an exception if you traverse through a string with a hash" do
+ expect(root).not_to receive(:reset_cache)
+ expect { vivid.write!("one", "two", "three", "four", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+ end
+
+ it "should raise an exception if you overwrite a nil with a hash" do
+ expect(root).not_to receive(:reset_cache)
+ expect { vivid.write!("nil", "one", "two") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+ end
+
+ it "should raise an exception if you traverse through a nil with a hash" do
+ expect(root).not_to receive(:reset_cache)
+ expect { vivid.write!("nil", "one", "two", "three") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+ end
+
+ it "writes with a block" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
+ vivid.write!("one", "five") { "six" }
+ expect(vivid["one"]["five"]).to eql("six")
+ end
+ end
+
+ context "#unlink" do
+ it "should return nil if the keys don't already exist" do
+ expect(root).not_to receive(:reset_cache)
+ expect(vivid.unlink("five", "six", "seven", "eight")).to eql(nil)
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+ end
+
+ it "should unlink hashes" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
+ expect( vivid.unlink("one") ).to eql({ "two" => { "three" => "four" } })
+ expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil })
+ end
+
+ it "should unlink array elements" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("array")
+ expect(vivid.unlink("array", 2)).to eql(2)
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil })
+ end
+
+ it "should unlink nil" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("nil")
+ expect(vivid.unlink("nil")).to eql(nil)
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] })
+ end
+
+ it "should traverse a nil and safely do nothing" do
+ expect(root).not_to receive(:reset_cache)
+ expect(vivid.unlink("nil", "foo")).to eql(nil)
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+ end
+ end
+
+ context "#unlink!" do
+ it "should raise an exception if the keys don't already exist" do
+ expect(root).not_to receive(:reset_cache)
+ expect { vivid.unlink!("five", "six", "seven", "eight") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+ end
+
+ it "should unlink! hashes" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("one")
+ expect( vivid.unlink!("one") ).to eql({ "two" => { "three" => "four" } })
+ expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil })
+ end
+
+ it "should unlink! array elements" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("array")
+ expect(vivid.unlink!("array", 2)).to eql(2)
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil })
+ end
+
+ it "should unlink! nil" do
+ expect(root).to receive(:reset_cache).at_least(:once).with("nil")
+ expect(vivid.unlink!("nil")).to eql(nil)
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] })
+ end
+
+ it "should raise an exception if it traverses a nil" do
+ expect(root).not_to receive(:reset_cache)
+ expect { vivid.unlink!("nil", "foo") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+ expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+ end
+ end
+end
diff --git a/spec/unit/node_map_spec.rb b/spec/unit/node_map_spec.rb
index 0480a721af..24f3bebe2a 100644
--- a/spec/unit/node_map_spec.rb
+++ b/spec/unit/node_map_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,6 +19,9 @@
require "spec_helper"
require "chef/node_map"
+class Foo; end
+class Bar; end
+
describe Chef::NodeMap do
let(:node_map) { Chef::NodeMap.new }
@@ -101,6 +104,62 @@ describe Chef::NodeMap do
end
end
+ describe "platform version checks" do
+ before do
+ node_map.set(:thing, :foo, platform_family: "rhel", platform_version: ">= 7")
+ end
+
+ it "handles non-x.y.z platform versions without throwing an exception" do
+ allow(node).to receive(:[]).with(:platform_family).and_return("rhel")
+ allow(node).to receive(:[]).with(:platform_version).and_return("7.19.2.2F")
+ expect(node_map.get(node, :thing)).to eql(:foo)
+ end
+
+ it "handles non-x.y.z platform versions without throwing an exception when the match fails" do
+ allow(node).to receive(:[]).with(:platform_family).and_return("rhel")
+ allow(node).to receive(:[]).with(:platform_version).and_return("4.19.2.2F")
+ expect(node_map.get(node, :thing)).to eql(nil)
+ end
+ end
+
+ describe "ordering classes" do
+ it "last writer wins when its reverse alphabetic order" do
+ node_map.set(:thing, Foo)
+ node_map.set(:thing, Bar)
+ expect(node_map.get(node, :thing)).to eql(Bar)
+ end
+
+ it "last writer wins when its alphabetic order" do
+ node_map.set(:thing, Bar)
+ node_map.set(:thing, Foo)
+ expect(node_map.get(node, :thing)).to eql(Foo)
+ end
+ end
+
+ describe "deleting classes" do
+ it "deletes a class and removes the mapping completely" do
+ node_map.set(:thing, Bar)
+ expect( node_map.delete_class(Bar) ).to eql({ :thing => [{ :klass => Bar }] })
+ expect( node_map.get(node, :thing) ).to eql(nil)
+ end
+
+ it "deletes a class and leaves the mapping that still has an entry" do
+ node_map.set(:thing, Bar)
+ node_map.set(:thing, Foo)
+ expect( node_map.delete_class(Bar) ).to eql({ :thing => [{ :klass => Bar }] })
+ expect( node_map.get(node, :thing) ).to eql(Foo)
+ end
+
+ it "handles deleting classes from multiple keys" do
+ node_map.set(:thing1, Bar)
+ node_map.set(:thing2, Bar)
+ node_map.set(:thing2, Foo)
+ expect( node_map.delete_class(Bar) ).to eql({ :thing1 => [{ :klass => Bar }], :thing2 => [{ :klass => Bar }] })
+ expect( node_map.get(node, :thing1) ).to eql(nil)
+ expect( node_map.get(node, :thing2) ).to eql(Foo)
+ end
+ end
+
describe "with a block doing platform_version checks" do
before do
node_map.set(:thing, :foo, platform_family: "rhel") do |node|
@@ -145,26 +204,4 @@ describe Chef::NodeMap do
end
end
- describe "resource back-compat testing" do
- before :each do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- end
-
- it "should handle :on_platforms => :all" do
- node_map.set(:chef_gem, :foo, :on_platforms => :all)
- allow(node).to receive(:[]).with(:platform).and_return("windows")
- expect(node_map.get(node, :chef_gem)).to eql(:foo)
- end
- it "should handle :on_platforms => [ 'windows' ]" do
- node_map.set(:dsc_script, :foo, :on_platforms => [ "windows" ])
- allow(node).to receive(:[]).with(:platform).and_return("windows")
- expect(node_map.get(node, :dsc_script)).to eql(:foo)
- end
- it "should handle :on_platform => :all" do
- node_map.set(:link, :foo, :on_platform => :all)
- allow(node).to receive(:[]).with(:platform).and_return("windows")
- expect(node_map.get(node, :link)).to eql(:foo)
- end
- end
-
end
diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb
index 923b488f72..3a43a155d5 100644
--- a/spec/unit/node_spec.rb
+++ b/spec/unit/node_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -186,7 +186,7 @@ describe Chef::Node do
expect { node.policy_group = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqurstuvwxyz0123456789-_:." }.to_not raise_error
end
- it "sets an environment with chef_environment(something)" do
+ it "sets a policy_group with policy_group(something)" do
node.policy_group("staging")
expect(node.policy_group).to eq("staging")
end
@@ -233,68 +233,103 @@ describe Chef::Node do
end
it "should let you go deep with attribute?" do
- node.set["battles"]["people"]["wonkey"] = true
+ node.normal["battles"]["people"]["wonkey"] = true
expect(node["battles"]["people"].attribute?("wonkey")).to eq(true)
expect(node["battles"]["people"].attribute?("snozzberry")).to eq(false)
end
- it "does not allow you to set an attribute via method_missing" do
- expect { node.sunshine = "is bright" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
+ it "does not allow modification of node attributes via hash methods" do
+ node.default["h4sh"] = { foo: "bar" }
+ expect { node["h4sh"].delete("foo") }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
end
- it "should allow you get get an attribute via method_missing" do
- node.default.sunshine = "is bright"
- expect(node.sunshine).to eql("is bright")
+ it "does not allow modification of node attributes via array methods" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ node.default["array"] = []
+ expect { node["array"] << "boom" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
+ end
+
+ it "returns merged immutable attributes for arrays" do
+ node.default["array"] = []
+ expect( node["array"].class ).to eql(Chef::Node::ImmutableArray)
+ end
+
+ it "returns merged immutable attributes for hashes" do
+ node.default["h4sh"] = {}
+ expect( node["h4sh"].class ).to eql(Chef::Node::ImmutableMash)
end
describe "normal attributes" do
it "should allow you to set an attribute with set, without pre-declaring a hash" do
- node.set[:snoopy][:is_a_puppy] = true
+ node.normal[:snoopy][:is_a_puppy] = true
expect(node[:snoopy][:is_a_puppy]).to eq(true)
end
it "should allow you to set an attribute with set_unless" do
- node.set_unless[:snoopy][:is_a_puppy] = false
+ node.normal_unless[:snoopy][:is_a_puppy] = false
expect(node[:snoopy][:is_a_puppy]).to eq(false)
end
it "should not allow you to set an attribute with set_unless if it already exists" do
- node.set[:snoopy][:is_a_puppy] = true
- node.set_unless[:snoopy][:is_a_puppy] = false
+ node.normal[:snoopy][:is_a_puppy] = true
+ node.normal_unless[:snoopy][:is_a_puppy] = false
expect(node[:snoopy][:is_a_puppy]).to eq(true)
end
it "should allow you to set an attribute with set_unless if is a nil value" do
node.attributes.normal = { snoopy: { is_a_puppy: nil } }
- node.set_unless[:snoopy][:is_a_puppy] = false
+ node.normal_unless[:snoopy][:is_a_puppy] = false
expect(node[:snoopy][:is_a_puppy]).to eq(false)
end
it "should allow you to set a value after a set_unless" do
# this tests for set_unless_present state bleeding between statements CHEF-3806
- node.set_unless[:snoopy][:is_a_puppy] = false
- node.set[:snoopy][:is_a_puppy] = true
+ node.normal_unless[:snoopy][:is_a_puppy] = false
+ node.normal[:snoopy][:is_a_puppy] = true
expect(node[:snoopy][:is_a_puppy]).to eq(true)
end
it "should let you set a value after a 'dangling' set_unless" do
# this tests for set_unless_present state bleeding between statements CHEF-3806
- node.set[:snoopy][:is_a_puppy] = "what"
- node.set_unless[:snoopy][:is_a_puppy]
- node.set[:snoopy][:is_a_puppy] = true
+ node.normal[:snoopy][:is_a_puppy] = "what"
+ node.normal_unless[:snoopy][:is_a_puppy]
+ node.normal[:snoopy][:is_a_puppy] = true
expect(node[:snoopy][:is_a_puppy]).to eq(true)
end
- it "auto-vivifies attributes created via method syntax" do
- node.set.fuu.bahrr.baz = "qux"
- expect(node.fuu.bahrr.baz).to eq("qux")
- end
-
it "should let you use tag as a convience method for the tags attribute" do
node.normal["tags"] = %w{one two}
node.tag("three", "four")
expect(node["tags"]).to eq(%w{one two three four})
end
+
+ it "normal_unless sets a value even if default or override attrs are set" do
+ node.default[:decontamination] = true
+ node.override[:decontamination] = false
+ node.normal_unless[:decontamination] = "foo"
+ expect(node.normal[:decontamination]).to eql("foo")
+ end
+
+ it "default_unless sets a value even if normal or override attrs are set" do
+ node.normal[:decontamination] = true
+ node.override[:decontamination] = false
+ node.default_unless[:decontamination] = "foo"
+ expect(node.default[:decontamination]).to eql("foo")
+ end
+
+ it "override_unless sets a value even if default or normal attrs are set" do
+ node.default[:decontamination] = true
+ node.normal[:decontamination] = false
+ node.override_unless[:decontamination] = "foo"
+ expect(node.override[:decontamination]).to eql("foo")
+ end
+
+ it "consume_attributes does not exhibit chef/chef/issues/6302 bug" do
+ node.normal["a"]["r1"] = nil
+ node.consume_attributes({ "a" => { "r2" => nil } })
+ expect(node["a"]["r1"]).to be_nil
+ expect(node["a"]["r2"]).to be_nil
+ end
end
describe "default attributes" do
@@ -329,9 +364,34 @@ describe Chef::Node do
expect(node[:snoopy][:is_a_puppy]).to eq(true)
end
- it "auto-vivifies attributes created via method syntax" do
- node.default.fuu.bahrr.baz = "qux"
- expect(node.fuu.bahrr.baz).to eq("qux")
+ it "does not exhibit chef/chef/issues/5005 bug" do
+ node.env_default["a"]["r1"]["g"]["u"] = "u1"
+ node.default_unless["a"]["r1"]["g"]["r"] = "r"
+ expect(node["a"]["r1"]["g"]["u"]).to eql("u1")
+ end
+
+ it "default_unless correctly resets the deep merge cache" do
+ node.normal["tags"] = [] # this sets our top-level breadcrumb
+ node.default_unless["foo"]["bar"] = "NK-19V"
+ expect(node["foo"]["bar"]).to eql("NK-19V")
+ node.default_unless["foo"]["baz"] = "NK-33"
+ expect(node["foo"]["baz"]).to eql("NK-33")
+ end
+
+ it "normal_unless correctly resets the deep merge cache" do
+ node.normal["tags"] = [] # this sets our top-level breadcrumb
+ node.normal_unless["foo"]["bar"] = "NK-19V"
+ expect(node["foo"]["bar"]).to eql("NK-19V")
+ node.normal_unless["foo"]["baz"] = "NK-33"
+ expect(node["foo"]["baz"]).to eql("NK-33")
+ end
+
+ it "override_unless correctly resets the deep merge cache" do
+ node.normal["tags"] = [] # this sets our top-level breadcrumb
+ node.override_unless["foo"]["bar"] = "NK-19V"
+ expect(node["foo"]["bar"]).to eql("NK-19V")
+ node.override_unless["foo"]["baz"] = "NK-33"
+ expect(node["foo"]["baz"]).to eql("NK-33")
end
end
@@ -366,11 +426,6 @@ describe Chef::Node do
node.override[:snoopy][:is_a_puppy] = true
expect(node[:snoopy][:is_a_puppy]).to eq(true)
end
-
- it "auto-vivifies attributes created via method syntax" do
- node.override.fuu.bahrr.baz = "qux"
- expect(node.fuu.bahrr.baz).to eq("qux")
- end
end
describe "globally deleting attributes" do
@@ -453,8 +508,9 @@ describe Chef::Node do
expect( node["mysql"]["server"][0]["port"] ).to be_nil
end
- it "does not have a horrible error message when mistaking arrays for hashes" do
- expect { node.rm("mysql", "server", "port") }.to raise_error(TypeError, "Wrong type in index of attribute (did you use a Hash index on an Array?)")
+ it "when mistaking arrays for hashes, it considers the value removed and does nothing" do
+ node.rm("mysql", "server", "port")
+ expect(node["mysql"]["server"][0]["port"]).to eql(3456)
end
end
end
@@ -700,15 +756,15 @@ describe Chef::Node do
# In Chef-12.0 there is a deep_merge cache on the top level attribute which had a bug
# where it cached node[:foo] separate from node['foo']. These tests exercise those edge conditions.
#
- # https://github.com/opscode/chef/issues/2700
- # https://github.com/opscode/chef/issues/2712
- # https://github.com/opscode/chef/issues/2745
+ # https://github.com/chef/chef/issues/2700
+ # https://github.com/chef/chef/issues/2712
+ # https://github.com/chef/chef/issues/2745
#
describe "deep merge attribute cache edge conditions" do
it "does not error with complicated attribute substitution" do
node.default["chef_attribute_hell"]["attr1"] = "attribute1"
- node.default["chef_attribute_hell"]["attr2"] = "#{node.chef_attribute_hell.attr1}/attr2"
- expect { node.default["chef_attribute_hell"]["attr3"] = "#{node.chef_attribute_hell.attr2}/attr3" }.not_to raise_error
+ node.default["chef_attribute_hell"]["attr2"] = "#{node[:chef_attribute_hell][:attr1]}/attr2"
+ expect { node.default["chef_attribute_hell"]["attr3"] = "#{node[:chef_attribute_hell][:attr2]}/attr3" }.not_to raise_error
end
it "caches both strings and symbols correctly" do
@@ -720,21 +776,23 @@ describe Chef::Node do
end
it "method interpolation syntax also works" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
node.default["passenger"]["version"] = "4.0.57"
node.default["passenger"]["root_path"] = "passenger-#{node['passenger']['version']}"
- node.default["passenger"]["root_path_2"] = "passenger-#{node.passenger['version']}"
+ node.default["passenger"]["root_path_2"] = "passenger-#{node[:passenger]['version']}"
expect(node["passenger"]["root_path_2"]).to eql("passenger-4.0.57")
expect(node[:passenger]["root_path_2"]).to eql("passenger-4.0.57")
end
end
it "should raise an ArgumentError if you ask for an attribute that doesn't exist via method_missing" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
expect { node.sunshine }.to raise_error(NoMethodError)
end
it "should allow you to iterate over attributes with each_attribute" do
- node.default.sunshine = "is bright"
- node.default.canada = "is a nice place"
+ node.default["sunshine"] = "is bright"
+ node.default["canada"] = "is a nice place"
seen_attributes = Hash.new
node.each_attribute do |a, v|
seen_attributes[a] = v
@@ -744,6 +802,51 @@ describe Chef::Node do
expect(seen_attributes["sunshine"]).to eq("is bright")
expect(seen_attributes["canada"]).to eq("is a nice place")
end
+
+ describe "functional attribute API" do
+ # deeper functional testing of this API is in the VividMash spec tests
+ it "should have an exist? function" do
+ node.default["foo"]["bar"] = "baz"
+ expect(node.exist?("foo", "bar")).to be true
+ expect(node.exist?("bar", "foo")).to be false
+ end
+
+ it "should have a read function" do
+ node.override["foo"]["bar"] = "baz"
+ expect(node.read("foo", "bar")).to eql("baz")
+ expect(node.read("bar", "foo")).to eql(nil)
+ end
+
+ it "should have a read! function" do
+ node.override["foo"]["bar"] = "baz"
+ expect(node.read!("foo", "bar")).to eql("baz")
+ expect { node.read!("bar", "foo") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+ end
+
+ it "delegates write(:level) to node.level.write()" do
+ node.write(:default, "foo", "bar", "baz")
+ expect(node.default["foo"]["bar"]).to eql("baz")
+ end
+
+ it "delegates write!(:level) to node.level.write!()" do
+ node.write!(:default, "foo", "bar", "baz")
+ expect(node.default["foo"]["bar"]).to eql("baz")
+ node.default["bar"] = true
+ expect { node.write!(:default, "bar", "foo", "baz") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
+ end
+
+ it "delegates unlink(:level) to node.level.unlink()" do
+ node.default["foo"]["bar"] = "baz"
+ expect(node.unlink(:default, "foo", "bar")).to eql("baz")
+ expect(node.unlink(:default, "bar", "foo")).to eql(nil)
+ end
+
+ it "delegates unlink!(:level) to node.level.unlink!()" do
+ node.default["foo"]["bar"] = "baz"
+ expect(node.unlink!(:default, "foo", "bar")).to eql("baz")
+ expect { node.unlink!(:default, "bar", "foo") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+ end
+ end
end
describe "consuming json" do
@@ -789,8 +892,8 @@ describe Chef::Node do
it "should add json attributes to the node" do
node.consume_external_attrs(@ohai_data, { "one" => "two", "three" => "four" })
- expect(node.one).to eql("two")
- expect(node.three).to eql("four")
+ expect(node["one"]).to eql("two")
+ expect(node["three"]).to eql("four")
end
it "should set the tags attribute to an empty array if it is not already defined" do
@@ -824,17 +927,17 @@ describe Chef::Node do
it "deep merges attributes instead of overwriting them" do
node.consume_external_attrs(@ohai_data, "one" => { "two" => { "three" => "four" } })
- expect(node.one.to_hash).to eq({ "two" => { "three" => "four" } })
+ expect(node["one"].to_hash).to eq({ "two" => { "three" => "four" } })
node.consume_external_attrs(@ohai_data, "one" => { "abc" => "123" })
node.consume_external_attrs(@ohai_data, "one" => { "two" => { "foo" => "bar" } })
- expect(node.one.to_hash).to eq({ "two" => { "three" => "four", "foo" => "bar" }, "abc" => "123" })
+ expect(node["one"].to_hash).to eq({ "two" => { "three" => "four", "foo" => "bar" }, "abc" => "123" })
end
it "gives attributes from JSON priority when deep merging" do
node.consume_external_attrs(@ohai_data, "one" => { "two" => { "three" => "four" } })
- expect(node.one.to_hash).to eq({ "two" => { "three" => "four" } })
+ expect(node["one"].to_hash).to eq({ "two" => { "three" => "four" } })
node.consume_external_attrs(@ohai_data, "one" => { "two" => { "three" => "forty-two" } })
- expect(node.one.to_hash).to eq({ "two" => { "three" => "forty-two" } })
+ expect(node["one"].to_hash).to eq({ "two" => { "three" => "forty-two" } })
end
end
@@ -850,6 +953,14 @@ describe Chef::Node do
expect(node.automatic_attrs[:platform_version]).to eq("23.42")
end
+ it "sets the chef guid attribute correctly" do
+ guid = Chef::Config[:chef_guid]
+ Chef::Config[:chef_guid] = "test-guid-guid"
+ node.consume_external_attrs(@ohai_data, {})
+ expect(node.automatic_attrs[:chef_guid]).to eq("test-guid-guid")
+ Chef::Config[:chef_guid] = guid
+ end
+
it "consumes the run list from provided json attributes" do
node.consume_external_attrs(@ohai_data, { "run_list" => ["recipe[unicorn]"] })
expect(node.run_list).to eq(["recipe[unicorn]"])
@@ -1036,10 +1147,10 @@ describe Chef::Node do
end
it "sets attributes from the files" do
- expect(node.ldap_server).to eql("ops1prod")
- expect(node.ldap_basedn).to eql("dc=hjksolutions,dc=com")
- expect(node.ldap_replication_password).to eql("forsure")
- expect(node.smokey).to eql("robinson")
+ expect(node["ldap_server"]).to eql("ops1prod")
+ expect(node["ldap_basedn"]).to eql("dc=hjksolutions,dc=com")
+ expect(node["ldap_replication_password"]).to eql("forsure")
+ expect(node["smokey"]).to eql("robinson")
end
it "gives a sensible error when attempting to load a missing attributes file" do
@@ -1049,16 +1160,16 @@ describe Chef::Node do
describe "roles" do
it "should allow you to query whether or not it has a recipe applied with role?" do
- node.run_list << "role[sunrise]"
+ node.automatic["roles"] = %w{sunrise}
expect(node.role?("sunrise")).to eql(true)
expect(node.role?("not at home")).to eql(false)
end
it "should allow you to set roles with arguments" do
- node.run_list << "role[one]"
- node.run_list << "role[two]"
+ node.automatic["roles"] = %w{one two}
expect(node.role?("one")).to eql(true)
expect(node.role?("two")).to eql(true)
+ expect(node.role?("three")).to eql(false)
end
end
@@ -1083,8 +1194,8 @@ describe Chef::Node do
it "should load a node from a ruby file" do
node.from_file(File.expand_path(File.join(CHEF_SPEC_DATA, "nodes", "test.rb")))
expect(node.name).to eql("test.example.com-short")
- expect(node.sunshine).to eql("in")
- expect(node.something).to eql("else")
+ expect(node["sunshine"]).to eql("in")
+ expect(node["something"]).to eql("else")
expect(node.run_list).to eq(["operations-master", "operations-monitoring"])
end
@@ -1250,15 +1361,16 @@ describe Chef::Node do
expect(round_tripped_node.policy_name).to eq("my-application")
expect(round_tripped_node.policy_group).to eq("staging")
+ expect(round_tripped_node.chef_environment).to eq("staging")
end
end
include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
- let(:jsonable) {
+ let(:jsonable) do
node.from_file(File.expand_path("nodes/test.example.com.rb", CHEF_SPEC_DATA))
node
- }
+ end
end
end
@@ -1456,6 +1568,78 @@ describe Chef::Node do
end
end
+ context "with blacklisted attributes configured" do
+ it "should only save non-blacklisted attributes (and subattributes)" do
+ Chef::Config[:automatic_attribute_blacklist] = [
+ ["filesystem", "/dev/disk0s2"],
+ "network/interfaces/eth0",
+ ]
+
+ data = {
+ "automatic" => {
+ "filesystem" => {
+ "/dev/disk0s2" => { "size" => "10mb" },
+ "map - autohome" => { "size" => "10mb" },
+ },
+ "network" => {
+ "interfaces" => {
+ "eth0" => {},
+ "eth1" => {},
+ },
+ },
+ },
+ "default" => {}, "normal" => {}, "override" => {}
+ }
+
+ selected_data = {
+ "automatic" => {
+ "filesystem" => {
+ "map - autohome" => { "size" => "10mb" },
+ },
+ "network" => {
+ "interfaces" => {
+ "eth1" => {},
+ },
+ },
+ },
+ "default" => {}, "normal" => {}, "override" => {}
+ }
+ node.name("picky-monkey")
+ allow(node).to receive(:for_json).and_return(data)
+ expect(@rest).to receive(:put).with("nodes/picky-monkey", selected_data).and_return("foo")
+ node.save
+ end
+
+ it "should save all attributes if the blacklist is empty" do
+ Chef::Config[:automatic_attribute_blacklist] = []
+
+ data = {
+ "automatic" => {
+ "filesystem" => {
+ "/dev/disk0s2" => { "size" => "10mb" },
+ "map - autohome" => { "size" => "10mb" },
+ },
+ },
+ "default" => {}, "normal" => {}, "override" => {}
+ }
+
+ selected_data = {
+ "automatic" => {
+ "filesystem" => {
+ "/dev/disk0s2" => { "size" => "10mb" },
+ "map - autohome" => { "size" => "10mb" },
+ },
+ },
+ "default" => {}, "normal" => {}, "override" => {}
+ }
+
+ node.name("picky-monkey")
+ allow(node).to receive(:for_json).and_return(data)
+ expect(@rest).to receive(:put).with("nodes/picky-monkey", selected_data).and_return("foo")
+ node.save
+ end
+ end
+
context "when policyfile attributes are present" do
before do
@@ -1504,62 +1688,134 @@ describe Chef::Node do
end
- context "on Chef Client 13 and later" do
+ it "lets the 400 pass through" do
+ expect(@rest).to receive(:put).and_raise(http_exception)
+ expect { node.save }.to raise_error(http_exception)
+ end
- # Though we normally attempt to provide compatibility with chef
- # server one major version back, policyfiles were beta when we
- # added the policyfile attributes to the node JSON, therefore
- # policyfile users need to be on 12.3 minimum when upgrading Chef
- # Client to 13+
- it "lets the 400 pass through", chef: ">= 13" do
- expect { node.save }.to raise_error(http_exception)
- end
+ end
- end
+ end
- context "when the node exists" do
+ end
+ end
- it "falls back to saving without policyfile attributes" do
- expect(@rest).to receive(:put).with("nodes/example-node", node.for_json).and_raise(http_exception)
- expect(@rest).to receive(:put).with("nodes/example-node", trimmed_node).and_return(@node)
- expect { node.save }.to_not raise_error
- end
+ describe "method_missing handling" do
+ it "should have an #empty? method via Chef::Node::Attribute" do
+ node.default["foo"] = "bar"
+ expect(node.empty?).to be false
+ end
- end
+ it "it should correctly implement #respond_to?" do
+ expect(node.respond_to?(:empty?)).to be true
+ end
- context "when the node doesn't exist" do
+ it "it should correctly retrieve the method with #method" do
+ expect(node.method(:empty?)).to be_kind_of(Method)
+ end
+ end
- let(:response_404) do
- Net::HTTPResponse.send(:response_class, "404").new("1.0", "404", "Not Found")
- end
+ describe "path tracking via __path__" do
+ it "works through hash keys" do
+ node.default["foo"] = { "bar" => { "baz" => "qux" } }
+ expect(node["foo"]["bar"].__path__).to eql(%w{foo bar})
+ end
- let(:http_exception_404) do
- begin
- response_404.error!
- rescue => e
- e
- end
- end
+ it "works through the default level" do
+ node.default["foo"] = { "bar" => { "baz" => "qux" } }
+ expect(node.default["foo"]["bar"].__path__).to eql(%w{foo bar})
+ end
- it "falls back to saving without policyfile attributes" do
- expect(@rest).to receive(:put).with("nodes/example-node", node.for_json).and_raise(http_exception)
- expect(@rest).to receive(:put).with("nodes/example-node", trimmed_node).and_raise(http_exception_404)
- expect(@rest).to receive(:post).with("nodes", trimmed_node).and_return(@node)
- node.save
- end
+ it "works through arrays" do
+ node.default["foo"] = [ { "bar" => { "baz" => "qux" } } ]
+ expect(node["foo"][0].__path__).to eql(["foo", 0])
+ expect(node["foo"][0]["bar"].__path__).to eql(["foo", 0, "bar"])
+ end
- it "creates the node without policyfile attributes" do
- expect(@rest).to receive(:post).with("nodes", node.for_json).and_raise(http_exception)
- expect(@rest).to receive(:post).with("nodes", trimmed_node).and_return(@node)
- node.create
- end
- end
+ it "works through arrays at the default level" do
+ node.default["foo"] = [ { "bar" => { "baz" => "qux" } } ]
+ expect(node.default["foo"][0].__path__).to eql(["foo", 0])
+ expect(node.default["foo"][0]["bar"].__path__).to eql(["foo", 0, "bar"])
+ end
- end
+ # if we set __path__ in the initializer we'd get this wrong, this is why we
+ # update the path on every #[] or #[]= operator
+ it "works on access when the node has been rearranged" do
+ node.default["foo"] = { "bar" => { "baz" => "qux" } }
+ a = node.default["foo"]
+ node.default["fizz"] = a
+ expect(node["fizz"]["bar"].__path__).to eql(%w{fizz bar})
+ expect(node["foo"]["bar"].__path__).to eql(%w{foo bar})
+ end
- end
+ # We have a problem because the __path__ is stored on in each node, but the
+ # node can be wired up at multiple locations in the tree via pointers. One
+ # solution would be to deep-dup the value in `#[]=(key, value)` and fix the
+ # __path__ on all the dup'd nodes. The problem is that this would create an
+ # unusual situation where after assignment, you couldn't mutate the thing you
+ # hand a handle on. I'm not entirely positive this behavior is the correct
+ # thing to support, but it is more hash-like (although if we start with a hash
+ # then convert_value does its thing and we *do* get dup'd on assignment). This
+ # behavior likely makes any implementation of a deep merge cache built over the
+ # top of __path__ tracking have edge conditions where it will fail.
+ #
+ # Removing this support would be a breaking change. The test is included here
+ # because it seems most likely that someone would break this behavior while trying
+ # to fix __path__ behavior.
+ it "does not dup in the background when a node is assigned" do
+ # get a handle on a vividmash (can't be a hash or else we convert_value it)
+ node.default["foo"] = { "bar" => { "baz" => "qux" } }
+ a = node.default["foo"]
+ # assign that somewhere else in the tree
+ node.default["fizz"] = a
+ # now upate the source
+ a["duptest"] = true
+ # the tree should have been updated
+ expect(node.default["fizz"]["duptest"]).to be true
+ expect(node["fizz"]["duptest"]).to be true
+ end
+ end
+
+ describe "root tracking via __root__" do
+ it "works through hash keys" do
+ node.default["foo"] = { "bar" => { "baz" => "qux" } }
+ expect(node["foo"]["bar"].__root__).to eql(node.attributes)
+ end
+ it "works through the default level" do
+ node.default["foo"] = { "bar" => { "baz" => "qux" } }
+ expect(node.default["foo"]["bar"].__root__).to eql(node.attributes)
+ end
+
+ it "works through arrays" do
+ node.default["foo"] = [ { "bar" => { "baz" => "qux" } } ]
+ expect(node["foo"][0].__root__).to eql(node.attributes)
+ expect(node["foo"][0]["bar"].__root__).to eql(node.attributes)
+ end
+
+ it "works through arrays at the default level" do
+ node.default["foo"] = [ { "bar" => { "baz" => "qux" } } ]
+ expect(node.default["foo"][0].__root__).to eql(node.attributes)
+ expect(node.default["foo"][0]["bar"].__root__).to eql(node.attributes)
end
end
+ describe "ways of abusing Chef 12 node state" do
+ # these tests abuse the top_level_breadcrumb state in Chef 12
+ it "derived attributes work correctly" do
+ node.default["v1"] = 1
+ expect(node["a"]).to eql(nil)
+ node.default["a"] = node["v1"]
+ expect(node["a"]).to eql(1)
+ end
+
+ it "works when saving nodes to variables" do
+ a = node.default["a"]
+ expect(node["a"]).to eql({})
+ node.default["b"] = 0
+ a["key"] = 1
+
+ expect(node["a"]["key"]).to eql(1)
+ end
+ end
end
diff --git a/spec/unit/platform/query_helpers_spec.rb b/spec/unit/platform/query_helpers_spec.rb
index 49da6a95f0..ac5158e913 100644
--- a/spec/unit/platform/query_helpers_spec.rb
+++ b/spec/unit/platform/query_helpers_spec.rb
@@ -18,19 +18,6 @@
require "spec_helper"
-describe "Chef::Platform#windows_server_2003?" do
- it "returns false early when not on windows" do
- allow(ChefConfig).to receive(:windows?).and_return(false)
- expect(Chef::Platform).not_to receive(:require)
- expect(Chef::Platform.windows_server_2003?).to be_falsey
- end
-
- # CHEF-4888: Need to call WIN32OLE.ole_initialize in new threads
- it "does not raise an exception" do
- expect { Thread.fork { Chef::Platform.windows_server_2003? }.join }.not_to raise_error
- end
-end
-
describe "Chef::Platform#windows_nano_server?" do
include_context "Win32"
@@ -151,7 +138,7 @@ describe "Chef::Platform#supports_msi?" do
end
end
-describe 'Chef::Platform#supports_dsc?' do
+describe "Chef::Platform#supports_dsc?" do
it "returns false if powershell is not present" do
node = Chef::Node.new
expect(Chef::Platform.supports_dsc?(node)).to be_falsey
@@ -174,7 +161,7 @@ describe 'Chef::Platform#supports_dsc?' do
end
end
-describe 'Chef::Platform#supports_dsc_invoke_resource?' do
+describe "Chef::Platform#supports_dsc_invoke_resource?" do
it "returns false if powershell is not present" do
node = Chef::Node.new
expect(Chef::Platform.supports_dsc_invoke_resource?(node)).to be_falsey
@@ -195,7 +182,7 @@ describe 'Chef::Platform#supports_dsc_invoke_resource?' do
end
end
-describe 'Chef::Platform#dsc_refresh_mode_disabled?' do
+describe "Chef::Platform#dsc_refresh_mode_disabled?" do
let(:node) { instance_double("Chef::Node") }
let(:cmdlet) { instance_double("Chef::Util::Powershell::Cmdlet") }
let(:cmdlet_result) { instance_double("Chef::Util::Powershell::CmdletResult") }
diff --git a/spec/unit/platform_spec.rb b/spec/unit/platform_spec.rb
deleted file mode 100644
index 0559229d62..0000000000
--- a/spec/unit/platform_spec.rb
+++ /dev/null
@@ -1,241 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-
-describe Chef::Platform do
-
- context "while testing with fake data" do
-
- before :all do
- @original_platform_map = Chef::Platform.platforms
- end
-
- after :all do ||
- Chef::Platform.platforms = @original_platform_map
- end
-
- before(:each) do
- Chef::Platform.platforms = {
- :darwin => {
- ">= 10.11" => {
- :file => "new_darwinian",
- },
- "9.2.2" => {
- :file => "darwinian",
- :else => "thing",
- },
- :default => {
- :file => "old school",
- :snicker => "snack",
- },
- },
- :mars_volta => {
- },
- :default => {
- :file => Chef::Provider::File,
- :pax => "brittania",
- :cat => "nice",
- },
- }
- @events = Chef::EventDispatch::Dispatcher.new
- end
-
- it "should allow you to look up a platform by name and version, returning the provider map for it" do
- pmap = Chef::Platform.find("Darwin", "9.2.2")
- expect(pmap).to be_a_kind_of(Hash)
- expect(pmap[:file]).to eql("darwinian")
- end
-
- it "should allow you to look up a platform by name and version using \"greater than\" style operators" do
- pmap = Chef::Platform.find("Darwin", "11.1.0")
- expect(pmap).to be_a_kind_of(Hash)
- expect(pmap[:file]).to eql("new_darwinian")
- end
-
- it "should use the default providers for an os if the specific version does not exist" do
- pmap = Chef::Platform.find("Darwin", "1")
- expect(pmap).to be_a_kind_of(Hash)
- expect(pmap[:file]).to eql("old school")
- end
-
- it "should use the default providers if the os doesn't give me a default, but does exist" do
- pmap = Chef::Platform.find("mars_volta", "1")
- expect(pmap).to be_a_kind_of(Hash)
- expect(pmap[:file]).to eql(Chef::Provider::File)
- end
-
- it "should use the default provider if the os does not exist" do
- pmap = Chef::Platform.find("AIX", "1")
- expect(pmap).to be_a_kind_of(Hash)
- expect(pmap[:file]).to eql(Chef::Provider::File)
- end
-
- it "should merge the defaults for an os with the specific version" do
- pmap = Chef::Platform.find("Darwin", "9.2.2")
- expect(pmap[:file]).to eql("darwinian")
- expect(pmap[:snicker]).to eql("snack")
- end
-
- it "should merge the defaults for an os with the universal defaults" do
- pmap = Chef::Platform.find("Darwin", "9.2.2")
- expect(pmap[:file]).to eql("darwinian")
- expect(pmap[:pax]).to eql("brittania")
- end
-
- it "should allow you to look up a provider for a platform directly by symbol" do
- expect(Chef::Platform.find_provider("Darwin", "9.2.2", :file)).to eql("darwinian")
- end
-
- it "should raise an exception if a provider cannot be found for a resource type" do
- expect { Chef::Platform.find_provider("Darwin", "9.2.2", :coffee) }.to raise_error(Chef::Exceptions::ProviderNotFound)
- end
-
- it "should look up a provider for a resource with a Chef::Resource object" do
- kitty = Chef::Resource::Cat.new("loulou")
- expect(Chef::Platform.find_provider("Darwin", "9.2.2", kitty)).to eql("nice")
- end
-
- it "should look up a provider with a node and a Chef::Resource object" do
- kitty = Chef::Resource::Cat.new("loulou")
- node = Chef::Node.new
- node.name("Intel")
- node.automatic_attrs[:platform] = "mac_os_x"
- node.automatic_attrs[:platform_version] = "9.2.2"
- expect(Chef::Platform.find_provider_for_node(node, kitty)).to eql("nice")
- end
-
- it "should not throw an exception when the platform version has an unknown format" do
- expect(Chef::Platform.find_provider(:darwin, "bad-version", :file)).to eql("old school")
- end
-
- it "should prefer an explicit provider" do
- kitty = Chef::Resource::Cat.new("loulou")
- allow(kitty).to receive(:provider).and_return(Chef::Provider::File)
- node = Chef::Node.new
- node.name("Intel")
- node.automatic_attrs[:platform] = "mac_os_x"
- node.automatic_attrs[:platform_version] = "9.2.2"
- expect(Chef::Platform.find_provider_for_node(node, kitty)).to eql(Chef::Provider::File)
- end
-
- it "should look up a provider based on the resource name if nothing else matches" do
- kitty = Chef::Resource::Cat.new("loulou")
- class Chef::Provider::Cat < Chef::Provider; end
- Chef::Platform.platforms[:default].delete(:cat)
- node = Chef::Node.new
- node.name("Intel")
- node.automatic_attrs[:platform] = "mac_os_x"
- node.automatic_attrs[:platform_version] = "8.5"
- expect(Chef::Platform.find_provider_for_node(node, kitty)).to eql(Chef::Provider::Cat)
- end
-
- def setup_file_resource
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "mac_os_x"
- node.automatic_attrs[:platform_version] = "9.2.2"
- run_context = Chef::RunContext.new(node, {}, @events)
- [ Chef::Resource::File.new("whateva", run_context), run_context ]
- end
-
- it "returns a provider object given a Chef::Resource object which has a valid run context and an action" do
- file, run_context = setup_file_resource
- provider = Chef::Platform.provider_for_resource(file, :foo)
- expect(provider).to be_an_instance_of(Chef::Provider::File)
- expect(provider.new_resource).to equal(file)
- expect(provider.run_context).to equal(run_context)
- end
-
- it "returns a provider object given a Chef::Resource object which has a valid run context without an action" do
- file, run_context = setup_file_resource
- provider = Chef::Platform.provider_for_resource(file)
- expect(provider).to be_an_instance_of(Chef::Provider::File)
- expect(provider.new_resource).to equal(file)
- expect(provider.run_context).to equal(run_context)
- end
-
- it "raises an error when trying to find the provider for a resource with no run context" do
- file = Chef::Resource::File.new("whateva")
- expect { Chef::Platform.provider_for_resource(file) }.to raise_error(ArgumentError)
- end
-
- it "does not support finding a provider by resource and node -- a run context is required" do
- expect { Chef::Platform.provider_for_node("node", "resource") }.to raise_error(NotImplementedError)
- end
-
- it "should update the provider map with map" do
- Chef::Platform.set(
- :platform => :darwin,
- :version => "9.2.2",
- :resource => :file,
- :provider => "masterful"
- )
- expect(Chef::Platform.platforms[:darwin]["9.2.2"][:file]).to eql("masterful")
- Chef::Platform.set(
- :platform => :darwin,
- :resource => :file,
- :provider => "masterful"
- )
- expect(Chef::Platform.platforms[:darwin][:default][:file]).to eql("masterful")
- Chef::Platform.set(
- :resource => :file,
- :provider => "masterful"
- )
- expect(Chef::Platform.platforms[:default][:file]).to eql("masterful")
-
- Chef::Platform.set(
- :platform => :hero,
- :version => "9.2.2",
- :resource => :file,
- :provider => "masterful"
- )
- expect(Chef::Platform.platforms[:hero]["9.2.2"][:file]).to eql("masterful")
-
- Chef::Platform.set(
- :resource => :file,
- :provider => "masterful"
- )
- expect(Chef::Platform.platforms[:default][:file]).to eql("masterful")
-
- Chef::Platform.platforms = {}
-
- Chef::Platform.set(
- :resource => :file,
- :provider => "masterful"
- )
- expect(Chef::Platform.platforms[:default][:file]).to eql("masterful")
-
- Chef::Platform.platforms = { :neurosis => {} }
- Chef::Platform.set(:platform => :neurosis, :resource => :package, :provider => "masterful")
- expect(Chef::Platform.platforms[:neurosis][:default][:package]).to eql("masterful")
-
- end
-
- it "does not overwrite the platform map when using :default platform" do
- Chef::Platform.set(
- :resource => :file,
- :platform => :default,
- :provider => "new school"
- )
- expect(Chef::Platform.platforms[:default][:file]).to eql("new school")
- expect(Chef::Platform.platforms[:default][:cat]).to eql("nice")
- end
-
- end
-
-end
diff --git a/spec/unit/policy_builder/dynamic_spec.rb b/spec/unit/policy_builder/dynamic_spec.rb
index f91b0ba7d2..d94b2a69a2 100644
--- a/spec/unit/policy_builder/dynamic_spec.rb
+++ b/spec/unit/policy_builder/dynamic_spec.rb
@@ -254,7 +254,7 @@ describe Chef::PolicyBuilder::Dynamic do
context "when running chef solo" do
before do
- Chef::Config[:solo] = true
+ Chef::Config[:solo_legacy_mode] = true
expect(Chef::Node).to receive(:build).with(node_name).and_return(node)
expect(policy_builder).to receive(:select_implementation).with(node)
expect(implementation).to receive(:finish_load_node).with(node)
diff --git a/spec/unit/policy_builder/expand_node_object_spec.rb b/spec/unit/policy_builder/expand_node_object_spec.rb
index 420db2e855..a7f4f1fa43 100644
--- a/spec/unit/policy_builder/expand_node_object_spec.rb
+++ b/spec/unit/policy_builder/expand_node_object_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,11 +34,7 @@ describe Chef::PolicyBuilder::ExpandNodeObject do
expect(policy_builder).to respond_to(:node)
end
- it "implements a load_node method for backwards compatibility until Chef 13" do
- expect(policy_builder).to respond_to(:load_node)
- end
-
- it "has removed the deprecated #load_node method", chef: ">= 13" do
+ it "has removed the deprecated #load_node method" do
expect(policy_builder).to_not respond_to(:load_node)
end
@@ -106,27 +102,6 @@ describe Chef::PolicyBuilder::ExpandNodeObject do
end
- context "deprecated #load_node method" do
-
- let(:node) do
- node = Chef::Node.new
- node.name(node_name)
- node.run_list(["recipe[a::default]", "recipe[b::server]"])
- node
- end
-
- before do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node)
- policy_builder.load_node
- end
-
- it "loads the node" do
- expect(policy_builder.node).to eq(node)
- end
-
- end
-
context "once the node has been loaded" do
let(:node) do
node = Chef::Node.new
diff --git a/spec/unit/policy_builder/policyfile_spec.rb b/spec/unit/policy_builder/policyfile_spec.rb
index 0f345ee344..5663cb8b19 100644
--- a/spec/unit/policy_builder/policyfile_spec.rb
+++ b/spec/unit/policy_builder/policyfile_spec.rb
@@ -70,8 +70,44 @@ describe Chef::PolicyBuilder::Policyfile do
}
end
- let(:policyfile_default_attributes) { { "policyfile_default_attr" => "policyfile_default_value" } }
- let(:policyfile_override_attributes) { { "policyfile_override_attr" => "policyfile_override_value" } }
+ let(:policyfile_default_attributes) do
+ {
+ "policyfile_default_attr" => "policyfile_default_value",
+ "top_level_attr" => "hat",
+ "baseline_attr" => {
+ "one" => 1,
+ "two" => 2,
+ "deep" => {
+ "three" => 3,
+ "four" => [4],
+ "five" => [5],
+ },
+ },
+ "policy_group_value" => {
+ "baseline_attr" => {
+ "one" => 111,
+ },
+ },
+ }
+ end
+
+ let(:policyfile_override_attributes) do
+ {
+ "policyfile_override_attr" => "policyfile_override_value",
+ "baseline_attr" => {
+ "deep" => {
+ "three" => 333 },
+ },
+ "policy_group_value" => {
+ "top_level_attr" => "cat",
+ "baseline_attr" => {
+ "deep" => {
+ "four" => [444],
+ },
+ },
+ },
+ }
+ end
let(:policyfile_run_list) { ["recipe[example1::default]", "recipe[example2::server]"] }
@@ -100,8 +136,8 @@ describe Chef::PolicyBuilder::Policyfile do
http = double("Chef::ServerAPI")
server_url = "https://api.opscode.com/organizations/example"
Chef::Config[:chef_server_url] = server_url
- expect(Chef::ServerAPI).to receive(:new).with(server_url).and_return(http)
- expect(policy_builder.http_api).to eq(http)
+ expect(Chef::ServerAPI).to receive(:new).with(server_url, version_class: Chef::CookbookManifestVersions).and_return(http)
+ expect(policy_builder.api_service).to eq(http)
end
describe "reporting unsupported features" do
@@ -115,7 +151,7 @@ describe Chef::PolicyBuilder::Policyfile do
end
context "chef-solo" do
- before { Chef::Config[:solo] = true }
+ before { Chef::Config[:solo_legacy_mode] = true }
it "errors on create" do
expect { initialize_pb }.to raise_error(err_namespace::UnsupportedFeature)
@@ -150,7 +186,7 @@ describe Chef::PolicyBuilder::Policyfile do
describe "loading policy data" do
- let(:http_api) { double("Chef::ServerAPI") }
+ let(:api_service) { double("Chef::ServerAPI") }
let(:configured_environment) { nil }
@@ -172,7 +208,7 @@ describe Chef::PolicyBuilder::Policyfile do
before do
Chef::Config[:policy_document_native_api] = false
Chef::Config[:deployment_group] = "example-policy-stage"
- allow(policy_builder).to receive(:http_api).and_return(http_api)
+ allow(policy_builder).to receive(:api_service).and_return(api_service)
end
describe "when using compatibility mode (policy_document_native_api == false)" do
@@ -185,7 +221,7 @@ describe Chef::PolicyBuilder::Policyfile do
let(:error404) { Net::HTTPServerException.new("404 message", :body) }
before do
- expect(http_api).to receive(:get).
+ expect(api_service).to receive(:get).
with("data/policyfiles/example-policy-stage").
and_raise(error404)
end
@@ -212,7 +248,7 @@ describe Chef::PolicyBuilder::Policyfile do
let(:policy_relative_url) { "data/policyfiles/example-policy-stage" }
before do
- expect(http_api).to receive(:get).with(policy_relative_url).and_return(parsed_policyfile_json)
+ expect(api_service).to receive(:get).with(policy_relative_url).and_return(parsed_policyfile_json)
end
it "fetches the policy file from a data bag item" do
@@ -253,7 +289,7 @@ describe Chef::PolicyBuilder::Policyfile do
let(:policy_relative_url) { "policy_groups/policy-stage/policies/example" }
before do
- expect(http_api).to receive(:get).with(policy_relative_url).and_return(parsed_policyfile_json)
+ expect(api_service).to receive(:get).with(policy_relative_url).and_return(parsed_policyfile_json)
end
it "fetches the policy file from a data bag item" do
@@ -330,6 +366,56 @@ describe Chef::PolicyBuilder::Policyfile do
end
+ describe "#build_node" do
+
+ let(:node) do
+ node = Chef::Node.new
+ node.name(node_name)
+ node
+ end
+
+ before do
+ allow(policy_builder).to receive(:node).and_return(node)
+ end
+
+ context "when the run is successful" do
+ let(:run_list) do
+ ["recipe[test::default]",
+ "recipe[test::other]"]
+ end
+
+ let(:version_hash) do
+ {
+ "version" => "0.1.0",
+ "identifier" => "012345678",
+ }
+ end
+
+ let(:run_list_for_data_collector) do
+ {
+ :id => "_policy_node",
+ :run_list => [
+ { :type => "recipe", :name => "test::default", :skipped => false, :version => nil },
+ { :type => "recipe", :name => "test::other", :skipped => false, :version => nil },
+ ],
+ }
+ end
+
+ before do
+ allow(policy_builder).to receive(:run_list)
+ .and_return(run_list)
+ allow(policy_builder).to receive(:cookbook_lock_for)
+ .and_return(version_hash)
+ end
+
+ it "sends the run_list_expanded event" do
+ policy_builder.build_node
+ expect(policy_builder.run_list_expansion_ish.to_hash)
+ .to eq(run_list_for_data_collector)
+ end
+ end
+ end
+
describe "building the node object" do
let(:extra_chef_config) { {} }
@@ -488,6 +574,10 @@ describe Chef::PolicyBuilder::Policyfile do
expect(Chef::Config[:policy_group]).to eq("policy_group_from_node_json")
expect(node.policy_name).to eq("policy_name_from_node_json")
expect(node.policy_group).to eq("policy_group_from_node_json")
+ expect(node.automatic_attrs[:policy_name]).to eq("policy_name_from_node_json")
+ expect(node.automatic_attrs[:policy_group]).to eq("policy_group_from_node_json")
+ expect(node.automatic_attrs[:chef_environment]).to eq("policy_group_from_node_json")
+
end
end
@@ -537,6 +627,7 @@ describe Chef::PolicyBuilder::Policyfile do
it "create node.automatic_attrs[:recipes]" do
expect(node.automatic_attrs[:recipes]).to eq(["example1::default", "example2::server"])
end
+
end
context "when a named run_list is given" do
@@ -586,6 +677,42 @@ describe Chef::PolicyBuilder::Policyfile do
end
end
+
+ describe "hoisting attribute values" do
+ context "with no policy group set" do
+ it "does not hoist policy_group specific attributes" do
+ expect( node["top_level_attr"] ).to eql("hat")
+ expect( node["baseline_attr"]["one"] ).to eql(1)
+ expect( node["baseline_attr"]["two"] ).to eql(2)
+ expect( node["baseline_attr"]["deep"]["three"] ).to eql(333)
+ expect( node["baseline_attr"]["deep"]["four"] ).to eql([4])
+ expect( node["baseline_attr"]["deep"]["five"] ).to eql([5])
+ end
+ end
+
+ context "with a policy group set" do
+ before do
+ Chef::Config[:policy_group] = "policy_group_value"
+ policy_builder.finish_load_node(node)
+ policy_builder.build_node
+ end
+
+ it "hoists default attributes" do
+ expect( node["top_level_attr"] ).to eql("cat")
+ expect( node["baseline_attr"]["one"]).to eql(111)
+ expect( node["baseline_attr"]["two"] ).to eql(2)
+ expect( node["baseline_attr"]["deep"]["five"] ).to eql([5])
+ end
+
+ it "hoists override attributes" do
+ expect( node["top_level_attr"] ).to eql("cat")
+ expect( node["baseline_attr"]["two"] ).to eql(2)
+ expect( node["baseline_attr"]["deep"]["three"] ).to eql(333)
+ expect( node["baseline_attr"]["deep"]["four"] ).to eql([444])
+ expect( node["baseline_attr"]["deep"]["five"] ).to eql([5])
+ end
+ end
+ end
end
describe "fetching the desired cookbook set" do
@@ -617,7 +744,7 @@ describe Chef::PolicyBuilder::Policyfile do
policy_builder.finish_load_node(node)
policy_builder.build_node
- expect(http_api).to receive(:get).with(cookbook1_url).
+ expect(api_service).to receive(:get).with(cookbook1_url).
and_raise(error404)
end
@@ -687,9 +814,9 @@ describe Chef::PolicyBuilder::Policyfile do
context "when the cookbooks exist on the server" do
before do
- expect(http_api).to receive(:get).with(cookbook1_url).
+ expect(api_service).to receive(:get).with(cookbook1_url).
and_return(example1_cookbook_data)
- expect(http_api).to receive(:get).with(cookbook2_url).
+ expect(api_service).to receive(:get).with(cookbook2_url).
and_return(example2_cookbook_data)
expect(Chef::CookbookVersion).to receive(:from_cb_artifact_data).with(example1_cookbook_data).
@@ -720,9 +847,9 @@ describe Chef::PolicyBuilder::Policyfile do
context "when the cookbooks exist on the server" do
before do
- expect(http_api).to receive(:get).with(cookbook1_url).
+ expect(api_service).to receive(:get).with(cookbook1_url).
and_return(example1_cookbook_data)
- expect(http_api).to receive(:get).with(cookbook2_url).
+ expect(api_service).to receive(:get).with(cookbook2_url).
and_return(example2_cookbook_data)
expect(Chef::CookbookVersion).to receive(:from_cb_artifact_data).with(example1_cookbook_data).
diff --git a/spec/unit/property/validation_spec.rb b/spec/unit/property/validation_spec.rb
index 4e1b252863..b05d8c4e17 100644
--- a/spec/unit/property/validation_spec.rb
+++ b/spec/unit/property/validation_spec.rb
@@ -100,13 +100,10 @@ describe "Chef::Resource.property validation" do
expect(resource.x).to be_nil
end
unless tags.include?(:nillable)
- it "changing x to nil warns that the get will change to a set in Chef 13 and does not change the value" do
+ it "changing x to nil does a set" do
resource.instance_eval { @x = "default" }
- expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /An attempt was made to change x from "default" to nil by calling x\(nil\). In Chef 12, this does a get rather than a set. In Chef 13, this will change to set the value to nil./
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- expect(resource.x nil).to eq "default"
- expect(resource.x).to eq "default"
+ expect(resource.x nil).to eq nil
+ expect(resource.x).to eq nil
end
end
end
@@ -116,13 +113,10 @@ describe "Chef::Resource.property validation" do
expect(resource.x nil).to be_nil
expect(resource.x).to be_nil
end
- it "changing x to nil warns that the get will change to a set in Chef 13 and does not change the value" do
+ it "changing x to nil does a set" do
resource.instance_eval { @x = "default" }
- expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /An attempt was made to change x from "default" to nil by calling x\(nil\). In Chef 12, this does a get rather than a set. In Chef 13, this will change to set the value to nil./
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- expect(resource.x nil).to eq "default"
- expect(resource.x).to eq "default"
+ expect(resource.x nil).to eq nil
+ expect(resource.x).to eq nil
end
end
elsif tags.include?(:nillable)
@@ -134,26 +128,17 @@ describe "Chef::Resource.property validation" do
expect(resource.x).to eq nil
end
end
- else
+ elsif tags.include?(:delayed_nil_default_failure)
it "property :x, #{validation}, default: nil warns that the default is invalid" do
- expect { resource_class.class_eval("property :x, #{validation}, default: nil", __FILE__, __LINE__) }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /Default value nil is invalid for property x of resource chef_resource_property_spec_(\d+). Possible fixes: 1. Remove 'default: nil' if nil means 'undefined'. 2. Set a valid default value if there is a reasonable one. 3. Allow nil as a valid value of your property \(for example, 'property :x, \[ String, nil \], default: nil'\)./
+ expect { resource_class.class_eval("property :x, #{validation}, default: nil", __FILE__, __LINE__) }.not_to raise_error
+ expect { resource.x }.to raise_error Chef::Exceptions::ValidationFailed, /Property x must be one of: .* You passed nil./
end
- context "With property :x, #{validation}, default: nil" do
- before do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- resource_class.class_eval("property :x, #{validation}, default: nil", __FILE__, __LINE__)
- Chef::Config[:treat_deprecation_warnings_as_errors] = true
- end
-
- it "changing x to nil emits a warning that the value is invalid and does not change the value" do
- resource.instance_eval { @x = "default" }
- expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /nil is an invalid value for x of resource chef_resource_property_spec_(\d+). In Chef 13, this warning will change to an error./
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- expect(resource.x nil).to eq "default"
- expect(resource.x).to eq "default"
- end
+ elsif tags.include?(:skip_nil_default)
+ # intentionally left blank
+ else
+ it "property :x, #{validation}, default: nil warns that the default is invalid" do
+ expect { resource_class.class_eval("property :x, #{validation}, default: nil", __FILE__, __LINE__) }.to raise_error Chef::Exceptions::ValidationFailed,
+ /Property x must be one of: .* You passed nil./
end
end
end
@@ -174,12 +159,10 @@ describe "Chef::Resource.property validation" do
it "set to invalid value raises ValidationFailed" do
expect { resource.x 10 }.to raise_error Chef::Exceptions::ValidationFailed
end
- it "set to nil emits a deprecation warning and does a get" do
- expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ it "set to nil does a set" do
resource.x "str"
- expect(resource.x nil).to eq "str"
- expect(resource.x).to eq "str"
+ expect(resource.x nil).to eq nil
+ expect(resource.x).to eq nil
end
end
context "when the variable does not have an initial value" do
@@ -206,12 +189,9 @@ describe "Chef::Resource.property validation" do
it "get succeeds" do
expect(resource.x).to eq "default"
end
- it "set(nil) emits a warning that the value will be set, but does not set the value" do
- expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /An attempt was made to change x from "default" to nil by calling x\(nil\). In Chef 12, this does a get rather than a set. In Chef 13, this will change to set the value to nil./
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- expect(resource.x nil).to eq "default"
- expect(resource.x).to eq "default"
+ it "set(nil) does a set" do
+ expect(resource.x nil).to eq nil
+ expect(resource.x).to eq nil
end
it "set to valid value succeeds" do
expect(resource.x "str").to eq "str"
@@ -328,11 +308,17 @@ describe "Chef::Resource.property validation" do
# Proc
validation_test "is: proc { |x| x }",
[ true, 1 ],
- [ false ]
+ [ false ],
+ # this is somewhat complicated, we test adding `default: nil` and that the default fails, but with a proc the
+ # validation is delayed until access, so we have to test after access not after declaring the default
+ :delayed_nil_default_failure
validation_test "is: proc { |x| x > blah }",
[ 10 ],
- [ -1 ]
+ [ -1 ],
+ # here the test of adding `default: nil` just causes the proc to explode because nil gets passed to the proc
+ # which throws a NoMethodError on NilClass for the `>` method.
+ :skip_nil_default
validation_test "is: nil",
[ ],
@@ -589,15 +575,21 @@ describe "Chef::Resource.property validation" do
it "value nil emits a validation failed error because it must have a value" do
expect { resource.x nil }.to raise_error Chef::Exceptions::ValidationFailed
end
- context "and value is set to something other than nil" do
- before { resource.x 10 }
- it "value nil emits a deprecation warning and does a get" do
- expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- resource.x 1
- expect(resource.x nil).to eq 1
- expect(resource.x).to eq 1
- end
+ end
+
+ with_property ":x, String, required: true" do
+ it "if x is not specified, retrieval fails" do
+ expect { resource.x }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ it "value nil is not valid (required means 'not nil')" do
+ expect { resource.x nil }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ it "value '1' is valid" do
+ expect(resource.x "1").to eq "1"
+ expect(resource.x).to eq "1"
+ end
+ it "value 1 is invalid" do
+ expect { resource.x 1 }.to raise_error Chef::Exceptions::ValidationFailed
end
end
@@ -625,12 +617,9 @@ describe "Chef::Resource.property validation" do
expect(resource.x 1).to eq 1
expect(resource.x).to eq 1
end
- it "value nil emits a deprecation warning and does a get" do
- expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- resource.x 1
- expect(resource.x nil).to eq 1
- expect(resource.x).to eq 1
+ it "value nil sets to the default" do
+ # this mildly complicated because the default of a name property is a lazy evaluator to the actual resource.name
+ expect(resource.x nil).to be_a(Chef::DelayedEvaluator)
end
end
@@ -642,12 +631,54 @@ describe "Chef::Resource.property validation" do
expect(resource.x 1).to eq 1
expect(resource.x).to eq 1
end
- it "value nil is invalid" do
- expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- resource.x 1
- expect(resource.x nil).to eq 1
- expect(resource.x).to eq 1
+ it "value nil sets the default" do
+ expect(resource.x nil).to eq 10
+ expect(resource.x).to eq 10
+ end
+ end
+ end
+
+ context "nil setting default" do
+ with_property ":x, String" do
+ it "if x is not specified, the default is returned" do
+ expect(resource.x).to eq nil
+ end
+ it "value '2' is valid" do
+ expect(resource.x "2").to eq "2"
+ expect(resource.x).to eq "2"
+ end
+ it "value nil sets the default" do
+ resource.x "2"
+ expect(resource.x nil).to eq nil
+ expect(resource.x).to eq nil
+ end
+ end
+ with_property ":x, String, default: '1'" do
+ it "if x is not specified, the default is returned" do
+ expect(resource.x).to eq "1"
+ end
+ it "value '2' is valid" do
+ expect(resource.x "2").to eq "2"
+ expect(resource.x).to eq "2"
+ end
+ it "value nil sets the default" do
+ resource.x "2"
+ expect(resource.x nil).to eq "1"
+ expect(resource.x).to eq "1"
+ end
+ end
+ with_property ":x, [ String, nil ] , default: '1'" do
+ it "if x is not specified, the default is returned" do
+ expect(resource.x).to eq "1"
+ end
+ it "value '2' is valid" do
+ expect(resource.x "2").to eq "2"
+ expect(resource.x).to eq "2"
+ end
+ it "value nil sets to nil" do
+ resource.x "2"
+ expect(resource.x nil).to eq nil
+ expect(resource.x).to eq nil
end
end
end
@@ -675,15 +706,13 @@ describe "Chef::Resource.property validation" do
end
it "value '1' is invalid" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
expect { resource.x "1" }.to raise_error Chef::Exceptions::ValidationFailed
end
- it "value nil does a get" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ it "value nil does a set" do
resource.x 1
resource.x nil
- expect(resource.x).to eq 1
+ expect(resource.x).to eq nil
end
end
end
@@ -709,12 +738,21 @@ describe "Chef::Resource.property validation" do
expect { resource.x "1" }.to raise_error Chef::Exceptions::ValidationFailed
end
- it "value nil does a get" do
+ it "value nil does a set" do
resource.x 1
resource.x nil
- expect(resource.x).to eq 1
+ expect(resource.x).to eq nil
end
end
end
end
+
+ context "custom validation messages" do
+ with_property ":x, String, validation_message: 'Must be a string, fool'" do
+ it "raise with the correct error message" do
+ expect { resource.x 1 }.to raise_error Chef::Exceptions::ValidationFailed,
+ "Must be a string, fool"
+ end
+ end
+ end
end
diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb
index f5eb361426..79c4baa89f 100644
--- a/spec/unit/property_spec.rb
+++ b/spec/unit/property_spec.rb
@@ -82,12 +82,10 @@ describe "Chef::Resource.property" do
expect(resource.bare_property 10).to eq 10
expect(resource.bare_property).to eq 10
end
- it "emits a deprecation warning and does a get, if set to nil" do
+ it "nil does a set" do
expect(resource.bare_property 10).to eq 10
- expect { resource.bare_property nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- expect(resource.bare_property nil).to eq 10
- expect(resource.bare_property).to eq 10
+ expect(resource.bare_property nil).to eq nil
+ expect(resource.bare_property).to eq nil
end
it "can be updated" do
expect(resource.bare_property 10).to eq 10
@@ -95,7 +93,7 @@ describe "Chef::Resource.property" do
expect(resource.bare_property).to eq 20
end
it "can be set with =" do
- expect(resource.bare_property 10).to eq 10
+ expect(resource.bare_property = 10).to eq 10
expect(resource.bare_property).to eq 10
end
it "can be set to nil with =" do
@@ -110,6 +108,29 @@ describe "Chef::Resource.property" do
end
end
+ with_property ":Straße" do
+ it "properties with UTF-8 in their name work" do
+ expect(resource.Straße).to eql(nil)
+ expect(resource.Straße "foo").to eql("foo")
+ expect(resource.Straße).to eql("foo")
+ expect(resource.Straße = "bar").to eql("bar")
+ expect(resource.Straße).to eql("bar")
+ end
+ end
+
+ context "deprecated properties" do
+ it "does not create a deprecation warning on definition" do
+ expect { resource_class.class_eval { property :x, String, deprecated: 10 } }.not_to raise_error Chef::Exceptions::DeprecatedFeatureError
+ end
+
+ with_property ":x, deprecated: 'a deprecated property'" do
+ it "deprecated properties emit a deprecation warning" do
+ expect(Chef).to receive(:deprecated).with(:property, "a deprecated property")
+ expect(resource.x 10).to eq 10
+ end
+ end
+ end
+
with_property ":x, name_property: true" do
context "and subclass" do
let(:subresource_class) do
@@ -293,7 +314,7 @@ describe "Chef::Resource.property" do
expect(resource.property_is_set?(:name)).to be_truthy
resource.reset_property(:name)
expect(resource.property_is_set?(:name)).to be_falsey
- expect(resource.name).to be_nil
+ expect { resource.name }.to raise_error Chef::Exceptions::ValidationFailed
end
it "when referencing an undefined property, reset_property(:x) raises an error" do
@@ -514,22 +535,41 @@ describe "Chef::Resource.property" do
end
end
- context "hash default" do
- context "(deprecations allowed)" do
- before { Chef::Config[:treat_deprecation_warnings_as_errors] = false }
+ context "string default" do
+ with_property ":x, default: ''" do
+ it "when x is not set, it returns ''" do
+ expect(resource.x).to eq ""
+ end
+ it "x is immutable" do
+ expect { resource.x << "foo" }.to raise_error(RuntimeError, "can't modify frozen String")
+ end
+ end
- with_property ":x, default: {}" do
- it "when x is not set, it returns {}" do
- expect(resource.x).to eq({})
- end
- it "The same exact value is returned multiple times in a row" do
- value = resource.x
- expect(value).to eq({})
- expect(resource.x.object_id).to eq(value.object_id)
- end
- it "Multiple instances of x receive the exact same value" do
- expect(resource.x.object_id).to eq(resource_class.new("blah2").x.object_id)
- end
+ with_property ":x, default: lazy { '' }" do
+ it "x is immutable" do
+ expect(resource.x).to eq ""
+ resource.x << "foo"
+ expect(resource.x).to eq "foo"
+ expect(resource_class.new("other").x).to eq ""
+ end
+ end
+ end
+
+ context "hash default" do
+ with_property ":x, default: {}" do
+ it "when x is not set, it returns {}" do
+ expect(resource.x).to eq({})
+ end
+ it "x is immutable" do
+ expect { resource.x["foo"] = "bar" }.to raise_error(RuntimeError, "can't modify frozen Hash")
+ end
+ it "The same exact value is returned multiple times in a row" do
+ value = resource.x
+ expect(value).to eq({})
+ expect(resource.x.object_id).to eq(value.object_id)
+ end
+ it "Multiple instances of x receive the exact same value" do
+ expect(resource.x.object_id).to eq(resource_class.new("blah2").x.object_id)
end
end
@@ -537,17 +577,34 @@ describe "Chef::Resource.property" do
it "when x is not set, it returns {}" do
expect(resource.x).to eq({})
end
- # it "The value is different each time it is called" do
- # value = resource.x
- # expect(value).to eq({})
- # expect(resource.x.object_id).not_to eq(value.object_id)
- # end
+ it "The value is the same each time it is called" do
+ value = resource.x
+ expect(value).to eq({})
+ expect(resource.x.object_id).to eq(value.object_id)
+ end
it "Multiple instances of x receive different values" do
expect(resource.x.object_id).not_to eq(resource_class.new("blah2").x.object_id)
end
end
end
+ context "complex, nested default" do
+ with_property ":x, default: [{foo: 'bar'}]" do
+ it "when x is not set, it returns [{foo: 'bar'}]" do
+ expect(resource.x).to eq([{ foo: "bar" }])
+ end
+ it "x is immutable" do
+ expect { resource.x << :other }.to raise_error(RuntimeError, "can't modify frozen Array")
+ end
+ it "x.first is immutable" do
+ expect { resource.x.first[:foo] = "other" }.to raise_error(RuntimeError, "can't modify frozen Hash")
+ end
+ it "x.first[:foo] is immutable" do
+ expect { resource.x.first[:foo] << "other" }.to raise_error(RuntimeError, "can't modify frozen String")
+ end
+ end
+ end
+
context "with a class with 'blah' as both class and instance methods" do
before do
resource_class.class_eval do
@@ -568,7 +625,6 @@ describe "Chef::Resource.property" do
it "x is run in the context of each instance it is run in" do
expect(resource.x).to eq "blah1"
expect(resource_class.new("another").x).to eq "another2"
- # expect(resource.x).to eq "blah3"
end
end
@@ -579,30 +635,13 @@ describe "Chef::Resource.property" do
it "x is passed the value of each instance it is run in" do
expect(resource.x).to eq "classblah1"
expect(resource_class.new("another").x).to eq "classanother2"
- # expect(resource.x).to eq "classblah3"
end
end
end
context "validation of defaults" do
- it "When a class is declared with property :x, String, default: 10, a warning is emitted" do
- expect { resource_class.class_eval { property :x, String, default: 10 } }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /Default value 10 is invalid for property x of resource chef_resource_property_spec_(\d+). In Chef 13 this will become an error: Property x must be one of: String! You passed 10./
- end
- context "With property :x, String, default: 10" do
- before do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- resource_class.class_eval { property :x, String, default: 10 }
- Chef::Config[:treat_deprecation_warnings_as_errors] = true
- end
-
- it "when x is set, no error is raised" do
- expect(resource.x "hi").to eq "hi"
- expect(resource.x).to eq "hi"
- end
- it "when x is retrieved, no validation error is raised" do
- expect(resource.x).to eq 10
- end
+ it "When a class is declared with property :x, String, default: 10, it immediately fails validation" do
+ expect { resource_class.class_eval { property :x, String, default: 10 } }.to raise_error Chef::Exceptions::ValidationFailed
end
with_property ":x, String, default: lazy { Namer.next_index }" do
@@ -613,12 +652,9 @@ describe "Chef::Resource.property" do
expect(resource.x "hi").to eq "hi"
expect(resource.x).to eq "hi"
end
- it "when x is retrieved, an invalid default warning is emitted and the value is returned" do
- expect { resource.x }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /Default value 1 is invalid for property x of resource chef_resource_property_spec_(\d+). In Chef 13 this will become an error: Property x must be one of: String! You passed 1./
+ it "when x is retrieved, it fails validation" do
+ expect { resource.x }.to raise_error Chef::Exceptions::ValidationFailed
expect(Namer.current_index).to eq 1
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- expect(resource.x).to eq 2
end
end
@@ -731,11 +767,8 @@ describe "Chef::Resource.property" do
end
end
with_property ':x, Integer, coerce: proc { |v| "#{v}#{next_index}" }, default: 10' do
- it "when x is retrieved, it is coerced and emits an invalid default warning, but still returns the value" do
- expect { resource.x }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /Default value 10 is invalid for property x of resource chef_resource_property_spec_(\d+). In Chef 13 this will become an error: Property x must be one of: Integer! You passed "101"./
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- expect(resource.x).to eq "102"
+ it "when x is retrieved, it is coerced and fails validation" do
+ expect { resource.x }.to raise_error Chef::Exceptions::ValidationFailed
end
end
with_property ':x, String, coerce: proc { |v| "#{v}#{next_index}" }, default: lazy { 10 }' do
@@ -744,11 +777,8 @@ describe "Chef::Resource.property" do
end
end
with_property ':x, Integer, coerce: proc { |v| "#{v}#{next_index}" }, default: lazy { 10 }' do
- it "when x is retrieved, it is coerced and emits an invalid default warning; the value is still returned." do
- expect { resource.x }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /Default value 10 is invalid for property x of resource chef_resource_property_spec_(\d+). In Chef 13 this will become an error: Property x must be one of: Integer! You passed "101"./
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- expect(resource.x).to eq "102"
+ it "when x is retrieved, it is coerced and fails validation" do
+ expect { resource.x }.to raise_error Chef::Exceptions::ValidationFailed
end
end
with_property ':x, proc { |v| Namer.next_index; true }, coerce: proc { |v| "#{v}#{next_index}" }, default: lazy { 10 }' do
@@ -1019,102 +1049,51 @@ describe "Chef::Resource.property" do
end
context "default ordering deprecation warnings" do
- it "emits a deprecation warning for property :x, default: 10, #{name}: true" do
- expect { resource_class.property :x, :default => 10, name.to_sym => true }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /Cannot specify both default and name_property together on property x of resource chef_resource_property_spec_(\d+). Only one \(default\) will be obeyed./
+ it "emits an error for property :x, default: 10, #{name}: true" do
+ expect { resource_class.property :x, :default => 10, name.to_sym => true }.to raise_error ArgumentError,
+ /A property cannot be both a name_property\/name_attribute and have a default value. Use one or the other on property x of resource chef_resource_property_spec_(\d+)/
end
- it "emits a deprecation warning for property :x, default: nil, #{name}: true" do
- expect { resource_class.property :x, :default => nil, name.to_sym => true }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /Cannot specify both default and name_property together on property x of resource chef_resource_property_spec_(\d+). Only one \(name_property\) will be obeyed./
+ it "emits an error for property :x, default: nil, #{name}: true" do
+ expect { resource_class.property :x, :default => nil, name.to_sym => true }.to raise_error ArgumentError,
+ /A property cannot be both a name_property\/name_attribute and have a default value. Use one or the other on property x of resource chef_resource_property_spec_(\d+)/
end
- it "emits a deprecation warning for property :x, #{name}: true, default: 10" do
- expect { resource_class.property :x, name.to_sym => true, :default => 10 }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /Cannot specify both default and name_property together on property x of resource chef_resource_property_spec_(\d+). Only one \(name_property\) will be obeyed./
+ it "emits an error for property :x, #{name}: true, default: 10" do
+ expect { resource_class.property :x, name.to_sym => true, :default => 10 }.to raise_error ArgumentError,
+ /A property cannot be both a name_property\/name_attribute and have a default value. Use one or the other on property x of resource chef_resource_property_spec_(\d+)/
end
- it "emits a deprecation warning for property :x, #{name}: true, default: nil" do
- expect { resource_class.property :x, name.to_sym => true, :default => nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /Cannot specify both default and name_property together on property x of resource chef_resource_property_spec_(\d+). Only one \(name_property\) will be obeyed./
+ it "emits an error for property :x, #{name}: true, default: nil" do
+ expect { resource_class.property :x, name.to_sym => true, :default => nil }.to raise_error ArgumentError,
+ /A property cannot be both a name_property\/name_attribute and have a default value. Use one or the other on property x of resource chef_resource_property_spec_(\d+)/
end
end
-
- context "default ordering" do
- before { Chef::Config[:treat_deprecation_warnings_as_errors] = false }
- with_property ":x, default: 10, #{name}: true" do
- it "chooses default over #{name}" do
- expect(resource.x).to eq 10
- end
- end
- with_property ":x, default: nil, #{name}: true" do
- it "chooses #{name} over default" do
- expect(resource.x).to eq "blah"
- end
- end
- with_property ":x, #{name}: true, default: 10" do
- it "chooses #{name} over default" do
- expect(resource.x).to eq "blah"
- end
- end
- with_property ":x, #{name}: true, default: nil" do
- it "chooses #{name} over default" do
- expect(resource.x).to eq "blah"
- end
- end
- end
-
- context "default ordering when #{name} is nil" do
- with_property ":x, #{name}: nil, default: 10" do
- it "chooses default" do
- expect(resource.x).to eq 10
- end
- end
- with_property ":x, default: 10, #{name}: nil" do
- it "chooses default" do
- expect(resource.x).to eq 10
- end
- end
- end
-
- context "default ordering when #{name} is false" do
- with_property ":x, #{name}: false, default: 10" do
- it "chooses default" do
- expect(resource.x).to eq 10
- end
- end
- with_property ":x, default: 10, #{name}: nil" do
- it "chooses default" do
- expect(resource.x).to eq 10
- end
- end
- end
-
end
end
it "raises an error if both name_property and name_attribute are specified" do
expect { resource_class.property :x, :name_property => false, :name_attribute => 1 }.to raise_error ArgumentError,
- /Cannot specify both name_property and name_attribute together on property x of resource chef_resource_property_spec_(\d+)./
+ /name_attribute and name_property are functionally identical and both cannot be specified on a property at once. Use just one on property x of resource chef_resource_property_spec_(\d+)./
expect { resource_class.property :x, :name_property => false, :name_attribute => nil }.to raise_error ArgumentError,
- /Cannot specify both name_property and name_attribute together on property x of resource chef_resource_property_spec_(\d+)./
+ /name_attribute and name_property are functionally identical and both cannot be specified on a property at once. Use just one on property x of resource chef_resource_property_spec_(\d+)./
expect { resource_class.property :x, :name_property => false, :name_attribute => false }.to raise_error ArgumentError,
- /Cannot specify both name_property and name_attribute together on property x of resource chef_resource_property_spec_(\d+)./
+ /name_attribute and name_property are functionally identical and both cannot be specified on a property at once. Use just one on property x of resource chef_resource_property_spec_(\d+)./
expect { resource_class.property :x, :name_property => true, :name_attribute => true }.to raise_error ArgumentError,
- /Cannot specify both name_property and name_attribute together on property x of resource chef_resource_property_spec_(\d+)./
+ /name_attribute and name_property are functionally identical and both cannot be specified on a property at once. Use just one on property x of resource chef_resource_property_spec_(\d+)./
end
context "property_type" do
it "property_types validate their defaults" do
- expect {
+ expect do
module ::PropertySpecPropertyTypes
include Chef::Mixin::Properties
property_type(is: [:a, :b], default: :c)
end
- }.to raise_error(Chef::Exceptions::DeprecatedFeatureError, /Default value :c is invalid for property <property type>./)
- expect {
+ end.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect do
module ::PropertySpecPropertyTypes
include Chef::Mixin::Properties
property_type(is: [:a, :b], default: :b)
end
- }.not_to raise_error
+ end.not_to raise_error
end
context "With property_type ABType (is: [:a, :b]) and CDType (is: [:c, :d])" do
@@ -1177,6 +1156,31 @@ describe "Chef::Resource.property" do
end
+ context "with aliased properties" do
+ with_property ":real, Integer" do
+ it "should set the real property and emit a deprecation message" do
+ expect(Chef).to receive(:deprecated).with(:property, "we don't like the deprecated property no more")
+ resource_class.class_eval { deprecated_property_alias :deprecated, :real, "we don't like the deprecated property no more" }
+ resource.deprecated 10
+ expect(resource.real).to eq 10
+ end
+ end
+ end
+
+ context "redefining Object methods" do
+ it "disallows redefining Object methods" do
+ expect { resource_class.class_eval { property :hash } }.to raise_error(ArgumentError)
+ end
+
+ it "disallows redefining Chef::Resource methods" do
+ expect { resource_class.class_eval { property :action } }.to raise_error(ArgumentError)
+ end
+
+ it "allows redefining properties on Chef::Resource" do
+ expect { resource_class.class_eval { property :sensitive } }.not_to raise_error
+ end
+ end
+
context "with a custom property type" do
class CustomPropertyType < Chef::Property
end
diff --git a/spec/unit/provider/apt_preference_spec.rb b/spec/unit/provider/apt_preference_spec.rb
new file mode 100644
index 0000000000..e37dc16ff9
--- /dev/null
+++ b/spec/unit/provider/apt_preference_spec.rb
@@ -0,0 +1,87 @@
+#
+# Author:: Thom May (<thom@chef.io>)
+# Author:: Tim Smith (<tim@chef.io>)
+# Copyright:: 2016-2017, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Provider::AptPreference do
+ let(:new_resource) { Chef::Resource::AptPreference.new("libmysqlclient16.1*") }
+ let(:pref_dir) { Dir.mktmpdir("apt_pref_d") }
+
+ before do
+ stub_const("Chef::Provider::AptPreference::APT_PREFERENCE_DIR", pref_dir)
+ new_resource.pin = "1.0.1"
+ new_resource.pin_priority 1001
+ end
+
+ let(:provider) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ Chef::Provider::AptPreference.new(new_resource, run_context)
+ end
+
+ it "responds to load_current_resource" do
+ expect(provider).to respond_to(:load_current_resource)
+ end
+
+ context "#action_add" do
+ context "without a preferences.d directory" do
+ before do
+ FileUtils.rmdir pref_dir
+ end
+
+ it "creates the preferences.d directory" do
+ provider.run_action(:add)
+ expect(new_resource).to be_updated_by_last_action
+ expect(File.exist?(pref_dir)).to be true
+ expect(File.directory?(pref_dir)).to be true
+ end
+ end
+
+ context "with a preferences.d directory" do
+ before do
+ FileUtils.mkdir pref_dir unless ::File.exist?(pref_dir)
+ FileUtils.touch("#{pref_dir}/libmysqlclient16.1*.pref")
+ FileUtils.touch("#{pref_dir}/libmysqlclient16.1*")
+ end
+
+ # FileUtils.touch throws "Invalid argument @ utime_failed" in appveyer
+ it "creates a sanitized .pref file and removes the legacy cookbook files", :unix_only do
+ provider.run_action(:add)
+ expect(new_resource).to be_updated_by_last_action
+ expect(File).not_to exist("#{pref_dir}/libmysqlclient16.1*.pref")
+ expect(File).not_to exist("#{pref_dir}/libmysqlclient16.1*")
+ expect(File.read(::File.join(pref_dir, "libmysqlclient16_1wildcard.pref"))).to match(/Package: libmysqlclient16.1*.*Pin: 1.0.1.*Pin-Priority: 1001/m)
+ end
+ end
+ end
+
+ context "#action_delete" do
+ before do
+ FileUtils.mkdir pref_dir unless ::File.exist?(pref_dir)
+ FileUtils.touch("#{pref_dir}/libmysqlclient16_1wildcard.pref")
+ end
+
+ it "deletes the name santized .pref file" do
+ provider.run_action(:remove)
+ expect(new_resource).to be_updated_by_last_action
+ expect(File).not_to exist("#{pref_dir}/libmysqlclient16_1wildcard.pref")
+ end
+ end
+end
diff --git a/spec/unit/provider/apt_repository_spec.rb b/spec/unit/provider/apt_repository_spec.rb
index d8f2c85cb7..d881d01124 100644
--- a/spec/unit/provider/apt_repository_spec.rb
+++ b/spec/unit/provider/apt_repository_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: 2016-2017, Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,38 +18,35 @@
require "spec_helper"
+# Now we are using the option --with-colons that works across old os versions
+# as well as the latest (16.10). This for both `apt-key` and `gpg` commands
+#
+# Output of the command:
+# => apt-key adv --list-public-keys --with-fingerprint --with-colons
APT_KEY_FINGER = <<-EOF
-/etc/apt/trusted.gpg
---------------------
-pub 1024D/437D05B5 2004-09-12
- Key fingerprint = 6302 39CC 130E 1A7F D81A 27B1 4097 6EAF 437D 05B5
-uid Ubuntu Archive Automatic Signing Key <ftpmaster@ubuntu.com>
-sub 2048g/79164387 2004-09-12
-
-pub 1024D/FBB75451 2004-12-30
- Key fingerprint = C598 6B4F 1257 FFA8 6632 CBA7 4618 1433 FBB7 5451
-uid Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>
-
-pub 4096R/C0B21F32 2012-05-11
- Key fingerprint = 790B C727 7767 219C 42C8 6F93 3B4F E6AC C0B2 1F32
-uid Ubuntu Archive Automatic Signing Key (2012) <ftpmaster@ubuntu.com>
-
-pub 4096R/EFE21092 2012-05-11
- Key fingerprint = 8439 38DF 228D 22F7 B374 2BC0 D94A A3F0 EFE2 1092
-uid Ubuntu CD Image Automatic Signing Key (2012) <cdimage@ubuntu.com>
-
+tru:t:1:1488924856:0:3:1:5
+pub:-:1024:17:40976EAF437D05B5:2004-09-12:::-:Ubuntu Archive Automatic Signing Key <ftpmaster@ubuntu.com>::scESC:
+fpr:::::::::630239CC130E1A7FD81A27B140976EAF437D05B5:
+sub:-:2048:16:251BEFF479164387:2004-09-12::::::e:
+pub:-:1024:17:46181433FBB75451:2004-12-30:::-:Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>::scSC:
+fpr:::::::::C5986B4F1257FFA86632CBA746181433FBB75451:
+pub:-:4096:1:3B4FE6ACC0B21F32:2012-05-11:::-:Ubuntu Archive Automatic Signing Key (2012) <ftpmaster@ubuntu.com>::scSC:
+fpr:::::::::790BC7277767219C42C86F933B4FE6ACC0B21F32:
+pub:-:4096:1:D94AA3F0EFE21092:2012-05-11:::-:Ubuntu CD Image Automatic Signing Key (2012) <cdimage@ubuntu.com>::scSC:
+fpr:::::::::843938DF228D22F7B3742BC0D94AA3F0EFE21092:
EOF
+# Output of the command:
+# => gpg --with-fingerprint --with-colons [FILE]
GPG_FINGER = <<-EOF
-pub 1024D/02A818DD 2009-04-22 Cloudera Apt Repository
- Key fingerprint = F36A 89E3 3CC1 BD0F 7107 9007 3275 74EE 02A8 18DD
-sub 2048g/D1CA74A1 2009-04-22
+pub:-:1024:17:327574EE02A818DD:2009-04-22:::-:Cloudera Apt Repository:
+fpr:::::::::F36A89E33CC1BD0F71079007327574EE02A818DD:
+sub:-:2048:16:84080586D1CA74A1:2009-04-22::::
EOF
describe Chef::Provider::AptRepository do
let(:new_resource) { Chef::Resource::AptRepository.new("multiverse") }
- let(:shellout_env) { { env: { "LANG" => "en_US", "LANGUAGE" => "en_US" } } }
let(:provider) do
node = Chef::Node.new
events = Chef::EventDispatch::Dispatcher.new
@@ -57,16 +54,27 @@ describe Chef::Provider::AptRepository do
Chef::Provider::AptRepository.new(new_resource, run_context)
end
+ let(:apt_key_finger_cmd) do
+ "apt-key adv --list-public-keys --with-fingerprint --with-colons"
+ end
+
let(:apt_key_finger) do
- r = double("Mixlib::ShellOut", stdout: APT_KEY_FINGER, exitstatus: 0, live_stream: true)
- allow(r).to receive(:run_command)
- r
+ double("shell_out", stdout: APT_KEY_FINGER, exitstatus: 0, error?: false)
end
let(:gpg_finger) do
- r = double("Mixlib::ShellOut", stdout: GPG_FINGER, exitstatus: 0, live_stream: true)
- allow(r).to receive(:run_command)
- r
+ double("shell_out", stdout: GPG_FINGER, exitstatus: 0, error?: false)
+ end
+
+ let(:gpg_shell_out_success) do
+ double("shell_out", :stdout => "pub 2048R/7BD9BF62 2011-08-19 nginx signing key <signing-key@nginx.com>",
+ :exitstatus => 0, :error? => false)
+ end
+
+ let(:gpg_shell_out_failure) do
+ double("shell_out", :stderr => "gpg: no valid OpenPGP data found.\n
+ gpg: processing message failed: eof",
+ :exitstatus => 1, :error? => true)
end
let(:apt_fingerprints) do
@@ -81,58 +89,126 @@ C5986B4F1257FFA86632CBA746181433FBB75451
end
describe "#is_key_id?" do
- it "should detect a key" do
+ it "detects a key" do
expect(provider.is_key_id?("A4FF2279")).to be_truthy
end
- it "should detect a key with a hex signifier" do
+ it "detects a key with a hex signifier" do
expect(provider.is_key_id?("0xA4FF2279")).to be_truthy
end
- it "should reject a key with the wrong length" do
+ it "rejects a key with the wrong length" do
expect(provider.is_key_id?("4FF2279")).to be_falsey
end
- it "should reject a key with non-hex characters" do
+ it "rejects a key with non-hex characters" do
expect(provider.is_key_id?("A4KF2279")).to be_falsey
end
end
describe "#extract_fingerprints_from_cmd" do
- before do
- expect(Mixlib::ShellOut).to receive(:new).and_return(apt_key_finger)
+ it "runs the desired command" do
+ expect(provider).to receive(:shell_out).and_return(apt_key_finger)
+ provider.extract_fingerprints_from_cmd(apt_key_finger_cmd)
end
- it "should run the desired command" do
- expect(apt_key_finger).to receive(:run_command)
- provider.extract_fingerprints_from_cmd("apt-key finger")
+ it "returns a list of key fingerprints" do
+ expect(provider).to receive(:shell_out).and_return(apt_key_finger)
+ expect(provider.extract_fingerprints_from_cmd(apt_key_finger_cmd)).to eql(apt_fingerprints)
end
+ end
- it "should return a list of key fingerprints" do
- expect(provider.extract_fingerprints_from_cmd("apt-key finger")).to eql(apt_fingerprints)
+ describe "#cookbook_name" do
+ it "returns 'test' when the cookbook property is set" do
+ new_resource.cookbook("test")
+ expect(provider.cookbook_name).to eq("test")
end
end
describe "#no_new_keys?" do
before do
- allow(provider).to receive(:extract_fingerprints_from_cmd).with("apt-key finger").and_return(apt_fingerprints)
+ allow(provider).to receive(:extract_fingerprints_from_cmd).with(apt_key_finger_cmd).and_return(apt_fingerprints)
end
let(:file) { "/tmp/remote-gpg-keyfile" }
- it "should match a set of keys" do
- allow(provider).to receive(:extract_fingerprints_from_cmd).with("gpg --with-fingerprint #{file}").and_return(Array(apt_fingerprints.first))
+ it "matches a set of keys" do
+ allow(provider).to receive(:extract_fingerprints_from_cmd)
+ .with("gpg --with-fingerprint --with-colons #{file}")
+ .and_return(Array(apt_fingerprints.first))
expect(provider.no_new_keys?(file)).to be_truthy
end
- it "should notice missing keys" do
- allow(provider).to receive(:extract_fingerprints_from_cmd).with("gpg --with-fingerprint #{file}").and_return(%w{ F36A89E33CC1BD0F71079007327574EE02A818DD })
+ it "notices missing keys" do
+ allow(provider).to receive(:extract_fingerprints_from_cmd)
+ .with("gpg --with-fingerprint --with-colons #{file}")
+ .and_return(%w{ F36A89E33CC1BD0F71079007327574EE02A818DD })
expect(provider.no_new_keys?(file)).to be_falsey
end
end
+ describe "#key_type" do
+ it "returns :remote_file with an http URL" do
+ expect(provider.key_type("https://www.chef.io/key")).to eq(:remote_file)
+ end
+
+ it "returns :cookbook_file with a chef managed file" do
+ expect(provider).to receive(:has_cookbook_file?).and_return(true)
+ expect(provider.key_type("/foo/bar.key")).to eq(:cookbook_file)
+ end
+
+ it "throws exception if an unknown file specified" do
+ expect(provider).to receive(:has_cookbook_file?).and_return(false)
+ expect { provider.key_type("/foo/bar.key") }.to raise_error(Chef::Exceptions::FileNotFound)
+ end
+ end
+
+ describe "#keyserver_install_cmd" do
+ it "returns keyserver install command" do
+ expect(provider.keyserver_install_cmd("ABC", "gpg.mit.edu")).to eq("apt-key adv --recv --keyserver hkp://gpg.mit.edu:80 ABC")
+ end
+
+ it "uses proxy if key_proxy property is set" do
+ new_resource.key_proxy("proxy.mycorp.dmz:3128")
+ expect(provider.keyserver_install_cmd("ABC", "gpg.mit.edu")).to eq("apt-key adv --recv --keyserver-options http-proxy=proxy.mycorp.dmz:3128 --keyserver hkp://gpg.mit.edu:80 ABC")
+ end
+
+ it "properly handles keyservers passed with hkp:// URIs" do
+ expect(provider.keyserver_install_cmd("ABC", "hkp://gpg.mit.edu")).to eq("apt-key adv --recv --keyserver hkp://gpg.mit.edu ABC")
+ end
+ end
+
+ describe "#is_ppa_url" do
+ it "returns true if the URL starts with ppa:" do
+ expect(provider.is_ppa_url?("ppa://example.com")).to be_truthy
+ end
+
+ it "returns false if the URL does not start with ppa:" do
+ expect(provider.is_ppa_url?("example.com")).to be_falsey
+ end
+ end
+
+ describe "#repo_components" do
+ it "returns 'main' if a PPA and components property not set" do
+ expect(provider).to receive(:is_ppa_url?).and_return(true)
+ expect(provider.repo_components).to eq("main")
+ end
+
+ it "returns components property if a PPA and components is set" do
+ new_resource.components(["foo"])
+ expect(provider).to receive(:is_ppa_url?).and_return(true)
+ expect(provider.repo_components).to eq(["foo"])
+ end
+
+ it "returns components property if not a PPA" do
+ new_resource.components(["foo"])
+ expect(provider).to receive(:is_ppa_url?).and_return(false)
+ expect(provider.repo_components).to eq(["foo"])
+ end
+ end
+
describe "#install_ppa_key" do
let(:url) { "https://launchpad.net/api/1.0/~chef/+archive/main" }
let(:key) { "C5986B4F1257FFA86632CBA746181433FBB75451" }
- it "should get a key" do
+ it "gets a key" do
simples = double("HTTP")
allow(simples).to receive(:get).and_return("\"#{key}\"")
expect(Chef::HTTP::Simple).to receive(:new).with(url).and_return(simples)
@@ -142,42 +218,37 @@ C5986B4F1257FFA86632CBA746181433FBB75451
end
describe "#make_ppa_url" do
- it "should ignore non-ppa repositories" do
- expect(provider.make_ppa_url("some_string")).to be_nil
- end
-
- it "should create a URL" do
+ it "creates a URL" do
expect(provider).to receive(:install_ppa_key).with("chef", "main").and_return(true)
expect(provider.make_ppa_url("ppa:chef/main")).to eql("http://ppa.launchpad.net/chef/main/ubuntu")
end
end
describe "#build_repo" do
- it "should create a repository string" do
+ it "creates a repository string" do
target = %Q{deb "http://test/uri" unstable main\n}
expect(provider.build_repo("http://test/uri", "unstable", "main", false, nil)).to eql(target)
end
- it "should create a repository string with no distribution" do
+ it "creates a repository string with no distribution" do
target = %Q{deb "http://test/uri" main\n}
expect(provider.build_repo("http://test/uri", nil, "main", false, nil)).to eql(target)
end
- it "should create a repository string with source" do
+ it "creates a repository string with source" do
target = %Q{deb "http://test/uri" unstable main\ndeb-src "http://test/uri" unstable main\n}
expect(provider.build_repo("http://test/uri", "unstable", "main", false, nil, true)).to eql(target)
end
- it "should create a repository string with options" do
+ it "creates a repository string with options" do
target = %Q{deb [trusted=yes] "http://test/uri" unstable main\n}
expect(provider.build_repo("http://test/uri", "unstable", "main", true, nil)).to eql(target)
end
- it "should handle a ppa repo" do
+ it "handles a ppa repo" do
target = %Q{deb "http://ppa.launchpad.net/chef/main/ubuntu" unstable main\n}
expect(provider).to receive(:make_ppa_url).with("ppa:chef/main").and_return("http://ppa.launchpad.net/chef/main/ubuntu")
expect(provider.build_repo("ppa:chef/main", "unstable", "main", false, nil)).to eql(target)
end
end
-
end
diff --git a/spec/unit/provider/apt_update_spec.rb b/spec/unit/provider/apt_update_spec.rb
index 351a10051c..e1ab0085ce 100644
--- a/spec/unit/provider/apt_update_spec.rb
+++ b/spec/unit/provider/apt_update_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) 2016-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -45,7 +45,7 @@ describe Chef::Provider::AptUpdate do
before do
FileUtils.rmdir config_dir
expect(File.exist?(config_dir)).to be false
- allow_any_instance_of(Chef::Provider::Execute).to receive(:shell_out!).with("apt-get -q update", anything())
+ allow_any_instance_of(Chef::Provider::Execute).to receive(:shell_out_with_systems_locale!).with("apt-get -q update", anything())
end
it "should create the directory" do
@@ -64,7 +64,7 @@ describe Chef::Provider::AptUpdate do
describe "#action_update" do
it "should update the apt cache" do
provider.load_current_resource
- expect_any_instance_of(Chef::Provider::Execute).to receive(:shell_out!).with("apt-get -q update", anything())
+ expect_any_instance_of(Chef::Provider::Execute).to receive(:shell_out_with_systems_locale!).with("apt-get -q update", anything())
provider.run_action(:update)
expect(new_resource).to be_updated_by_last_action
end
@@ -79,14 +79,14 @@ describe Chef::Provider::AptUpdate do
it "should run if the time stamp is old" do
expect(File).to receive(:mtime).with("#{stamp_dir}/update-success-stamp").and_return(Time.now - 86_500)
- expect_any_instance_of(Chef::Provider::Execute).to receive(:shell_out!).with("apt-get -q update", anything())
+ expect_any_instance_of(Chef::Provider::Execute).to receive(:shell_out_with_systems_locale!).with("apt-get -q update", anything())
provider.run_action(:periodic)
expect(new_resource).to be_updated_by_last_action
end
it "should not run if the time stamp is new" do
expect(File).to receive(:mtime).with("#{stamp_dir}/update-success-stamp").and_return(Time.now)
- expect_any_instance_of(Chef::Provider::Execute).not_to receive(:shell_out!).with("apt-get -q update", anything())
+ expect_any_instance_of(Chef::Provider::Execute).not_to receive(:shell_out_with_systems_locale!).with("apt-get -q update", anything())
provider.run_action(:periodic)
expect(new_resource).to_not be_updated_by_last_action
end
@@ -98,14 +98,14 @@ describe Chef::Provider::AptUpdate do
it "should run if the time stamp is old" do
expect(File).to receive(:mtime).with("#{stamp_dir}/update-success-stamp").and_return(Time.now - 500)
- expect_any_instance_of(Chef::Provider::Execute).to receive(:shell_out!).with("apt-get -q update", anything())
+ expect_any_instance_of(Chef::Provider::Execute).to receive(:shell_out_with_systems_locale!).with("apt-get -q update", anything())
provider.run_action(:periodic)
expect(new_resource).to be_updated_by_last_action
end
it "should not run if the time stamp is new" do
expect(File).to receive(:mtime).with("#{stamp_dir}/update-success-stamp").and_return(Time.now - 300)
- expect_any_instance_of(Chef::Provider::Execute).not_to receive(:shell_out!).with("apt-get -q update", anything())
+ expect_any_instance_of(Chef::Provider::Execute).not_to receive(:shell_out_with_systems_locale!).with("apt-get -q update", anything())
provider.run_action(:periodic)
expect(new_resource).to_not be_updated_by_last_action
end
diff --git a/spec/unit/provider/breakpoint_spec.rb b/spec/unit/provider/breakpoint_spec.rb
deleted file mode 100644
index ffe8c8261f..0000000000
--- a/spec/unit/provider/breakpoint_spec.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-describe Chef::Provider::Breakpoint do
-
- before do
- @resource = Chef::Resource::Breakpoint.new
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @collection = double("resource collection")
- allow(@run_context).to receive(:resource_collection).and_return(@collection)
- @provider = Chef::Provider::Breakpoint.new(@resource, @run_context)
- end
-
- it "responds to load_current_resource" do
- expect(@provider).to respond_to(:load_current_resource)
- end
-
- it "gets the iterator from @collection and pauses it" do
- allow(Shell).to receive(:running?).and_return(true)
- @iterator = double("stepable_iterator")
- allow(@collection).to receive(:iterator).and_return(@iterator)
- expect(@iterator).to receive(:pause)
- @provider.action_break
- expect(@resource).to be_updated
- end
-
- it "doesn't pause the iterator if chef-shell isn't running" do
- allow(Shell).to receive(:running?).and_return(false)
- @iterator = double("stepable_iterator")
- allow(@collection).to receive(:iterator).and_return(@iterator)
- expect(@iterator).not_to receive(:pause)
- @provider.action_break
- end
-
-end
diff --git a/spec/unit/provider/cookbook_file_spec.rb b/spec/unit/provider/cookbook_file_spec.rb
index b375784d00..f27392de5d 100644
--- a/spec/unit/provider/cookbook_file_spec.rb
+++ b/spec/unit/provider/cookbook_file_spec.rb
@@ -25,13 +25,14 @@ require "support/shared/unit/provider/file"
describe Chef::Provider::CookbookFile do
let(:node) { double("Chef::Node") }
let(:events) { double("Chef::Events").as_null_object } # mock all the methods
- let(:run_context) { double("Chef::RunContext", :node => node, :events => events) }
- let(:enclosing_directory) {
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+ let(:run_context) { double("Chef::RunContext", :node => node, :events => events, :logger => logger) }
+ let(:enclosing_directory) do
canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
- }
- let(:resource_path) {
+ end
+ let(:resource_path) do
canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
- }
+ end
# Subject
diff --git a/spec/unit/provider/cron/unix_spec.rb b/spec/unit/provider/cron/unix_spec.rb
index 83e0f431bf..62b941c4aa 100644
--- a/spec/unit/provider/cron/unix_spec.rb
+++ b/spec/unit/provider/cron/unix_spec.rb
@@ -41,10 +41,15 @@ describe Chef::Provider::Cron::Unix do
let(:exitstatus) { 0 }
let(:shell_out) { double("Mixlib::ShellOut", :status => status, :stdout => stdout, :stderr => stderr) }
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+
it "is a Chef::Provider:Cron" do
expect(provider).to be_a(Chef::Provider::Cron)
end
+ before do
+ allow(run_context).to receive(:logger).and_return(logger)
+ end
describe "read_crontab" do
let(:stderr) { "" }
let(:stdout) do
@@ -59,7 +64,7 @@ describe Chef::Provider::Cron::Unix do
end
before do
- allow(Chef::Log).to receive(:debug)
+ allow(logger).to receive(:trace)
allow(shell_out).to receive(:format_for_exception).and_return("formatted command output")
allow(provider).to receive(:shell_out).with("/usr/bin/crontab -l", :user => username).and_return(shell_out)
end
@@ -83,7 +88,7 @@ describe Chef::Provider::Cron::Unix do
it "logs the crontab output to debug" do
provider.send(:read_crontab)
- expect(Chef::Log).to have_received(:debug).with("formatted command output")
+ expect(logger).to have_received(:trace).with("formatted command output")
end
end
@@ -91,14 +96,14 @@ describe Chef::Provider::Cron::Unix do
let (:exitstatus) { 2 }
it "should raise an exception if another error occurs" do
- expect {
+ expect do
provider.send(:read_crontab)
- }.to raise_error(Chef::Exceptions::Cron, "Error determining state of #{new_resource.name}, exit: 2")
+ end.to raise_error(Chef::Exceptions::Cron, "Error determining state of #{new_resource.name}, exit: 2")
end
it "logs the crontab output to debug" do
provider.send(:read_crontab) rescue nil
- expect(Chef::Log).to have_received(:debug).with("formatted command output")
+ expect(logger).to have_received(:trace).with("formatted command output")
end
end
end
@@ -130,9 +135,9 @@ describe Chef::Provider::Cron::Unix do
context "when writing the crontab fails" do
let(:exitstatus) { 1 }
it "should raise an exception if the command returns non-zero" do
- expect {
+ expect do
provider.send(:write_crontab, "Foo")
- }.to raise_error(Chef::Exceptions::Cron, /Error updating state of #{new_resource.name}, exit: 1/)
+ end.to raise_error(Chef::Exceptions::Cron, /Error updating state of #{new_resource.name}, exit: 1/)
end
end
end
diff --git a/spec/unit/provider/cron_spec.rb b/spec/unit/provider/cron_spec.rb
index 010b1b09f3..5673ecc317 100644
--- a/spec/unit/provider/cron_spec.rb
+++ b/spec/unit/provider/cron_spec.rb
@@ -19,16 +19,23 @@
require "spec_helper"
describe Chef::Provider::Cron do
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+
+ before do
+ @node = Chef::Node.new
+ @events = Chef::EventDispatch::Dispatcher.new
+ @run_context = Chef::RunContext.new(@node, {}, @events)
+ allow(@run_context).to receive(:logger).and_return(logger)
+
+ @new_resource = Chef::Resource::Cron.new("cronhole some stuff", @run_context)
+ @new_resource.user "root"
+ @new_resource.minute "30"
+ @new_resource.command "/bin/true"
+ @provider = Chef::Provider::Cron.new(@new_resource, @run_context)
+ end
+
describe "when with special time string" do
before do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
-
- @new_resource = Chef::Resource::Cron.new("cronhole some stuff", @run_context)
- @new_resource.user "root"
- @new_resource.minute "30"
- @new_resource.command "/bin/true"
@new_resource.time :reboot
@provider = Chef::Provider::Cron.new(@new_resource, @run_context)
end
@@ -113,7 +120,7 @@ CRONTAB
end
it "should report the match" do
- expect(Chef::Log).to receive(:debug).with("Found cron '#{@new_resource.name}'")
+ expect(logger).to receive(:trace).with("Found cron '#{@new_resource.name}'")
@provider.load_current_resource
end
@@ -141,18 +148,6 @@ CRONTAB
end
end
- before do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
-
- @new_resource = Chef::Resource::Cron.new("cronhole some stuff", @run_context)
- @new_resource.user "root"
- @new_resource.minute "30"
- @new_resource.command "/bin/true"
- @provider = Chef::Provider::Cron.new(@new_resource, @run_context)
- end
-
describe "when examining the current system state" do
context "with no crontab for the user" do
before :each do
@@ -166,7 +161,7 @@ CRONTAB
end
it "should report an empty crontab" do
- expect(Chef::Log).to receive(:debug).with("Cron empty for '#{@new_resource.user}'")
+ expect(logger).to receive(:trace).with("Cron empty for '#{@new_resource.user}'")
@provider.load_current_resource
end
end
@@ -190,7 +185,7 @@ CRONTAB
end
it "should report no entry found" do
- expect(Chef::Log).to receive(:debug).with("Cron '#{@new_resource.name}' not found")
+ expect(logger).to receive(:trace).with("Cron '#{@new_resource.name}' not found")
@provider.load_current_resource
end
@@ -199,9 +194,9 @@ CRONTAB
# Chef Name: foo[bar] (baz)
21 */4 * * * some_prog 1234567
CRONTAB
- expect {
+ expect do
@provider.load_current_resource
- }.not_to raise_error
+ end.not_to raise_error
end
end
@@ -296,7 +291,7 @@ CRONTAB
end
it "should report the match" do
- expect(Chef::Log).to receive(:debug).with("Found cron '#{@new_resource.name}'")
+ expect(logger).to receive(:trace).with("Found cron '#{@new_resource.name}'")
@provider.load_current_resource
end
end
@@ -332,7 +327,7 @@ CRONTAB
end
it "should report the match" do
- expect(Chef::Log).to receive(:debug).with("Found cron '#{@new_resource.name}'")
+ expect(logger).to receive(:trace).with("Found cron '#{@new_resource.name}'")
@provider.load_current_resource
end
end
@@ -462,10 +457,10 @@ CRONTAB
@new_resource.environment "TEST" => "LOL"
expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
# Chef Name: cronhole some stuff
-MAILTO=foo@example.com
-PATH=/usr/bin:/my/custom/path
-SHELL=/bin/foosh
-HOME=/home/foo
+MAILTO="foo@example.com"
+PATH="/usr/bin:/my/custom/path"
+SHELL="/bin/foosh"
+HOME="/home/foo"
TEST=LOL
30 * * * * /bin/true
ENDCRON
@@ -478,7 +473,7 @@ TEST=LOL
end
it "should log the action" do
- expect(Chef::Log).to receive(:info).with("cron[cronhole some stuff] added crontab entry")
+ expect(logger).to receive(:info).with("cron[cronhole some stuff] added crontab entry")
@provider.run_action(:create)
end
end
@@ -524,10 +519,10 @@ TEST=LOL
# Another comment
# Chef Name: cronhole some stuff
-MAILTO=foo@example.com
-PATH=/usr/bin:/my/custom/path
-SHELL=/bin/foosh
-HOME=/home/foo
+MAILTO="foo@example.com"
+PATH="/usr/bin:/my/custom/path"
+SHELL="/bin/foosh"
+HOME="/home/foo"
TEST=LOL
30 * * * * /bin/true
ENDCRON
@@ -540,7 +535,7 @@ TEST=LOL
end
it "should log the action" do
- expect(Chef::Log).to receive(:info).with("cron[cronhole some stuff] added crontab entry")
+ expect(logger).to receive(:info).with("cron[cronhole some stuff] added crontab entry")
@provider.run_action(:create)
end
end
@@ -585,10 +580,10 @@ TEST=LOL
0 2 * * * /some/other/command
# Chef Name: cronhole some stuff
-MAILTO=foo@example.com
-PATH=/usr/bin:/my/custom/path
-SHELL=/bin/foosh
-HOME=/home/foo
+MAILTO="foo@example.com"
+PATH="/usr/bin:/my/custom/path"
+SHELL="/bin/foosh"
+HOME="/home/foo"
TEST=LOL
30 * * * * /bin/true
# Chef Name: something else
@@ -605,7 +600,7 @@ TEST=LOL
end
it "should log the action" do
- expect(Chef::Log).to receive(:info).with("cron[cronhole some stuff] updated crontab entry")
+ expect(logger).to receive(:info).with("cron[cronhole some stuff] updated crontab entry")
@provider.run_action(:create)
end
end
@@ -679,10 +674,10 @@ HOME=/home/foo
0 2 * * * /some/other/command
# Chef Name: cronhole some stuff
-MAILTO=foo@example.com
-PATH=/usr/bin:/my/custom/path
-SHELL=/bin/foosh
-HOME=/home/foo
+MAILTO="foo@example.com"
+PATH="/usr/bin:/my/custom/path"
+SHELL="/bin/foosh"
+HOME="/home/foo"
30 * * * * /bin/true
# Chef Name: something else
@@ -719,8 +714,8 @@ CRONTAB
end
it "should log nothing changed" do
- expect(Chef::Log).to receive(:debug).with("Found cron '#{@new_resource.name}'")
- expect(Chef::Log).to receive(:debug).with("Skipping existing cron entry '#{@new_resource.name}'")
+ expect(logger).to receive(:trace).with("Found cron '#{@new_resource.name}'")
+ expect(logger).to receive(:trace).with("Skipping existing cron entry '#{@new_resource.name}'")
@provider.run_action(:create)
end
end
@@ -739,7 +734,7 @@ CRONTAB
it "should do nothing" do
expect(@provider).not_to receive(:write_crontab)
- expect(Chef::Log).not_to receive(:info)
+ expect(logger).not_to receive(:info)
@provider.run_action(:delete)
end
@@ -806,7 +801,7 @@ FOO=test
end
it "should log the action" do
- expect(Chef::Log).to receive(:info).with("#{@new_resource} deleted crontab entry")
+ expect(logger).to receive(:info).with("#{@new_resource} deleted crontab entry")
@provider.run_action(:delete)
end
end
@@ -880,8 +875,7 @@ MAILTO=foo@example.com
describe "read_crontab" do
before :each do
- @status = double("Status", :exitstatus => 0)
- @stdout = StringIO.new(<<-CRONTAB)
+ @stdout = <<-CRONTAB
0 2 * * * /some/other/command
# Chef Name: something else
@@ -889,11 +883,12 @@ MAILTO=foo@example.com
# Another comment
CRONTAB
- allow(@provider).to receive(:popen4).and_yield(1234, StringIO.new, @stdout, StringIO.new).and_return(@status)
+ @status = double("Status", exitstatus: 0, stdout: @stdout)
+ allow(@provider).to receive(:shell_out!).and_return(@status)
end
it "should call crontab -l with the user" do
- expect(@provider).to receive(:popen4).with("crontab -l -u #{@new_resource.user}").and_return(@status)
+ expect(@provider).to receive(:shell_out!).with("crontab -l -u #{@new_resource.user}", returns: [0, 1]).and_return(@status)
@provider.send(:read_crontab)
end
@@ -910,60 +905,36 @@ MAILTO=foo@example.com
end
it "should return nil if the user has no crontab" do
- status = double("Status", :exitstatus => 1)
- allow(@provider).to receive(:popen4).and_return(status)
+ @status = double("Status", exitstatus: 1, stdout: "")
+ allow(@provider).to receive(:shell_out!).and_return(@status)
expect(@provider.send(:read_crontab)).to eq(nil)
end
it "should raise an exception if another error occurs" do
- status = double("Status", :exitstatus => 2)
- allow(@provider).to receive(:popen4).and_return(status)
- expect do
- @provider.send(:read_crontab)
- end.to raise_error(Chef::Exceptions::Cron, "Error determining state of #{@new_resource.name}, exit: 2")
+ @status = double("Status", exitstatus: 2)
+ allow(@provider).to receive(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed)
+ expect { @provider.send(:read_crontab) }.to raise_error(Chef::Exceptions::Cron)
end
end
describe "write_crontab" do
before :each do
- @status = double("Status", :exitstatus => 0)
- @stdin = StringIO.new
- allow(@provider).to receive(:popen4).and_yield(1234, @stdin, StringIO.new, StringIO.new).and_return(@status)
+ @status = double("Status", exitstatus: 0)
+ allow(@provider).to receive(:shell_out!).and_return(@status)
end
it "should call crontab for the user" do
- expect(@provider).to receive(:popen4).with("crontab -u #{@new_resource.user} -", :waitlast => true).and_return(@status)
+ expect(@provider).to receive(:shell_out!).with("crontab -u #{@new_resource.user} -", input: "Foo").and_return(@status)
@provider.send(:write_crontab, "Foo")
end
- it "should write the given string to the crontab command" do
- @provider.send(:write_crontab, "Foo\n# wibble\n wah!!")
- expect(@stdin.string).to eq("Foo\n# wibble\n wah!!")
- end
-
it "should raise an exception if the command returns non-zero" do
- allow(@status).to receive(:exitstatus).and_return(1)
+ @status = double("Status", exitstatus: 1)
+ allow(@provider).to receive(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed)
expect do
@provider.send(:write_crontab, "Foo")
- end.to raise_error(Chef::Exceptions::Cron, "Error updating state of #{@new_resource.name}, exit: 1")
+ end.to raise_error(Chef::Exceptions::Cron)
end
-
- it "should raise an exception if the command die's and parent tries to write" do
- class WriteErrPipe
- def write(str)
- raise Errno::EPIPE, "Test"
- end
- end
- allow(@status).to receive(:exitstatus).and_return(1)
- allow(@provider).to receive(:popen4).and_yield(1234, WriteErrPipe.new, StringIO.new, StringIO.new).and_return(@status)
-
- expect(Chef::Log).to receive(:debug).with("Broken pipe - Test")
-
- expect do
- @provider.send(:write_crontab, "Foo")
- end.to raise_error(Chef::Exceptions::Cron, "Error updating state of #{@new_resource.name}, exit: 1")
- end
-
end
describe "weekday_in_crontab" do
diff --git a/spec/unit/provider/deploy/revision_spec.rb b/spec/unit/provider/deploy/revision_spec.rb
deleted file mode 100644
index 8f8280e11d..0000000000
--- a/spec/unit/provider/deploy/revision_spec.rb
+++ /dev/null
@@ -1,110 +0,0 @@
-#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-
-describe Chef::Provider::Deploy::Revision do
-
- before do
- allow(ChefConfig).to receive(:windows?) { false }
- @temp_dir = Dir.mktmpdir
- Chef::Config[:file_cache_path] = @temp_dir
- @resource = Chef::Resource::Deploy.new("/my/deploy/dir")
- @resource.revision("8a3195bf3efa246f743c5dfa83683201880f935c")
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @provider = Chef::Provider::Deploy::Revision.new(@resource, @run_context)
- @provider.load_current_resource
- @runner = double("runnah")
- allow(Chef::Runner).to receive(:new).and_return(@runner)
- @expected_release_dir = "/my/deploy/dir/releases/8a3195bf3efa246f743c5dfa83683201880f935c"
- end
-
- after do
- # Make sure we don't keep any state in our tests
- FileUtils.rm_rf @temp_dir if File.directory?( @temp_dir )
- end
-
- it "uses the resolved revision from the SCM as the release slug" do
- allow(@provider.scm_provider).to receive(:revision_slug).and_return("uglySlugly")
- expect(@provider.send(:release_slug)).to eq("uglySlugly")
- end
-
- it "deploys to a dir named after the revision" do
- expect(@provider.release_path).to eq(@expected_release_dir)
- end
-
- it "stores the release dir in the file cache in the cleanup step" do
- allow(FileUtils).to receive(:mkdir_p)
- allow(FileUtils).to receive(:cp_r)
- @provider.cleanup!
- allow(@provider).to receive(:release_slug).and_return("73219b87e977d9c7ba1aa57e9ad1d88fa91a0ec2")
- @provider.load_current_resource
- @provider.cleanup!
- second_release = "/my/deploy/dir/releases/73219b87e977d9c7ba1aa57e9ad1d88fa91a0ec2"
-
- expect(@provider.all_releases).to eq([@expected_release_dir, second_release])
- end
-
- it "removes a release from the file cache when it's used again in another release and append it to the end" do
- allow(FileUtils).to receive(:mkdir_p)
- allow(FileUtils).to receive(:cp_r)
- @provider.cleanup!
- allow(@provider).to receive(:release_slug).and_return("73219b87e977d9c7ba1aa57e9ad1d88fa91a0ec2")
- @provider.load_current_resource
- @provider.cleanup!
- second_release = "/my/deploy/dir/releases/73219b87e977d9c7ba1aa57e9ad1d88fa91a0ec2"
- expect(@provider.all_releases).to eq([@expected_release_dir, second_release])
- @provider.cleanup!
-
- allow(@provider).to receive(:release_slug).and_return("8a3195bf3efa246f743c5dfa83683201880f935c")
- @provider.load_current_resource
- @provider.cleanup!
- expect(@provider.all_releases).to eq([second_release, @expected_release_dir])
- end
-
- it "removes a release from the file cache when it's deleted by :cleanup!" do
- release_paths = %w{first second third fourth fifth}.map do |release_name|
- "/my/deploy/dir/releases/#{release_name}"
- end
- release_paths.each do |release_path|
- @provider.send(:release_created, release_path)
- end
- expect(@provider.all_releases).to eq(release_paths)
-
- allow(FileUtils).to receive(:rm_rf)
- @provider.cleanup!
-
- expected_release_paths = (%w{second third fourth fifth} << @resource.revision).map do |release_name|
- "/my/deploy/dir/releases/#{release_name}"
- end
-
- expect(@provider.all_releases).to eq(expected_release_paths)
- end
-
- it "regenerates the file cache if it's not available" do
- oldest = "/my/deploy/dir/releases/oldest"
- latest = "/my/deploy/dir/releases/latest"
- expect(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return([latest, oldest])
- expect(::File).to receive(:ctime).with(oldest).and_return(Time.now - 10)
- expect(::File).to receive(:ctime).with(latest).and_return(Time.now - 1)
- expect(@provider.all_releases).to eq([oldest, latest])
- end
-
-end
diff --git a/spec/unit/provider/deploy/timestamped_spec.rb b/spec/unit/provider/deploy/timestamped_spec.rb
deleted file mode 100644
index fdb90bf438..0000000000
--- a/spec/unit/provider/deploy/timestamped_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-
-describe Chef::Provider::Deploy::Timestamped do
-
- before do
- @release_time = Time.utc( 2004, 8, 15, 16, 23, 42)
- allow(Time).to receive(:now).and_return(@release_time)
- @expected_release_dir = "/my/deploy/dir/releases/20040815162342"
- @resource = Chef::Resource::Deploy.new("/my/deploy/dir")
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @timestamped_deploy = Chef::Provider::Deploy::Timestamped.new(@resource, @run_context)
- @runner = double("runnah")
- allow(Chef::Runner).to receive(:new).and_return(@runner)
- end
-
- it "gives a timestamp for release_slug" do
- expect(@timestamped_deploy.send(:release_slug)).to eq("20040815162342")
- end
-
-end
diff --git a/spec/unit/provider/deploy_spec.rb b/spec/unit/provider/deploy_spec.rb
deleted file mode 100644
index 62d9123e7b..0000000000
--- a/spec/unit/provider/deploy_spec.rb
+++ /dev/null
@@ -1,641 +0,0 @@
-#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-
-describe Chef::Provider::Deploy do
-
- before do
- allow(ChefConfig).to receive(:windows?) { false }
- @release_time = Time.utc( 2004, 8, 15, 16, 23, 42)
- allow(Time).to receive(:now).and_return(@release_time)
- @expected_release_dir = "/my/deploy/dir/releases/20040815162342"
- @resource = Chef::Resource::Deploy.new("/my/deploy/dir")
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @provider = Chef::Provider::Deploy.new(@resource, @run_context)
- allow(@provider).to receive(:release_slug)
- allow(@provider).to receive(:release_path).and_return(@expected_release_dir)
- end
-
- it "loads scm resource" do
- expect(@provider.scm_provider).to receive(:load_current_resource)
- @provider.load_current_resource
- end
-
- it "supports :deploy and :rollback actions" do
- expect(@provider).to respond_to(:action_deploy)
- expect(@provider).to respond_to(:action_rollback)
- end
-
- context "when the deploy resource has a timeout attribute" do
- let(:ten_seconds) { 10 }
- before { @resource.timeout(ten_seconds) }
- it "relays the timeout to the scm resource" do
- expect(@provider.scm_provider.new_resource.timeout).to eq(ten_seconds)
- end
- end
-
- context "when the deploy resource has no timeout attribute" do
- it "should not set a timeout on the scm resource" do
- expect(@provider.scm_provider.new_resource.timeout).to be_nil
- end
- end
-
- context "when the deploy_to dir does not exist yet" do
- before do
- expect(FileUtils).to receive(:mkdir_p).with(@resource.deploy_to).ordered
- expect(FileUtils).to receive(:mkdir_p).with(@resource.shared_path).ordered
- allow(::File).to receive(:directory?).and_return(false)
- allow(@provider).to receive(:symlink)
- allow(@provider).to receive(:migrate)
- allow(@provider).to receive(:copy_cached_repo)
- end
-
- it "creates deploy_to dir" do
- expect(::Dir).to receive(:chdir).with(@expected_release_dir).exactly(4).times
- expect(@provider).to receive(:enforce_ownership).twice
- allow(@provider).to receive(:update_cached_repo)
- @provider.deploy
- end
-
- end
-
- it "does not create deploy_to dir if it exists" do
- allow(::File).to receive(:directory?).and_return(true)
- expect(::Dir).to receive(:chdir).with(@expected_release_dir).exactly(4).times
- expect(FileUtils).not_to receive(:mkdir_p).with(@resource.deploy_to)
- expect(FileUtils).not_to receive(:mkdir_p).with(@resource.shared_path)
- expect(@provider).to receive(:enforce_ownership).twice
- allow(@provider).to receive(:copy_cached_repo)
- allow(@provider).to receive(:update_cached_repo)
- allow(@provider).to receive(:symlink)
- allow(@provider).to receive(:migrate)
- @provider.deploy
- end
-
- it "ensures the deploy_to dir ownership after the verfication that it exists" do
- expect(::Dir).to receive(:chdir).with(@expected_release_dir).exactly(4).times
- expect(@provider).to receive(:verify_directories_exist).ordered
- expect(@provider).to receive(:enforce_ownership).ordered
- allow(@provider).to receive(:copy_cached_repo)
- allow(@provider).to receive(:update_cached_repo)
- allow(@provider).to receive(:install_gems)
- expect(@provider).to receive(:enforce_ownership).ordered
- allow(@provider).to receive(:enforce_ownership)
- allow(@provider).to receive(:symlink)
- allow(@provider).to receive(:migrate)
- @provider.deploy
- end
-
- it "updates and copies the repo, then does a migrate, symlink, restart, restart, cleanup on deploy" do
- allow(FileUtils).to receive(:mkdir_p).with("/my/deploy/dir")
- allow(FileUtils).to receive(:mkdir_p).with("/my/deploy/dir/shared")
- expect(@provider).to receive(:enforce_ownership).twice
- expect(@provider).to receive(:update_cached_repo)
- expect(@provider).to receive(:copy_cached_repo)
- expect(@provider).to receive(:install_gems)
- expect(@provider).to receive(:callback).with(:before_migrate, nil)
- expect(@provider).to receive(:migrate)
- expect(@provider).to receive(:callback).with(:before_symlink, nil)
- expect(@provider).to receive(:symlink)
- expect(@provider).to receive(:callback).with(:before_restart, nil)
- expect(@provider).to receive(:restart)
- expect(@provider).to receive(:callback).with(:after_restart, nil)
- expect(@provider).to receive(:cleanup!)
- @provider.deploy
- end
-
- it "should not deploy if there is already a deploy at release_path, and it is the current release" do
- allow(@provider).to receive(:all_releases).and_return([@expected_release_dir])
- allow(@provider).to receive(:current_release?).with(@expected_release_dir).and_return(true)
- expect(@provider).not_to receive(:deploy)
- @provider.run_action(:deploy)
- end
-
- it "should call action_rollback if there is already a deploy of this revision at release_path, and it is not the current release" do
- allow(@provider).to receive(:all_releases).and_return([@expected_release_dir, "102021"])
- allow(@provider).to receive(:current_release?).with(@expected_release_dir).and_return(false)
- expect(@provider).to receive(:rollback_to).with(@expected_release_dir)
- expect(@provider).to receive(:current_release?)
- @provider.run_action(:deploy)
- end
-
- it "calls deploy when deploying a new release" do
- allow(@provider).to receive(:all_releases).and_return([])
- expect(@provider).to receive(:deploy)
- @provider.run_action(:deploy)
- end
-
- it "runs action svn_force_export when new_resource.svn_force_export is true" do
- @resource.svn_force_export true
- expect(@provider.scm_provider).to receive(:run_action).with(:force_export)
- @provider.update_cached_repo
- end
-
- it "Removes the old release before deploying when force deploying over it" do
- allow(@provider).to receive(:all_releases).and_return([@expected_release_dir])
- expect(FileUtils).to receive(:rm_rf).with(@expected_release_dir)
- expect(@provider).to receive(:deploy)
- @provider.run_action(:force_deploy)
- end
-
- it "deploys as normal when force deploying and there's no prior release at the same path" do
- allow(@provider).to receive(:all_releases).and_return([])
- expect(@provider).to receive(:deploy)
- @provider.run_action(:force_deploy)
- end
-
- it "dont care by default if error happens on deploy" do
- allow(@provider).to receive(:all_releases).and_return(["previous_release"])
- allow(@provider).to receive(:deploy) { raise "Unexpected error" }
- allow(@provider).to receive(:previous_release_path).and_return("previous_release")
- expect(@provider).not_to receive(:rollback)
- expect {
- @provider.run_action(:deploy)
- }.to raise_exception(RuntimeError, "Unexpected error")
- end
-
- it "rollbacks to previous release if error happens on deploy" do
- @resource.rollback_on_error true
- allow(@provider).to receive(:all_releases).and_return(["previous_release"])
- allow(@provider).to receive(:deploy) { raise "Unexpected error" }
- allow(@provider).to receive(:previous_release_path).and_return("previous_release")
- expect(@provider).to receive(:rollback)
- expect {
- @provider.run_action(:deploy)
- }.to raise_exception(RuntimeError, "Unexpected error")
- end
-
- describe "on systems without broken Dir.glob results" do
- it "sets the release path to the penultimate release when one is not specified, symlinks, and rm's the last release on rollback" do
- allow(@provider).to receive(:release_path).and_return("/my/deploy/dir/releases/3")
- all_releases = ["/my/deploy/dir/releases/1", "/my/deploy/dir/releases/2", "/my/deploy/dir/releases/3", "/my/deploy/dir/releases/4", "/my/deploy/dir/releases/5"]
- allow(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases)
- expect(@provider).to receive(:symlink)
- expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/releases/4")
- expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/releases/5")
- @provider.run_action(:rollback)
- expect(@provider.release_path).to eql("/my/deploy/dir/releases/3")
- expect(@provider.shared_path).to eql("/my/deploy/dir/shared")
- end
-
- it "sets the release path to the specified release, symlinks, and rm's any newer releases on rollback" do
- allow(@provider).to receive(:release_path).and_call_original
- all_releases = ["/my/deploy/dir/releases/20040815162342", "/my/deploy/dir/releases/20040700000000",
- "/my/deploy/dir/releases/20040600000000", "/my/deploy/dir/releases/20040500000000"].sort!
- allow(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases)
- expect(@provider).to receive(:symlink)
- expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/releases/20040815162342")
- @provider.run_action(:rollback)
- expect(@provider.release_path).to eql("/my/deploy/dir/releases/20040700000000")
- expect(@provider.shared_path).to eql("/my/deploy/dir/shared")
- end
-
- it "sets the release path to the penultimate release, symlinks, and rm's the last release on rollback" do
- allow(@provider).to receive(:release_path).and_call_original
- all_releases = [ "/my/deploy/dir/releases/20040815162342",
- "/my/deploy/dir/releases/20040700000000",
- "/my/deploy/dir/releases/20040600000000",
- "/my/deploy/dir/releases/20040500000000"]
- allow(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases)
- expect(@provider).to receive(:symlink)
- expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/releases/20040815162342")
- @provider.run_action(:rollback)
- expect(@provider.release_path).to eql("/my/deploy/dir/releases/20040700000000")
- expect(@provider.shared_path).to eql("/my/deploy/dir/shared")
- end
-
- describe "if there are no releases to fallback to" do
-
- it "an exception is raised when there is only 1 release" do
- #@provider.unstub(:release_path) -- unstub the release path on top to feed our own release path
- all_releases = [ "/my/deploy/dir/releases/20040815162342"]
- allow(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases)
- #@provider.should_receive(:symlink)
- #FileUtils.should_receive(:rm_rf).with("/my/deploy/dir/releases/20040815162342")
- #@provider.run_action(:rollback)
- #@provider.release_path.should eql(NIL) -- no check needed since assertions will fail
- expect {
- @provider.run_action(:rollback)
- }.to raise_exception(RuntimeError, "There is no release to rollback to!")
- end
-
- it "an exception is raised when there are no releases" do
- all_releases = []
- allow(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases)
- expect {
- @provider.run_action(:rollback)
- }.to raise_exception(RuntimeError, "There is no release to rollback to!")
- end
- end
- end
-
- describe "CHEF-628: on systems with broken Dir.glob results" do
- it "sets the release path to the penultimate release, symlinks, and rm's the last release on rollback" do
- allow(@provider).to receive(:release_path).and_call_original
- all_releases = [ "/my/deploy/dir/releases/20040500000000",
- "/my/deploy/dir/releases/20040600000000",
- "/my/deploy/dir/releases/20040700000000",
- "/my/deploy/dir/releases/20040815162342" ]
- allow(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases)
- expect(@provider).to receive(:symlink)
- expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/releases/20040815162342")
- @provider.run_action(:rollback)
- expect(@provider.release_path).to eql("/my/deploy/dir/releases/20040700000000")
- expect(@provider.shared_path).to eql("/my/deploy/dir/shared")
- end
- end
-
- it "raises a runtime error when there's no release to rollback to" do
- all_releases = []
- allow(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases)
- expect { @provider.run_action(:rollback) }.to raise_error(RuntimeError)
- end
-
- it "runs the new resource collection in the runner during a callback" do
- @runner = double("Runner")
- allow(Chef::Runner).to receive(:new).and_return(@runner)
- expect(@runner).to receive(:converge)
- callback_code = Proc.new { :noop }
- @provider.callback(:whatevs, callback_code)
- end
-
- it "loads callback files from the release/ dir if the file exists" do
- foo_callback = @expected_release_dir + "/deploy/foo.rb"
- expect(::File).to receive(:exist?).with(foo_callback).once.and_return(true)
- expect(::Dir).to receive(:chdir).with(@expected_release_dir).and_yield
- expect(@provider).to receive(:from_file).with(foo_callback)
- @provider.callback(:foo, "deploy/foo.rb")
- end
-
- it "raises a runtime error if a callback file is explicitly specified but does not exist" do
- baz_callback = "/deploy/baz.rb"
- expect(::File).to receive(:exist?).with("#{@expected_release_dir}/#{baz_callback}").and_return(false)
- @resource.before_migrate baz_callback
- @provider.define_resource_requirements
- @provider.action = :deploy
- expect { @provider.process_resource_requirements }.to raise_error(RuntimeError)
- end
-
- it "runs a default callback if the callback code is nil" do
- bar_callback = @expected_release_dir + "/deploy/bar.rb"
- expect(::File).to receive(:exist?).with(bar_callback).and_return(true)
- expect(::Dir).to receive(:chdir).with(@expected_release_dir).and_yield
- expect(@provider).to receive(:from_file).with(bar_callback)
- @provider.callback(:bar, nil)
- end
-
- it "skips an eval callback if the file doesn't exist" do
- barbaz_callback = @expected_release_dir + "/deploy/barbaz.rb"
- expect(::File).to receive(:exist?).with(barbaz_callback).and_return(false)
- expect(::Dir).to receive(:chdir).with(@expected_release_dir).and_yield
- expect(@provider).not_to receive(:from_file)
- @provider.callback(:barbaz, nil)
- end
-
- # CHEF-3449 #converge_by is called in #recipe_eval and must happen in sequence
- # with the other calls to #converge_by to keep the train on the tracks
- it "evaluates a callback file before the corresponding step" do
- expect(@provider).to receive(:verify_directories_exist)
- expect(@provider).to receive(:update_cached_repo)
- expect(@provider).to receive(:enforce_ownership)
- expect(@provider).to receive(:copy_cached_repo)
- expect(@provider).to receive(:install_gems)
- expect(@provider).to receive(:enforce_ownership)
- expect(@provider).to receive(:converge_by).ordered # before_migrate
- expect(@provider).to receive(:migrate).ordered
- expect(@provider).to receive(:converge_by).ordered # before_symlink
- expect(@provider).to receive(:symlink).ordered
- expect(@provider).to receive(:converge_by).ordered # before_restart
- expect(@provider).to receive(:restart).ordered
- expect(@provider).to receive(:converge_by).ordered # after_restart
- expect(@provider).to receive(:cleanup!)
- @provider.deploy
- end
-
- it "gets a SCM provider as specified by its resource" do
- expect(@provider.scm_provider).to be_an_instance_of(Chef::Provider::Git)
- expect(@provider.scm_provider.new_resource.destination).to eql("/my/deploy/dir/shared/cached-copy")
- end
-
- it "syncs the cached copy of the repo" do
- expect(@provider.scm_provider).to receive(:run_action).with(:sync)
- @provider.update_cached_repo
- end
-
- it "makes a copy of the cached repo in releases dir" do
- expect(FileUtils).to receive(:mkdir_p).with("/my/deploy/dir/releases")
- expect(FileUtils).to receive(:cp_r).with("/my/deploy/dir/shared/cached-copy/.", @expected_release_dir, :preserve => true)
- @provider.copy_cached_repo
- end
-
- it "calls the internal callback :release_created when cleaning up the releases" do
- allow(FileUtils).to receive(:mkdir_p)
- allow(FileUtils).to receive(:cp_r)
- expect(@provider).to receive(:release_created)
- @provider.cleanup!
- end
-
- it "chowns the whole release dir to user and group specified in the resource" do
- @resource.user "foo"
- @resource.group "bar"
- expect(FileUtils).to receive(:chown_R).with("foo", "bar", "/my/deploy/dir", { :force => true })
- @provider.enforce_ownership
- end
-
- it "skips the migration when resource.migrate => false but runs symlinks before migration" do
- @resource.migrate false
- expect(@provider).not_to receive :shell_out!
- expect(@provider).to receive :run_symlinks_before_migrate
- @provider.migrate
- end
-
- it "links the database.yml and runs resource.migration_command when resource.migrate #=> true" do
- @resource.migrate true
- @resource.migration_command "migration_foo"
- @resource.user "deployNinja"
- @resource.group "deployNinjas"
- @resource.environment "RAILS_ENV" => "production"
- expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/config/database.yml", @expected_release_dir + "/config/database.yml")
- expect(@provider).to receive(:enforce_ownership)
-
- allow(STDOUT).to receive(:tty?).and_return(true)
- allow(Chef::Log).to receive(:info?).and_return(true)
- expect(@provider).to receive(:shell_out!).with("migration_foo", :cwd => @expected_release_dir,
- :user => "deployNinja", :group => "deployNinjas",
- :log_level => :info, :live_stream => STDOUT,
- :log_tag => "deploy[/my/deploy/dir]",
- :environment => { "RAILS_ENV" => "production" })
- @provider.migrate
- end
-
- it "purges the current release's /log /tmp/pids/ and /public/system directories" do
- expect(FileUtils).to receive(:rm_rf).with(@expected_release_dir + "/log")
- expect(FileUtils).to receive(:rm_rf).with(@expected_release_dir + "/tmp/pids")
- expect(FileUtils).to receive(:rm_rf).with(@expected_release_dir + "/public/system")
- @provider.purge_tempfiles_from_current_release
- end
-
- it "symlinks temporary files and logs from the shared dir into the current release" do
- allow(FileUtils).to receive(:mkdir_p).with(@resource.shared_path + "/system")
- allow(FileUtils).to receive(:mkdir_p).with(@resource.shared_path + "/pids")
- allow(FileUtils).to receive(:mkdir_p).with(@resource.shared_path + "/log")
- expect(FileUtils).to receive(:mkdir_p).with(@expected_release_dir + "/tmp")
- expect(FileUtils).to receive(:mkdir_p).with(@expected_release_dir + "/public")
- expect(FileUtils).to receive(:mkdir_p).with(@expected_release_dir + "/config")
- expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/system", @expected_release_dir + "/public/system")
- expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/pids", @expected_release_dir + "/tmp/pids")
- expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/log", @expected_release_dir + "/log")
- expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/config/database.yml", @expected_release_dir + "/config/database.yml")
- expect(@provider).to receive(:enforce_ownership)
- @provider.link_tempfiles_to_current_release
- end
-
- it "symlinks the current release dir into production" do
- expect(FileUtils).to receive(:rm_f).with("/my/deploy/dir/current")
- expect(FileUtils).to receive(:ln_sf).with(@expected_release_dir, "/my/deploy/dir/current")
- expect(@provider).to receive(:enforce_ownership)
- @provider.link_current_release_to_production
- end
-
- context "with a customized app layout" do
-
- before do
- @resource.purge_before_symlink(%w{foo bar})
- @resource.create_dirs_before_symlink(%w{baz qux})
- @resource.symlinks "foo/bar" => "foo/bar", "baz" => "qux/baz"
- @resource.symlink_before_migrate "radiohead/in_rainbows.yml" => "awesome"
- end
-
- it "purges the purge_before_symlink directories" do
- expect(FileUtils).to receive(:rm_rf).with(@expected_release_dir + "/foo")
- expect(FileUtils).to receive(:rm_rf).with(@expected_release_dir + "/bar")
- @provider.purge_tempfiles_from_current_release
- end
-
- it "symlinks files from the shared directory to the current release directory" do
- expect(FileUtils).to receive(:mkdir_p).with(@expected_release_dir + "/baz")
- expect(FileUtils).to receive(:mkdir_p).with(@expected_release_dir + "/qux")
- allow(FileUtils).to receive(:mkdir_p).with(@resource.shared_path + "/foo/bar")
- allow(FileUtils).to receive(:mkdir_p).with(@resource.shared_path + "/baz")
- expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/foo/bar", @expected_release_dir + "/foo/bar")
- expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/baz", @expected_release_dir + "/qux/baz")
- expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/radiohead/in_rainbows.yml", @expected_release_dir + "/awesome")
- expect(@provider).to receive(:enforce_ownership)
- @provider.link_tempfiles_to_current_release
- end
-
- end
-
- it "does nothing for restart if restart_command is empty" do
- expect(@provider).not_to receive(:shell_out!)
- @provider.restart
- end
-
- it "runs the restart command in the current application dir when the resource has a restart_command" do
- @resource.restart_command "restartcmd"
- expect(@provider).to receive(:shell_out!).with("restartcmd", :cwd => "/my/deploy/dir/current", :log_tag => "deploy[/my/deploy/dir]", :log_level => :debug)
- @provider.restart
- end
-
- it "lists all available releases" do
- all_releases = ["/my/deploy/dir/20040815162342", "/my/deploy/dir/20040700000000",
- "/my/deploy/dir/20040600000000", "/my/deploy/dir/20040500000000"].sort!
- expect(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases)
- expect(@provider.all_releases).to eql(all_releases)
- end
-
- it "removes all but the 5 newest releases" do
- all_releases = ["/my/deploy/dir/20040815162342", "/my/deploy/dir/20040700000000",
- "/my/deploy/dir/20040600000000", "/my/deploy/dir/20040500000000",
- "/my/deploy/dir/20040400000000", "/my/deploy/dir/20040300000000",
- "/my/deploy/dir/20040200000000", "/my/deploy/dir/20040100000000"].sort!
- allow(@provider).to receive(:all_releases).and_return(all_releases)
- expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/20040100000000")
- expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/20040200000000")
- expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/20040300000000")
- @provider.cleanup!
- end
-
- it "removes all but a certain number of releases when the resource has a keep_releases" do
- @resource.keep_releases 7
- all_releases = ["/my/deploy/dir/20040815162342", "/my/deploy/dir/20040700000000",
- "/my/deploy/dir/20040600000000", "/my/deploy/dir/20040500000000",
- "/my/deploy/dir/20040400000000", "/my/deploy/dir/20040300000000",
- "/my/deploy/dir/20040200000000", "/my/deploy/dir/20040100000000"].sort!
- allow(@provider).to receive(:all_releases).and_return(all_releases)
- expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/20040100000000")
- @provider.cleanup!
- end
-
- it "fires a callback for :release_deleted when deleting an old release" do
- all_releases = ["/my/deploy/dir/20040815162342", "/my/deploy/dir/20040700000000",
- "/my/deploy/dir/20040600000000", "/my/deploy/dir/20040500000000",
- "/my/deploy/dir/20040400000000", "/my/deploy/dir/20040300000000"].sort!
- allow(@provider).to receive(:all_releases).and_return(all_releases)
- allow(FileUtils).to receive(:rm_rf)
- expect(@provider).to receive(:release_deleted).with("/my/deploy/dir/20040300000000")
- @provider.cleanup!
- end
-
- it "puts resource.to_hash in @configuration for backwards compat with capistano-esque deploy hooks" do
- expect(@provider.instance_variable_get(:@configuration)).to eq(@resource.to_hash)
- end
-
- it "sets @configuration[:environment] to the value of RAILS_ENV for backwards compat reasons" do
- resource = Chef::Resource::Deploy.new("/my/deploy/dir")
- resource.environment "production"
- provider = Chef::Provider::Deploy.new(resource, @run_context)
- expect(provider.instance_variable_get(:@configuration)[:environment]).to eql("production")
- end
-
- it "shouldn't give a no method error on migrate if the environment is nil" do
- allow(@provider).to receive(:enforce_ownership)
- allow(@provider).to receive(:run_symlinks_before_migrate)
- allow(@provider).to receive(:shell_out!)
- @provider.migrate
-
- end
-
- context "using inline recipes for callbacks" do
-
- it "runs an inline recipe with the provided block for :callback_name == {:recipe => &block} " do
- snitch = nil
- recipe_code = Proc.new { snitch = 42 }
- #@provider.should_receive(:instance_eval).with(&recipe_code)
- @provider.callback(:whateverz, recipe_code)
- expect(snitch).to eq(42)
- end
-
- it "loads a recipe file from the specified path and from_file evals it" do
- expect(::File).to receive(:exist?).with(@expected_release_dir + "/chefz/foobar_callback.rb").once.and_return(true)
- expect(::Dir).to receive(:chdir).with(@expected_release_dir).and_yield
- expect(@provider).to receive(:from_file).with(@expected_release_dir + "/chefz/foobar_callback.rb")
- @provider.callback(:whateverz, "chefz/foobar_callback.rb")
- end
-
- it "instance_evals a block/proc for restart command" do
- snitch = nil
- restart_cmd = Proc.new { snitch = 42 }
- @resource.restart(&restart_cmd)
- @provider.restart
- expect(snitch).to eq(42)
- end
-
- end
-
- describe "API bridge to capistrano" do
- it "defines sudo as a forwarder to execute" do
- expect(@provider).to receive(:execute).with("the moon, fool")
- @provider.sudo("the moon, fool")
- end
-
- it "defines run as a forwarder to execute, setting the user, group, cwd and environment to new_resource.user" do
- mock_execution = double("Resource::Execute")
- expect(@provider).to receive(:execute).with("iGoToHell4this").and_return(mock_execution)
- @resource.user("notCoolMan")
- @resource.group("Ggroup")
- @resource.environment("APP_ENV" => "staging")
- @resource.deploy_to("/my/app")
- expect(mock_execution).to receive(:user).with("notCoolMan")
- expect(mock_execution).to receive(:group).with("Ggroup")
- expect(mock_execution).to receive(:cwd) {|*args|
- if args.empty?
- nil
- else
- expect(args.size).to eq(1)
- expect(args.first).to eq(@provider.release_path)
- end
- }.twice
- expect(mock_execution).to receive(:environment) { |*args|
- if args.empty?
- nil
- else
- expect(args.size).to eq(1)
- expect(args.first).to eq({ "APP_ENV" => "staging" })
- end
- }.twice
- @provider.run("iGoToHell4this")
-
- end
-
- it "defines run as a forwarder to execute, setting cwd and environment but not override" do
- mock_execution = double("Resource::Execute")
- expect(@provider).to receive(:execute).with("iGoToHell4this").and_return(mock_execution)
- @resource.user("notCoolMan")
- expect(mock_execution).to receive(:user).with("notCoolMan")
- expect(mock_execution).to receive(:cwd).with(no_args()).and_return("/some/value")
- expect(mock_execution).to receive(:environment).with(no_args()).and_return({})
- @provider.run("iGoToHell4this")
- end
-
- it "converts sudo and run to exec resources in hooks" do
- runner = double("tehRunner")
- allow(Chef::Runner).to receive(:new).and_return(runner)
-
- snitch = nil
- @resource.user("tehCat")
-
- callback_code = Proc.new do
- snitch = 42
- temp_collection = self.resource_collection
- run("tehMice")
- snitch = temp_collection.lookup("execute[tehMice]")
- end
-
- expect(runner).to receive(:converge)
- #
- @provider.callback(:phony, callback_code)
- expect(snitch).to be_an_instance_of(Chef::Resource::Execute)
- expect(snitch.user).to eq("tehCat")
- end
- end
-
- describe "installing gems from a gems.yml" do
-
- before do
- allow(::File).to receive(:exist?).with("#{@expected_release_dir}/gems.yml").and_return(true)
- @gem_list = [{ :name => "eventmachine", :version => "0.12.9" }]
- end
-
- it "reads a gems.yml file, creating gem providers for each with action :upgrade" do
- expect(IO).to receive(:read).with("#{@expected_release_dir}/gems.yml").and_return("cookie")
- expect(YAML).to receive(:load).with("cookie").and_return(@gem_list)
-
- gems = @provider.send(:gem_packages)
-
- expect(gems.map { |g| g.action }).to eq([%i{install}])
- expect(gems.map { |g| g.name }).to eq(%w{eventmachine})
- expect(gems.map { |g| g.version }).to eq(%w{0.12.9})
- end
-
- it "takes a list of gem providers converges them" do
- allow(IO).to receive(:read)
- allow(YAML).to receive(:load).and_return(@gem_list)
- expected_gem_resources = @provider.send(:gem_packages).map { |r| [r.name, r.version] }
- gem_runner = @provider.send(:gem_resource_collection_runner)
- # no one has heard of defining == to be meaningful so I have use this monstrosity
- actual = gem_runner.run_context.resource_collection.all_resources.map { |r| [r.name, r.version] }
- expect(actual).to eq(expected_gem_resources)
- end
-
- end
-
-end
diff --git a/spec/unit/provider/directory_spec.rb b/spec/unit/provider/directory_spec.rb
index f8864af7f8..4672db7d8d 100644
--- a/spec/unit/provider/directory_spec.rb
+++ b/spec/unit/provider/directory_spec.rb
@@ -148,6 +148,16 @@ describe Chef::Provider::Directory do
directory.run_action(:create)
expect(new_resource).not_to be_updated
end
+
+ context "in why run mode" do
+ before { Chef::Config[:why_run] = true }
+ after { Chef::Config[:why_run] = false }
+
+ it "does not modify new_resource" do
+ expect(directory).not_to receive(:load_resource_attributes_from_file).with(new_resource)
+ directory.run_action(:create)
+ end
+ end
end
describe "when the directory does not exist" do
@@ -227,7 +237,7 @@ describe Chef::Provider::Directory do
end
end
- describe "#run_action(:create)" do
+ describe "#run_action(:delete)" do
describe "when the directory exists" do
it "deletes the directory" do
directory.run_action(:delete)
@@ -238,6 +248,16 @@ describe Chef::Provider::Directory do
directory.run_action(:delete)
expect(new_resource).to be_updated
end
+
+ it "does not use rm_rf which silently consumes errors" do
+ expect(FileUtils).not_to receive(:rm_rf)
+ expect(FileUtils).to receive(:rm_r)
+ # set recursive or FileUtils isn't used at all.
+ new_resource.recursive(true)
+ directory.run_action(:delete)
+ # reset back...
+ new_resource.recursive(false)
+ end
end
describe "when the directory does not exist" do
diff --git a/spec/unit/provider/dsc_resource_spec.rb b/spec/unit/provider/dsc_resource_spec.rb
index 2a00c2ed7f..747e37f308 100644
--- a/spec/unit/provider/dsc_resource_spec.rb
+++ b/spec/unit/provider/dsc_resource_spec.rb
@@ -28,11 +28,11 @@ describe Chef::Provider::DscResource do
end
context "when Powershell does not support Invoke-DscResource" do
- let (:node) {
+ let (:node) do
node = Chef::Node.new
node.automatic[:languages][:powershell][:version] = "4.0"
node
- }
+ end
it "raises a ProviderNotFound exception" do
expect(provider).not_to receive(:meta_configuration)
expect { provider.run_action(:run) }.to raise_error(
@@ -44,11 +44,11 @@ describe Chef::Provider::DscResource do
context "when RefreshMode is not set to Disabled" do
context "and the WMF 5 is a preview release" do
- let (:node) {
+ let (:node) do
node = Chef::Node.new
node.automatic[:languages][:powershell][:version] = "5.0.10018.0"
node
- }
+ end
it "raises an exception" do
expect(provider).to receive(:dsc_refresh_mode_disabled?).and_return(false)
expect { provider.run_action(:run) }.to raise_error(
@@ -56,11 +56,11 @@ describe Chef::Provider::DscResource do
end
end
context "and the WMF is 5 RTM or newer" do
- let (:node) {
+ let (:node) do
node = Chef::Node.new
node.automatic[:languages][:powershell][:version] = "5.0.10586.0"
node
- }
+ end
it "does not raises an exception" do
expect(provider).to receive(:test_resource)
expect(provider).to receive(:set_resource)
@@ -72,11 +72,11 @@ describe Chef::Provider::DscResource do
end
context "when the LCM supports Invoke-DscResource" do
- let (:node) {
+ let (:node) do
node = Chef::Node.new
node.automatic[:languages][:powershell][:version] = "5.0.10018.0"
node
- }
+ end
let (:resource_result) { double("CmdletResult", return_value: { "InDesiredState" => true }, stream: "description") }
let (:invoke_dsc_resource) { double("cmdlet", run!: resource_result) }
let (:store) { double("ResourceStore", find: resource_records) }
@@ -153,10 +153,11 @@ describe Chef::Provider::DscResource do
end
context "multiple resource are found" do
- let (:resource_records) { [
- { "Module" => { "Name" => "ModuleName1" } },
- { "Module" => { "Name" => "ModuleName2" } },
- ] }
+ let (:resource_records) do
+ [
+ { "Module" => { "Name" => "ModuleName1", "Version" => "1.0.0.0" } },
+ { "Module" => { "Name" => "ModuleName1", "Version" => "2.0.0.0" } },
+ ] end
it "raises MultipleDscResourcesFound" do
expect { provider.run_action(:run) }.to raise_error(Chef::Exceptions::MultipleDscResourcesFound)
@@ -164,4 +165,179 @@ describe Chef::Provider::DscResource do
end
end
end
+
+ describe "define_resource_requirements" do
+ let (:node) do
+ set_node_object
+ end
+
+ context "module usage is valid" do
+ before do
+ allow(provider).to receive(:module_usage_valid?).and_return(true)
+ allow(provider).to receive(:action_run)
+ end
+
+ [:run].each do |action|
+ context "action #{action}" do
+ it "does not raise the exception" do
+ expect { provider.run_action(action) }.not_to raise_error
+ end
+ end
+ end
+ end
+
+ context "module usage is invalid" do
+ before do
+ allow(provider).to receive(:module_usage_valid?).and_return(false)
+ allow(provider).to receive(:action_run)
+ end
+
+ [:run].each do |action|
+ context "action #{action}" do
+ it "raises the exception" do
+ expect { provider.run_action(action) }.to raise_error(
+ Chef::Exceptions::DSCModuleNameMissing
+ )
+ end
+ end
+ end
+ end
+ end
+
+ describe "module_usage_valid?" do
+ let (:node) do
+ set_node_object
+ end
+
+ context "module_name and module_version both are not provided" do
+ before do
+ provider.instance_variable_set(:@module_name, nil)
+ provider.instance_variable_set(:@module_version, nil)
+ end
+
+ it "returns true" do
+ response = provider.send(:module_usage_valid?)
+ expect(response).to be == true
+ end
+ end
+
+ context "module_name and module_version both are provided" do
+ before do
+ provider.instance_variable_set(:@module_name, "my_module")
+ provider.instance_variable_set(:@module_version, "1.3")
+ end
+
+ it "returns true" do
+ response = provider.send(:module_usage_valid?)
+ expect(response).to be == true
+ end
+ end
+
+ context "module_name is given but module_version is not given" do
+ before do
+ provider.instance_variable_set(:@module_name, "my_module")
+ provider.instance_variable_set(:@module_version, nil)
+ end
+
+ it "returns true" do
+ response = provider.send(:module_usage_valid?)
+ expect(response).to be == true
+ end
+ end
+
+ context "module_name is not given but module_version is given" do
+ before do
+ provider.instance_variable_set(:@module_name, nil)
+ provider.instance_variable_set(:@module_version, "1.4.0.1")
+ end
+
+ it "returns false" do
+ response = provider.send(:module_usage_valid?)
+ expect(response).to be == false
+ end
+ end
+ end
+
+ describe "module_info_object" do
+ let (:node) do
+ set_node_object
+ end
+
+ context "module_version is not given" do
+ before do
+ provider.instance_variable_set(:@module_version, nil)
+ allow(provider).to receive(:module_name).and_return("my_module")
+ end
+
+ it "returns only name of the module" do
+ response = provider.send(:module_info_object)
+ expect(response).to be == "my_module"
+ end
+ end
+
+ context "module_version is given" do
+ before do
+ provider.instance_variable_set(:@module_version, "1.3.1")
+ allow(provider).to receive(:module_name).and_return("my_module")
+ end
+
+ it "returns the module info object" do
+ response = provider.send(:module_info_object)
+ expect(response).to be == "@{ModuleName='my_module';ModuleVersion='1.3.1'}"
+ end
+ end
+ end
+
+ describe "invoke_resource" do
+ let (:node) do
+ set_node_object
+ end
+
+ let(:cmdlet) { double(:run! => nil) }
+
+ before(:each) do
+ allow(provider).to receive(:translate_type).and_return("my_properties")
+ provider.instance_variable_set(:@new_resource, double(
+ :properties => "my_properties", :resource => "my_resource", :timeout => 123
+ ))
+ end
+
+ context "when module_version is not given" do
+ before do
+ allow(provider).to receive(:module_info_object).and_return("my_module")
+ end
+
+ it "invokes Invoke-DscResource command with module name" do
+ expect(Chef::Util::Powershell::Cmdlet).to receive(:new).with(
+ node,
+ "Invoke-DscResource -Method my_method -Name my_resource -Property my_properties -Module my_module -Verbose",
+ "my_output_format"
+ ).and_return(cmdlet)
+ provider.send(:invoke_resource, "my_method", "my_output_format")
+ end
+ end
+
+ context "when module_version is given" do
+ before do
+ allow(provider).to receive(:module_info_object).and_return(
+ "@{ModuleName='my_module';ModuleVersion='my_module_version'}"
+ )
+ end
+
+ it "invokes Invoke-DscResource command with module info object" do
+ expect(Chef::Util::Powershell::Cmdlet).to receive(:new).with(
+ node,
+ "Invoke-DscResource -Method my_method -Name my_resource -Property my_properties -Module @{ModuleName='my_module';ModuleVersion='my_module_version'} -Verbose",
+ "my_output_format"
+ ).and_return(cmdlet)
+ provider.send(:invoke_resource, "my_method", "my_output_format")
+ end
+ end
+ end
+end
+
+def set_node_object
+ node = Chef::Node.new
+ node.automatic[:languages][:powershell][:version] = "5.0.10586.0"
+ node
end
diff --git a/spec/unit/provider/dsc_script_spec.rb b/spec/unit/provider/dsc_script_spec.rb
index 3877a37e61..5f091b8813 100644
--- a/spec/unit/provider/dsc_script_spec.rb
+++ b/spec/unit/provider/dsc_script_spec.rb
@@ -23,11 +23,11 @@ require "spec_helper"
describe Chef::Provider::DscScript do
context "when DSC is available" do
- let (:node) {
+ let (:node) do
node = Chef::Node.new
node.automatic[:languages][:powershell][:version] = "4.0"
node
- }
+ end
let (:events) { Chef::EventDispatch::Dispatcher.new }
let (:run_context) { Chef::RunContext.new(node, {}, events) }
let (:resource) { Chef::Resource::DscScript.new("script", run_context) }
@@ -35,7 +35,7 @@ describe Chef::Provider::DscScript do
Chef::Provider::DscScript.new(resource, run_context)
end
- describe '#load_current_resource' do
+ describe "#load_current_resource" do
it "describes the resource as converged if there were 0 DSC resources" do
allow(provider).to receive(:run_configuration).with(:test).and_return([])
provider.load_current_resource
@@ -75,7 +75,7 @@ describe Chef::Provider::DscScript do
end
end
- describe '#generate_configuration_document' do
+ describe "#generate_configuration_document" do
# I think integration tests should cover these cases
it "uses configuration_document_from_script_path when a dsc script file is given" do
@@ -123,7 +123,7 @@ describe Chef::Provider::DscScript do
end
end
- describe '#generate_description' do
+ describe "#generate_description" do
it "removes the resource name from the beginning of any log line from the LCM" do
dsc_resource_info = Chef::Util::DSC::ResourceInfo.new("resourcename", true, ["resourcename doing something", "lastline"])
provider.instance_variable_set("@dsc_resources_info", [dsc_resource_info])
@@ -156,16 +156,16 @@ describe Chef::Provider::DscScript do
it "raises an exception for powershell version '#{version}'" do
node.automatic[:languages][:powershell][:version] = version
- expect {
+ expect do
provider.run_action(:run)
- }.to raise_error(Chef::Exceptions::ProviderNotFound)
+ end.to raise_error(Chef::Exceptions::ProviderNotFound)
end
end
it "raises an exception if Powershell is not present" do
- expect {
+ expect do
provider.run_action(:run)
- }.to raise_error(Chef::Exceptions::ProviderNotFound)
+ end.to raise_error(Chef::Exceptions::ProviderNotFound)
end
end
diff --git a/spec/unit/provider/env/windows_spec.rb b/spec/unit/provider/env/windows_spec.rb
deleted file mode 100644
index abe2344443..0000000000
--- a/spec/unit/provider/env/windows_spec.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-#
-# Author:: Sander van Harmelen <svanharmelen@schubergphilis.com>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-
-describe Chef::Provider::Env::Windows, :windows_only do
- let(:node) { Chef::Node.new }
- let(:events) { Chef::EventDispatch::Dispatcher.new }
- let(:run_context) { Chef::RunContext.new(node, {}, events) }
-
- context "when environment variable is not PATH" do
- let(:new_resource) {
- new_resource = Chef::Resource::Env.new("CHEF_WINDOWS_ENV_TEST")
- new_resource.value("foo")
- new_resource
- }
- let(:provider) {
- provider = Chef::Provider::Env::Windows.new(new_resource, run_context)
- allow(provider).to receive(:env_obj).and_return(double("null object").as_null_object)
- provider
- }
-
- describe "action_create" do
- before do
- ENV.delete("CHEF_WINDOWS_ENV_TEST")
- provider.key_exists = false
- end
-
- it "should update the ruby ENV object when it creates the key" do
- provider.action_create
- expect(ENV["CHEF_WINDOWS_ENV_TEST"]).to eql("foo")
- end
- end
-
- describe "action_modify" do
- before do
- ENV["CHEF_WINDOWS_ENV_TEST"] = "foo"
- end
-
- it "should update the ruby ENV object when it updates the value" do
- expect(provider).to receive(:requires_modify_or_create?).and_return(true)
- new_resource.value("foobar")
- provider.action_modify
- expect(ENV["CHEF_WINDOWS_ENV_TEST"]).to eql("foobar")
- end
-
- describe "action_delete" do
- before do
- ENV["CHEF_WINDOWS_ENV_TEST"] = "foo"
- end
-
- it "should update the ruby ENV object when it deletes the key" do
- provider.action_delete
- expect(ENV["CHEF_WINDOWS_ENV_TEST"]).to eql(nil)
- end
- end
- end
- end
-
- context "when environment is PATH" do
- describe "for PATH" do
- let(:system_root) { "%SystemRoot%" }
- let(:system_root_value) { 'D:\Windows' }
- let(:new_resource) {
- new_resource = Chef::Resource::Env.new("PATH")
- new_resource.value(system_root)
- new_resource
- }
- let(:provider) {
- provider = Chef::Provider::Env::Windows.new(new_resource, run_context)
- allow(provider).to receive(:env_obj).and_return(double("null object").as_null_object)
- provider
- }
-
- before do
- stub_const("ENV", { "PATH" => "" })
- end
-
- it "replaces Windows system variables" do
- expect(provider).to receive(:requires_modify_or_create?).and_return(true)
- expect(provider).to receive(:expand_path).with(system_root).and_return(system_root_value)
- provider.action_modify
- expect(ENV["PATH"]).to eql(system_root_value)
- end
- end
-
- end
-end
diff --git a/spec/unit/provider/erl_call_spec.rb b/spec/unit/provider/erl_call_spec.rb
deleted file mode 100644
index f1c229028a..0000000000
--- a/spec/unit/provider/erl_call_spec.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-#
-# Author:: Joe Williams (<joe@joetify.com>)
-# Copyright:: Copyright 2009-2016, Joe Williams
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-
-describe Chef::Provider::ErlCall do
- before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
-
- @new_resource = Chef::Resource::ErlCall.new("test", @node)
- @new_resource.code("io:format(\"burritos\", []).")
- @new_resource.node_name("chef@localhost")
- @new_resource.name("test")
-
- @provider = Chef::Provider::ErlCall.new(@new_resource, @run_context)
-
- allow(@provider).to receive(:popen4).and_return(@status)
- @stdin = StringIO.new
- @stdout = StringIO.new("{ok, woohoo}")
- @stderr = StringIO.new
- @pid = 2342999
- end
-
- it "should return a Chef::Provider::ErlCall object" do
- provider = Chef::Provider::ErlCall.new(@new_resource, @run_context)
- expect(provider).to be_a_kind_of(Chef::Provider::ErlCall)
- end
-
- it "should return true" do
- expect(@provider.load_current_resource).to eql(true)
- end
-
- describe "when running a distributed erl call resource" do
- before do
- @new_resource.cookie("nomnomnom")
- @new_resource.distributed(true)
- @new_resource.name_type("sname")
- end
-
- it "should write to stdin of the erl_call command" do
- expected_cmd = "erl_call -e -s -sname chef@localhost -c nomnomnom"
- expect(@provider).to receive(:popen4).with(expected_cmd, :waitlast => true).and_return([@pid, @stdin, @stdout, @stderr])
- expect(Process).to receive(:wait).with(@pid)
-
- @provider.action_run
-
- expect(@stdin.string).to eq("#{@new_resource.code}\n")
- end
- end
-
- describe "when running a local erl call resource" do
- before do
- @new_resource.cookie(nil)
- @new_resource.distributed(false)
- @new_resource.name_type("name")
- end
-
- it "should write to stdin of the erl_call command" do
- expect(@provider).to receive(:popen4).with("erl_call -e -name chef@localhost ", :waitlast => true).and_return([@pid, @stdin, @stdout, @stderr])
- expect(Process).to receive(:wait).with(@pid)
-
- @provider.action_run
-
- expect(@stdin.string).to eq("#{@new_resource.code}\n")
- end
- end
-
-end
diff --git a/spec/unit/provider/execute_spec.rb b/spec/unit/provider/execute_spec.rb
index 4b0afcb928..58cb9f33a8 100644
--- a/spec/unit/provider/execute_spec.rb
+++ b/spec/unit/provider/execute_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Prajakta Purohit (<prajakta@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -76,16 +76,34 @@ describe Chef::Provider::Execute do
describe "#action_run" do
it "runs shell_out with the default options" do
- expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with(new_resource.name, opts)
expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original
expect(Chef::Log).not_to receive(:warn)
provider.run_action(:run)
expect(new_resource).to be_updated
end
+ # this next test is tightly coupled to the implementation of the underlying shell_out mixin that we're using
+ # but the point is to ensure that we are not picking up the PATH mangling and locale-variable mangling that the internal
+ # shell_out API uses. we are asserting that we emulate `ls -la` when the user does `execute "ls -la"`, and to
+ # do that we get dirty and start mocking the implementation of the shell_out mixin itself. while arguments like
+ # "timeout", "returns", "log_level" and "log_tag" appear here, we MUST NOT have an "environment" or "env" argument
+ # that we are passing to Mixlib::ShellOut by default -- ever. you might have to add some other argument here from
+ # time to time, but you MUST NOT change the environment.
+ it "does not use shell_out in such a way as to insert extra environment variables" do
+ mock = instance_double(Mixlib::ShellOut)
+ expect(Mixlib::ShellOut).to receive(:new).with("foo_resource", { timeout: 3600, returns: 0, log_level: :info, log_tag: "execute[foo_resource]" }).and_return(mock)
+ expect(mock).to receive(:live_stream=).with(nil)
+ allow(mock).to receive(:live_stream)
+ expect(mock).to receive(:run_command)
+ expect(mock).to receive(:error!)
+ provider.run_action(:run)
+ expect(new_resource).to be_updated
+ end
+
it "if you pass a command attribute, it runs the command" do
new_resource.command "/usr/argelbargle/bin/oogachacka 12345"
- expect(provider).to receive(:shell_out!).with(new_resource.command, opts)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with(new_resource.command, opts)
expect(provider).to receive(:converge_by).with("execute #{new_resource.command}").and_call_original
expect(Chef::Log).not_to receive(:warn)
provider.run_action(:run)
@@ -96,7 +114,7 @@ describe Chef::Provider::Execute do
new_resource.sensitive true
# Since the resource is sensitive, it should not have :live_stream set
opts.delete(:live_stream)
- expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with(new_resource.name, opts)
expect(provider).to receive(:converge_by).with("execute sensitive resource").and_call_original
expect(Chef::Log).not_to receive(:warn)
provider.run_action(:run)
@@ -106,7 +124,7 @@ describe Chef::Provider::Execute do
it "should do nothing if the sentinel file exists" do
new_resource.creates "/foo_resource"
expect(FileTest).to receive(:exist?).with(new_resource.creates).and_return(true)
- expect(provider).not_to receive(:shell_out!)
+ expect(provider).not_to receive(:shell_out_with_systems_locale!)
expect(Chef::Log).not_to receive(:warn)
provider.run_action(:run)
expect(new_resource).not_to be_updated
@@ -118,19 +136,9 @@ describe Chef::Provider::Execute do
new_resource.creates "foo_resource"
end
- it "should warn in Chef-12", chef: "< 13" do
- expect(Chef::Log).to receive(:warn).with(/relative path/)
- expect(FileTest).to receive(:exist?).with(new_resource.creates).and_return(true)
- expect(provider).not_to receive(:shell_out!)
- provider.run_action(:run)
- expect(new_resource).not_to be_updated
- end
-
- it "should raise if user specified relative path without cwd for Chef-13", chef: ">= 13" do
- expect(Chef::Log).to receive(:warn).with(/relative path/)
- expect(FileTest).to receive(:exist?).with(new_resource.creates).and_return(true)
- expect(provider).not_to receive(:shell_out!)
- expect { provider.run_action(:run) }.to raise_error # @todo: add a real error for Chef-13
+ it "should raise if user specified relative path without cwd for Chef-13" do
+ expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect { provider.run_action(:run) }.to raise_error(Chef::Exceptions::Execute)
end
end
@@ -140,7 +148,7 @@ describe Chef::Provider::Execute do
expect(FileTest).not_to receive(:exist?).with(new_resource.creates)
expect(FileTest).to receive(:exist?).with(File.join("/tmp", new_resource.creates)).and_return(true)
expect(Chef::Log).not_to receive(:warn)
- expect(provider).not_to receive(:shell_out!)
+ expect(provider).not_to receive(:shell_out_with_systems_locale!)
provider.run_action(:run)
expect(new_resource).not_to be_updated
@@ -149,7 +157,7 @@ describe Chef::Provider::Execute do
it "should not include stdout/stderr in failure exception for sensitive resource" do
opts.delete(:live_stream)
new_resource.sensitive true
- expect(provider).to receive(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed)
+ expect(provider).to receive(:shell_out_with_systems_locale!).and_raise(Mixlib::ShellOut::ShellCommandFailed)
expect do
provider.run_action(:run)
end.to raise_error(Mixlib::ShellOut::ShellCommandFailed, /suppressed for sensitive resource/)
@@ -158,7 +166,7 @@ describe Chef::Provider::Execute do
describe "streaming output" do
it "should not set the live_stream if sensitive is on" do
new_resource.sensitive true
- expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with(new_resource.name, opts)
expect(provider).to receive(:converge_by).with("execute sensitive resource").and_call_original
expect(Chef::Log).not_to receive(:warn)
provider.run_action(:run)
@@ -175,7 +183,7 @@ describe Chef::Provider::Execute do
it "should set the live_stream if the log level is info or above" do
nopts = opts
nopts[:live_stream] = @live_stream
- expect(provider).to receive(:shell_out!).with(new_resource.name, nopts)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with(new_resource.name, nopts)
expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original
expect(Chef::Log).not_to receive(:warn)
provider.run_action(:run)
@@ -187,7 +195,7 @@ describe Chef::Provider::Execute do
new_resource.live_stream true
nopts = opts
nopts[:live_stream] = @live_stream
- expect(provider).to receive(:shell_out!).with(new_resource.name, nopts)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with(new_resource.name, nopts)
expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original
expect(Chef::Log).not_to receive(:warn)
provider.run_action(:run)
@@ -196,7 +204,7 @@ describe Chef::Provider::Execute do
it "should not set the live_stream if the resource is sensitive" do
new_resource.sensitive true
- expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with(new_resource.name, opts)
expect(provider).to receive(:converge_by).with("execute sensitive resource").and_call_original
expect(Chef::Log).not_to receive(:warn)
provider.run_action(:run)
@@ -209,7 +217,7 @@ describe Chef::Provider::Execute do
nopts = opts
nopts[:live_stream] = STDOUT
allow(STDOUT).to receive(:tty?).and_return(true)
- expect(provider).to receive(:shell_out!).with(new_resource.name, nopts)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with(new_resource.name, nopts)
expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original
expect(Chef::Log).not_to receive(:warn)
provider.run_action(:run)
@@ -219,7 +227,7 @@ describe Chef::Provider::Execute do
it "should not set the live_stream to STDOUT if we are a TTY, not daemonized, but sensitive" do
new_resource.sensitive true
allow(STDOUT).to receive(:tty?).and_return(true)
- expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with(new_resource.name, opts)
expect(provider).to receive(:converge_by).with("execute sensitive resource").and_call_original
expect(Chef::Log).not_to receive(:warn)
provider.run_action(:run)
@@ -229,7 +237,7 @@ describe Chef::Provider::Execute do
it "should not set the live_stream to STDOUT if we are a TTY, but daemonized" do
Chef::Config[:daemon] = true
allow(STDOUT).to receive(:tty?).and_return(true)
- expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with(new_resource.name, opts)
expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original
expect(Chef::Log).not_to receive(:warn)
provider.run_action(:run)
@@ -238,6 +246,5 @@ describe Chef::Provider::Execute do
end
end
-
end
end
diff --git a/spec/unit/provider/file/content_spec.rb b/spec/unit/provider/file/content_spec.rb
index a31c75baf4..f840d92dbb 100644
--- a/spec/unit/provider/file/content_spec.rb
+++ b/spec/unit/provider/file/content_spec.rb
@@ -28,12 +28,12 @@ describe Chef::Provider::File::Content do
double("Chef::Provider::File::Resource (current)")
end
- let(:enclosing_directory) {
+ let(:enclosing_directory) do
canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
- }
- let(:resource_path) {
+ end
+ let(:resource_path) do
canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
- }
+ end
let(:new_resource) do
double("Chef::Provider::File::Resource (new)", :name => "seattle.txt", :path => resource_path)
@@ -77,9 +77,9 @@ describe Chef::Provider::File::Content do
end
context "when creating a tempfiles in destdir fails" do
- let(:enclosing_directory) {
+ let(:enclosing_directory) do
canonicalize_path("/nonexisting/path")
- }
+ end
it "returns a tempfile in the tempdir when :file_deployment_uses_destdir is set to :auto" do
Chef::Config[:file_staging_uses_destdir] = :auto
diff --git a/spec/unit/provider/file_spec.rb b/spec/unit/provider/file_spec.rb
index 11bef8a44d..fed9cd5ece 100644
--- a/spec/unit/provider/file_spec.rb
+++ b/spec/unit/provider/file_spec.rb
@@ -34,13 +34,14 @@ describe Chef::Provider::File do
let(:node) { double("Chef::Node") }
let(:events) { double("Chef::Events").as_null_object } # mock all the methods
- let(:run_context) { double("Chef::RunContext", :node => node, :events => events) }
- let(:enclosing_directory) {
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+ let(:run_context) { double("Chef::RunContext", :node => node, :events => events, :logger => logger) }
+ let(:enclosing_directory) do
canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
- }
- let(:resource_path) {
+ end
+ let(:resource_path) do
canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
- }
+ end
# Subject
diff --git a/spec/unit/provider/git_spec.rb b/spec/unit/provider/git_spec.rb
index 97f04a5a77..b8a26a3543 100644
--- a/spec/unit/provider/git_spec.rb
+++ b/spec/unit/provider/git_spec.rb
@@ -58,7 +58,7 @@ describe Chef::Provider::Git do
it "determines the current revision when there is one" do
expect(::File).to receive(:exist?).with("/my/deploy/dir/.git").and_return(true)
@stdout = "9b4d8dc38dd471246e7cfb1c3c1ad14b0f2bee13\n"
- expect(@provider).to receive(:shell_out!).with("git rev-parse HEAD", { :cwd => "/my/deploy/dir", :returns => [0, 128] }).and_return(double("ShellOut result", :stdout => @stdout))
+ expect(@provider).to receive(:shell_out!).with("git rev-parse HEAD", { :cwd => "/my/deploy/dir", :returns => [0, 128], :log_tag => "git[web2.0 app]" }).and_return(double("ShellOut result", :stdout => @stdout))
expect(@provider.find_current_revision).to eql("9b4d8dc38dd471246e7cfb1c3c1ad14b0f2bee13")
end
@@ -66,7 +66,7 @@ describe Chef::Provider::Git do
expect(::File).to receive(:exist?).with("/my/deploy/dir/.git").and_return(true)
@stderr = "fatal: Not a git repository (or any of the parent directories): .git"
@stdout = ""
- expect(@provider).to receive(:shell_out!).with("git rev-parse HEAD", :cwd => "/my/deploy/dir", :returns => [0, 128]).and_return(double("ShellOut result", :stdout => "", :stderr => @stderr))
+ expect(@provider).to receive(:shell_out!).with("git rev-parse HEAD", :cwd => "/my/deploy/dir", :returns => [0, 128], :log_tag => "git[web2.0 app]" ).and_return(double("ShellOut result", :stdout => "", :stderr => @stderr))
expect(@provider.find_current_revision).to be_nil
end
end
@@ -83,6 +83,61 @@ describe Chef::Provider::Git do
expect(@provider.new_resource).to equal(@resource)
end
+ context "cast git version into gem version object" do
+ it "returns correct version with standard git" do
+ expect(@provider).to receive(:shell_out!)
+ .with("git --version", log_tag: "git[web2.0 app]")
+ .and_return(double("ShellOut result", stdout: "git version 2.14.1"))
+ expect(@provider.git_gem_version).to eq Gem::Version.new("2.14.1")
+ end
+
+ it "returns correct version with Apple git" do
+ expect(@provider).to receive(:shell_out!)
+ .with("git --version", log_tag: "git[web2.0 app]")
+ .and_return(double("ShellOut result", stdout: "git version 2.11.0 (Apple Git-81)"))
+ expect(@provider.git_gem_version).to eq Gem::Version.new("2.11.0")
+ end
+
+ it "maintains deprecated method name" do
+ expect(@provider).to receive(:shell_out!)
+ .with("git --version", log_tag: "git[web2.0 app]")
+ .and_return(double("ShellOut result", stdout: "git version 1.2.3"))
+ expect(@provider.git_minor_version).to eq Gem::Version.new("1.2.3")
+ end
+
+ it "does not know how to handle other version" do
+ expect(@provider).to receive(:shell_out!)
+ .with("git --version", log_tag: "git[web2.0 app]")
+ .and_return(double("ShellOut result", stdout: "git version home-grown-git-99"))
+ expect(@provider.git_gem_version).to be_nil
+ end
+
+ it "determines single branch option when it fails to parse git version" do
+ expect(@provider).to receive(:shell_out!)
+ .with("git --version", log_tag: "git[web2.0 app]")
+ .and_return(double("ShellOut result", stdout: "git version home-grown-git-99"))
+ expect(@provider.git_has_single_branch_option?).to be false
+ end
+
+ it "determines single branch option as true when it parses git version and version is large" do
+ expect(@provider).to receive(:shell_out!)
+ .with("git --version", log_tag: "git[web2.0 app]")
+ .and_return(double("ShellOut result", stdout: "git version 1.8.0"))
+ expect(@provider.git_has_single_branch_option?).to be true
+ end
+
+ it "determines single branch option as false when it parses git version and version is small" do
+ expect(@provider).to receive(:shell_out!)
+ .with("git --version", log_tag: "git[web2.0 app]")
+ .and_return(double("ShellOut result", stdout: "git version 1.7.4"))
+ expect(@provider.git_has_single_branch_option?).to be false
+ end
+
+ it "is compatible with git in travis" do
+ expect(@provider.git_gem_version).to be > Gem::Version.new("1.0")
+ end
+ end
+
context "resolving revisions to a SHA" do
before do
@@ -218,7 +273,7 @@ SHAS
context "with an ssh wrapper" do
let(:deploy_user) { "deployNinja" }
let(:wrapper) { "do_it_this_way.sh" }
- let(:expected_cmd) { 'git clone "git://github.com/opscode/chef.git" "/my/deploy/dir"' }
+ let(:expected_cmd) { 'git clone "git://github.com/opscode/chef.git" "/my/deploy/dir"' }
let(:default_options) do
{
:user => deploy_user,
@@ -267,16 +322,52 @@ SHAS
end
end
+ context "with a user id" do
+ let(:deploy_user) { 123 }
+ let(:expected_cmd) { 'git clone "git://github.com/opscode/chef.git" "/my/deploy/dir"' }
+ let(:default_options) do
+ {
+ :user => 123,
+ :environment => { "HOME" => "/home/deployNinja" },
+ :log_tag => "git[web2.0 app]",
+ }
+ end
+ before do
+ @resource.user deploy_user
+ allow(Etc).to receive(:getpwuid).and_return(double("Struct::Passwd", :name => @resource.user, :dir => "/home/deployNinja"))
+ end
+ context "with a specific home" do
+ let (:override_home) do
+ { "HOME" => "/home/masterNinja" }
+ end
+ let(:overrided_options) do
+ {
+ :user => 123,
+ :environment => { "HOME" => "/home/masterNinja" },
+ :log_tag => "git[web2.0 app]",
+ }
+ end
+ before do
+ @resource.environment(override_home)
+ end
+ before { @resource.environment(override_home) }
+ it "clones a repo with amended git options with specific home" do
+ expect(@provider).to receive(:shell_out!).with(expected_cmd, hash_including(overrided_options))
+ @provider.clone
+ end
+ end
+ end
+
it "runs a clone command with escaped destination" do
@resource.user "deployNinja"
allow(Etc).to receive(:getpwnam).and_return(double("Struct::Passwd", :name => @resource.user, :dir => "/home/deployNinja"))
@resource.destination "/Application Support/with/space"
@resource.ssh_wrapper "do_it_this_way.sh"
- expected_cmd = "git clone \"git://github.com/opscode/chef.git\" \"/Application Support/with/space\""
+ expected_cmd = "git clone \"git://github.com/opscode/chef.git\" \"/Application Support/with/space\""
expect(@provider).to receive(:shell_out!).with(expected_cmd, :user => "deployNinja",
- :environment => { "GIT_SSH" => "do_it_this_way.sh",
- "HOME" => "/home/deployNinja" },
- :log_tag => "git[web2.0 app]")
+ :log_tag => "git[web2.0 app]",
+ :environment => { "HOME" => "/home/deployNinja",
+ "GIT_SSH" => "do_it_this_way.sh" })
@provider.clone
end
@@ -334,8 +425,12 @@ SHAS
it "runs a sync command with default options" do
expect(@provider).to receive(:setup_remote_tracking_branches).with(@resource.remote, @resource.repository)
- expected_cmd = "git fetch origin && git fetch origin --tags && git reset --hard d35af14d41ae22b19da05d7d03a0bafc321b244c"
- expect(@provider).to receive(:shell_out!).with(expected_cmd, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]")
+ expected_cmd1 = "git fetch --prune origin"
+ expect(@provider).to receive(:shell_out!).with(expected_cmd1, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]")
+ expected_cmd2 = "git fetch origin --tags"
+ expect(@provider).to receive(:shell_out!).with(expected_cmd2, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]")
+ expected_cmd3 = "git reset --hard d35af14d41ae22b19da05d7d03a0bafc321b244c"
+ expect(@provider).to receive(:shell_out!).with(expected_cmd3, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]")
@provider.fetch_updates
end
@@ -344,27 +439,46 @@ SHAS
allow(Etc).to receive(:getpwnam).and_return(double("Struct::Passwd", :name => @resource.user, :dir => "/home/whois"))
@resource.group("thisis")
expect(@provider).to receive(:setup_remote_tracking_branches).with(@resource.remote, @resource.repository)
- expected_cmd = "git fetch origin && git fetch origin --tags && git reset --hard d35af14d41ae22b19da05d7d03a0bafc321b244c"
- expect(@provider).to receive(:shell_out!).with(expected_cmd, :cwd => "/my/deploy/dir",
- :user => "whois", :group => "thisis",
- :log_tag => "git[web2.0 app]",
- :environment => { "HOME" => "/home/whois" })
+
+ expected_cmd1 = "git fetch --prune origin"
+ expect(@provider).to receive(:shell_out!).with(expected_cmd1, :cwd => "/my/deploy/dir",
+ :user => "whois", :group => "thisis",
+ :log_tag => "git[web2.0 app]",
+ :environment => { "HOME" => "/home/whois" })
+ expected_cmd2 = "git fetch origin --tags"
+ expect(@provider).to receive(:shell_out!).with(expected_cmd2, :cwd => "/my/deploy/dir",
+ :user => "whois", :group => "thisis",
+ :log_tag => "git[web2.0 app]",
+ :environment => { "HOME" => "/home/whois" })
+ expected_cmd3 = "git reset --hard d35af14d41ae22b19da05d7d03a0bafc321b244c"
+ expect(@provider).to receive(:shell_out!).with(expected_cmd3, :cwd => "/my/deploy/dir",
+ :user => "whois", :group => "thisis",
+ :log_tag => "git[web2.0 app]",
+ :environment => { "HOME" => "/home/whois" })
@provider.fetch_updates
end
it "configures remote tracking branches when remote is ``origin''" do
@resource.remote "origin"
expect(@provider).to receive(:setup_remote_tracking_branches).with(@resource.remote, @resource.repository)
- fetch_command = "git fetch origin && git fetch origin --tags && git reset --hard d35af14d41ae22b19da05d7d03a0bafc321b244c"
- expect(@provider).to receive(:shell_out!).with(fetch_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]")
+ fetch_command1 = "git fetch --prune origin"
+ expect(@provider).to receive(:shell_out!).with(fetch_command1, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]")
+ fetch_command2 = "git fetch origin --tags"
+ expect(@provider).to receive(:shell_out!).with(fetch_command2, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]")
+ fetch_command3 = "git reset --hard d35af14d41ae22b19da05d7d03a0bafc321b244c"
+ expect(@provider).to receive(:shell_out!).with(fetch_command3, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]")
@provider.fetch_updates
end
it "configures remote tracking branches when remote is not ``origin''" do
@resource.remote "opscode"
expect(@provider).to receive(:setup_remote_tracking_branches).with(@resource.remote, @resource.repository)
- fetch_command = "git fetch opscode && git fetch opscode --tags && git reset --hard d35af14d41ae22b19da05d7d03a0bafc321b244c"
- expect(@provider).to receive(:shell_out!).with(fetch_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]")
+ fetch_command1 = "git fetch --prune opscode"
+ expect(@provider).to receive(:shell_out!).with(fetch_command1, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]")
+ fetch_command2 = "git fetch opscode --tags"
+ expect(@provider).to receive(:shell_out!).with(fetch_command2, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]")
+ fetch_command3 = "git reset --hard d35af14d41ae22b19da05d7d03a0bafc321b244c"
+ expect(@provider).to receive(:shell_out!).with(fetch_command3, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]")
@provider.fetch_updates
end
diff --git a/spec/unit/provider/group/dscl_spec.rb b/spec/unit/provider/group/dscl_spec.rb
index bc69d7580e..6e40e41579 100644
--- a/spec/unit/provider/group/dscl_spec.rb
+++ b/spec/unit/provider/group/dscl_spec.rb
@@ -19,21 +19,24 @@
require "spec_helper"
describe Chef::Provider::Group::Dscl do
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+
before do
@node = Chef::Node.new
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
+ allow(@run_context).to receive(:logger).and_return(logger)
@new_resource = Chef::Resource::Group.new("aj")
@current_resource = Chef::Resource::Group.new("aj")
@provider = Chef::Provider::Group::Dscl.new(@new_resource, @run_context)
@provider.current_resource = @current_resource
- @status = double(:stdout => "\n", :stderr => "", :exitstatus => 0)
+ @status = double(stdout: "\n", stderr: "", exitstatus: 0)
allow(@provider).to receive(:shell_out).and_return(@status)
end
it "should run shell_out with the supplied array of arguments appended to the dscl command" do
- expect(@provider).to receive(:shell_out).with("dscl . -cmd /Path arg1 arg2")
+ expect(@provider).to receive(:shell_out).with("dscl", ".", "-cmd", "/Path", "arg1", "arg2")
@provider.dscl("cmd", "/Path", "arg1", "arg2")
end
@@ -45,30 +48,29 @@ describe Chef::Provider::Group::Dscl do
describe "safe_dscl" do
before do
- @node = Chef::Node.new
- @provider = Chef::Provider::Group::Dscl.new(@node, @new_resource)
+ @provider = Chef::Provider::Group::Dscl.new(@new_resource, @run_context)
allow(@provider).to receive(:dscl).and_return(["cmd", @status, "stdout", "stderr"])
end
it "should run dscl with the supplied cmd /Path args" do
- expect(@provider).to receive(:dscl).with("cmd /Path args")
- @provider.safe_dscl("cmd /Path args")
+ expect(@provider).to receive(:dscl).with(*"cmd /Path args".split(" "))
+ @provider.safe_dscl(*"cmd /Path args".split(" "))
end
describe "with the dscl command returning a non zero exit status for a delete" do
before do
- @status = double("Process::Status", :exitstatus => 1)
+ @status = double("Process::Status", exitstatus: 1)
allow(@provider).to receive(:dscl).and_return(["cmd", @status, "stdout", "stderr"])
end
it "should return an empty string of standard output for a delete" do
- safe_dscl_retval = @provider.safe_dscl("delete /Path args")
+ safe_dscl_retval = @provider.safe_dscl(*"delete /Path args".split(" "))
expect(safe_dscl_retval).to be_a_kind_of(String)
expect(safe_dscl_retval).to eq("")
end
it "should raise an exception for any other command" do
- expect { @provider.safe_dscl("cmd /Path arguments") }.to raise_error(Chef::Exceptions::Group)
+ expect { @provider.safe_dscl(*"cmd /Path arguments".split(" ")) }.to raise_error(Chef::Exceptions::Group)
end
end
@@ -78,13 +80,13 @@ describe Chef::Provider::Group::Dscl do
end
it "should raise an exception" do
- expect { @provider.safe_dscl("cmd /Path arguments") }.to raise_error(Chef::Exceptions::Group)
+ expect { @provider.safe_dscl(*"cmd /Path arguments".split(" ")) }.to raise_error(Chef::Exceptions::Group)
end
end
describe "with the dscl command returning a zero exit status" do
it "should return the third array element, the string of standard output" do
- safe_dscl_retval = @provider.safe_dscl("cmd /Path args")
+ safe_dscl_retval = @provider.safe_dscl(*"cmd /Path args".split(" "))
expect(safe_dscl_retval).to be_a_kind_of(String)
expect(safe_dscl_retval).to eq("stdout")
end
@@ -93,13 +95,12 @@ describe Chef::Provider::Group::Dscl do
describe "get_free_gid" do
before do
- @node = Chef::Node.new
- @provider = Chef::Provider::Group::Dscl.new(@node, @new_resource)
+ @provider = Chef::Provider::Group::Dscl.new(@new_resource, @run_context)
allow(@provider).to receive(:safe_dscl).and_return("\naj 200\njt 201\n")
end
it "should run safe_dscl with list /Groups gid" do
- expect(@provider).to receive(:safe_dscl).with("list /Groups gid")
+ expect(@provider).to receive(:safe_dscl).with(*"list /Groups gid".split(" "))
@provider.get_free_gid
end
@@ -115,13 +116,16 @@ describe Chef::Provider::Group::Dscl do
describe "gid_used?" do
before do
- @node = Chef::Node.new
- @provider = Chef::Provider::Group::Dscl.new(@node, @new_resource)
- allow(@provider).to receive(:safe_dscl).and_return("\naj 500\n")
+ allow(@provider).to receive(:safe_dscl).and_return(<<-eos
+ someprogram somethingElse:gid = (
+ 500
+ )
+ eos
+ )
end
- it "should run safe_dscl with list /Groups gid" do
- expect(@provider).to receive(:safe_dscl).with("list /Groups gid")
+ it "should run safe_dscl with search /Groups gid" do
+ expect(@provider).to receive(:safe_dscl).with(*"search /Groups PrimaryGroupID 500".split(" "))
@provider.gid_used?(500)
end
@@ -130,7 +134,11 @@ describe Chef::Provider::Group::Dscl do
end
it "should return false for an unused gid number" do
- expect(@provider.gid_used?(501)).to be_falsey
+ expect(@provider.gid_used?(0)).to be_falsey
+ expect(@provider.gid_used?(50)).to be_falsey
+ expect(@provider.gid_used?(5000)).to be_falsey
+ expect(@provider.gid_used?(1500)).to be_falsey
+ expect(@provider.gid_used?(18)).to be_falsey
end
it "should return false if not given any valid gid number" do
@@ -171,8 +179,8 @@ describe Chef::Provider::Group::Dscl do
describe "with a valid gid number which is not already in use" do
it "should run safe_dscl with create /Groups/group PrimaryGroupID gid" do
allow(@provider).to receive(:get_free_gid).and_return(50)
- expect(@provider).to receive(:safe_dscl).with("list /Groups gid")
- expect(@provider).to receive(:safe_dscl).with("create /Groups/aj PrimaryGroupID 50").and_return(true)
+ expect(@provider).to receive(:safe_dscl).with(*"search /Groups PrimaryGroupID 50".split(" ")).and_return("")
+ expect(@provider).to receive(:safe_dscl).with("create", "/Groups/aj", "PrimaryGroupID", 50).and_return(true)
@provider.set_gid
end
end
@@ -182,19 +190,19 @@ describe Chef::Provider::Group::Dscl do
describe "with existing members in the current resource and append set to false in the new resource" do
before do
- allow(@new_resource).to receive(:members).and_return([])
- allow(@new_resource).to receive(:append).and_return(false)
+ @new_resource.members([])
+ @new_resource.append(false)
allow(@current_resource).to receive(:members).and_return(%w{all your base})
end
it "should log an appropriate message" do
- expect(Chef::Log).to receive(:debug).with("group[aj] removing group members all your base")
+ expect(logger).to receive(:trace).with("group[aj] removing group members all your base")
@provider.set_members
end
it "should run safe_dscl with create /Groups/group GroupMembership to clear the Group's UID list" do
- expect(@provider).to receive(:safe_dscl).with("create /Groups/aj GroupMembers ''").and_return(true)
- expect(@provider).to receive(:safe_dscl).with("create /Groups/aj GroupMembership ''").and_return(true)
+ expect(@provider).to receive(:safe_dscl).with("create", "/Groups/aj", "GroupMembers", "").and_return(true)
+ expect(@provider).to receive(:safe_dscl).with("create", "/Groups/aj", "GroupMembership", "").and_return(true)
@provider.set_members
end
end
@@ -206,14 +214,14 @@ describe Chef::Provider::Group::Dscl do
end
it "should log an appropriate debug message" do
- expect(Chef::Log).to receive(:debug).with("group[aj] setting group members all, your, base")
+ expect(logger).to receive(:trace).with("group[aj] setting group members all, your, base")
@provider.set_members
end
it "should run safe_dscl with append /Groups/group GroupMembership and group members all, your, base" do
- expect(@provider).to receive(:safe_dscl).with("create /Groups/aj GroupMembers ''").and_return(true)
- expect(@provider).to receive(:safe_dscl).with("append /Groups/aj GroupMembership all your base").and_return(true)
- expect(@provider).to receive(:safe_dscl).with("create /Groups/aj GroupMembership ''").and_return(true)
+ expect(@provider).to receive(:safe_dscl).with("create", "/Groups/aj", "GroupMembers", "").and_return(true)
+ expect(@provider).to receive(:safe_dscl).with(*"append /Groups/aj GroupMembership all your base".split(" ")).and_return(true)
+ expect(@provider).to receive(:safe_dscl).with("create", "/Groups/aj", "GroupMembership", "").and_return(true)
@provider.set_members
end
end
@@ -232,20 +240,20 @@ describe Chef::Provider::Group::Dscl do
end
describe "when loading the current system state" do
- before (:each) do
+ before(:each) do
@provider.action = :create
@provider.load_current_resource
@provider.define_resource_requirements
end
it "raises an error if the required binary /usr/bin/dscl doesn't exist" do
- expect(File).to receive(:exists?).with("/usr/bin/dscl").and_return(false)
+ expect(File).to receive(:exist?).with("/usr/bin/dscl").and_return(false)
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group)
end
it "doesn't raise an error if /usr/bin/dscl exists" do
- allow(File).to receive(:exists?).and_return(true)
+ allow(File).to receive(:exist?).and_return(true)
expect { @provider.process_resource_requirements }.not_to raise_error
end
end
@@ -254,8 +262,8 @@ describe Chef::Provider::Group::Dscl do
it "creates the group, password field, gid, and sets group membership" do
expect(@provider).to receive(:set_gid).and_return(true)
expect(@provider).to receive(:set_members).and_return(true)
- expect(@provider).to receive(:safe_dscl).with("create /Groups/aj Password '*'")
- expect(@provider).to receive(:safe_dscl).with("create /Groups/aj")
+ expect(@provider).to receive(:safe_dscl).with(*"create /Groups/aj Password *".split(" "))
+ expect(@provider).to receive(:safe_dscl).with(*"create /Groups/aj".split(" "))
@provider.create_group
end
end
@@ -265,8 +273,8 @@ describe Chef::Provider::Group::Dscl do
@current_resource.group_name("oldval")
@new_resource.group_name("newname")
expect(@provider).to receive(:set_members).and_return(true)
- expect(@provider).to receive(:safe_dscl).with("create /Groups/newname")
- expect(@provider).to receive(:safe_dscl).with("create /Groups/newname Password '*'")
+ expect(@provider).to receive(:safe_dscl).with(*"create /Groups/newname".split(" "))
+ expect(@provider).to receive(:safe_dscl).with(*"create /Groups/newname Password *".split(" "))
@provider.manage_group
end
@@ -287,7 +295,7 @@ describe Chef::Provider::Group::Dscl do
describe "remove_group" do
it "should run safe_dscl with delete /Groups/group and with the new resources group name" do
- expect(@provider).to receive(:safe_dscl).with("delete /Groups/aj").and_return(true)
+ expect(@provider).to receive(:safe_dscl).with(*"delete /Groups/aj".split(" ")).and_return(true)
@provider.remove_group
end
end
@@ -315,17 +323,17 @@ RecordName: com.apple.aj
RecordType: dsRecTypeStandard:Groups
GroupMembership: waka bar
EOF
- allow(@provider).to receive(:safe_dscl).with("read /Groups/aj").and_return(@output)
+ allow(@provider).to receive(:safe_dscl).with(*"read /Groups/aj".split(" ")).and_return(@output)
@current_resource = @provider.load_current_resource
end
it "should parse gid properly" do
- allow(File).to receive(:exists?).and_return(true)
+ allow(File).to receive(:exist?).and_return(true)
expect(@current_resource.gid).to eq("999")
end
it "should parse members properly" do
- allow(File).to receive(:exists?).and_return(true)
+ allow(File).to receive(:exist?).and_return(true)
expect(@current_resource.members).to eq(%w{waka bar})
end
end
diff --git a/spec/unit/provider/group/gpasswd_spec.rb b/spec/unit/provider/group/gpasswd_spec.rb
index 16e6ad15ea..287951a1a9 100644
--- a/spec/unit/provider/group/gpasswd_spec.rb
+++ b/spec/unit/provider/group/gpasswd_spec.rb
@@ -19,19 +19,22 @@
require "spec_helper"
describe Chef::Provider::Group::Gpasswd, "modify_group_members" do
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+
before do
@node = Chef::Node.new
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
+ allow(@run_context).to receive(:logger).and_return(logger)
@new_resource = Chef::Resource::Group.new("wheel")
@new_resource.members %w{lobster rage fist}
@new_resource.append false
@provider = Chef::Provider::Group::Gpasswd.new(@new_resource, @run_context)
- #@provider.stub(:run_command).and_return(true)
+ # @provider.stub(:run_command).and_return(true)
end
describe "when determining the current group state" do
- before (:each) do
+ before(:each) do
@provider.action = :create
@provider.load_current_resource
@provider.define_resource_requirements
@@ -41,13 +44,13 @@ describe Chef::Provider::Group::Gpasswd, "modify_group_members" do
# for Chef::Provider::Group - no need to repeat it here. We'll
# include only what's specific to this provider.
it "should raise an error if the required binary /usr/bin/gpasswd doesn't exist" do
- allow(File).to receive(:exists?).and_return(true)
- expect(File).to receive(:exists?).with("/usr/bin/gpasswd").and_return(false)
+ allow(File).to receive(:exist?).and_return(true)
+ expect(File).to receive(:exist?).with("/usr/bin/gpasswd").and_return(false)
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group)
end
it "shouldn't raise an error if the required binaries exist" do
- allow(File).to receive(:exists?).and_return(true)
+ allow(File).to receive(:exist?).and_return(true)
expect { @provider.process_resource_requirements }.not_to raise_error
end
end
@@ -65,8 +68,8 @@ describe Chef::Provider::Group::Gpasswd, "modify_group_members" do
end
it "logs a message and sets group's members to 'none'" do
- expect(Chef::Log).to receive(:debug).with("group[wheel] setting group members to: none")
- expect(@provider).to receive(:shell_out!).with("gpasswd -M \"\" wheel")
+ expect(logger).to receive(:trace).with("group[wheel] setting group members to: none")
+ expect(@provider).to receive(:shell_out!).with("gpasswd", "-M", "", "wheel")
@provider.modify_group_members
end
end
@@ -85,13 +88,13 @@ describe Chef::Provider::Group::Gpasswd, "modify_group_members" do
describe "when the resource specifies group members" do
it "should log an appropriate debug message" do
- expect(Chef::Log).to receive(:debug).with("group[wheel] setting group members to: lobster, rage, fist")
+ expect(logger).to receive(:trace).with("group[wheel] setting group members to: lobster, rage, fist")
allow(@provider).to receive(:shell_out!)
@provider.modify_group_members
end
it "should run gpasswd with the members joined by ',' followed by the target group" do
- expect(@provider).to receive(:shell_out!).with("gpasswd -M lobster,rage,fist wheel")
+ expect(@provider).to receive(:shell_out!).with("gpasswd", "-M", "lobster,rage,fist", "wheel")
@provider.modify_group_members
end
@@ -104,9 +107,9 @@ describe Chef::Provider::Group::Gpasswd, "modify_group_members" do
it "should run gpasswd individually for each user when the append option is set" do
@new_resource.append(true)
- expect(@provider).to receive(:shell_out!).with("gpasswd -a lobster wheel")
- expect(@provider).to receive(:shell_out!).with("gpasswd -a rage wheel")
- expect(@provider).to receive(:shell_out!).with("gpasswd -a fist wheel")
+ expect(@provider).to receive(:shell_out!).with("gpasswd", "-a", "lobster", "wheel")
+ expect(@provider).to receive(:shell_out!).with("gpasswd", "-a", "rage", "wheel")
+ expect(@provider).to receive(:shell_out!).with("gpasswd", "-a", "fist", "wheel")
@provider.modify_group_members
end
end
diff --git a/spec/unit/provider/group/groupadd_spec.rb b/spec/unit/provider/group/groupadd_spec.rb
index 93159dd16e..ded1bc76f1 100644
--- a/spec/unit/provider/group/groupadd_spec.rb
+++ b/spec/unit/provider/group/groupadd_spec.rb
@@ -18,156 +18,176 @@
require "spec_helper"
-describe Chef::Provider::Group::Groupadd, "set_options" do
- before do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::Group.new("aj")
- @new_resource.gid(50)
- @new_resource.members(%w{root aj})
- @new_resource.system false
- @new_resource.non_unique false
- @current_resource = Chef::Resource::Group.new("aj")
- @current_resource.gid(50)
- @current_resource.members(%w{root aj})
- @current_resource.system false
- @current_resource.non_unique false
- @provider = Chef::Provider::Group::Groupadd.new(@new_resource, @run_context)
- @provider.current_resource = @current_resource
- end
-
- field_list = {
- :gid => "-g",
- }
-
- field_list.each do |attribute, option|
- it "should check for differences in #{attribute} between the current and new resources" do
- expect(@new_resource).to receive(attribute)
- expect(@current_resource).to receive(attribute)
- @provider.set_options
+describe Chef::Provider::Group::Groupadd do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:new_resource) do
+ Chef::Resource::Group.new("aj").tap do |r|
+ r.gid 50
+ r.members %w{root aj}
+ r.system false
+ r.non_unique false
end
- it "should set the option for #{attribute} if the new resources #{attribute} is not null" do
- allow(@new_resource).to receive(attribute).and_return("wowaweea")
- expect(@provider.set_options).to eql(" #{option} '#{@new_resource.send(attribute)}' #{@new_resource.group_name}")
+ end
+ let(:current_resource) do
+ Chef::Resource::Group.new("aj").tap do |r|
+ r.gid 50
+ r.members %w{root aj}
+ r.system false
+ r.non_unique false
end
end
-
- it "should combine all the possible options" do
- match_string = ""
- field_list.sort { |a, b| a[0] <=> b[0] }.each do |attribute, option|
- allow(@new_resource).to receive(attribute).and_return("hola")
- match_string << " #{option} 'hola'"
+ let(:provider) do
+ described_class.new(new_resource, run_context).tap do |p|
+ p.current_resource = current_resource
end
- match_string << " aj"
- expect(@provider.set_options).to eql(match_string)
end
- describe "when we want to create a system group" do
- it "should not set groupadd_options '-r' when system is false" do
- @new_resource.system(false)
- expect(@provider.groupadd_options).not_to match(/-r/)
+ describe "#set_options" do
+ field_list = {
+ gid: "-g",
+ }
+
+ field_list.each do |attribute, option|
+ it "should check for differences in #{attribute} between the current and new resources" do
+ expect(new_resource).to receive(attribute)
+ expect(current_resource).to receive(attribute)
+ provider.set_options
+ end
+
+ it "should set the option for #{attribute} if the new resources #{attribute} is not null" do
+ allow(new_resource).to receive(attribute).and_return("wowaweea")
+ expect(provider.set_options).to eql([ option, new_resource.send(attribute), new_resource.group_name])
+ end
end
- it "should set groupadd -r if system is true" do
- @new_resource.system(true)
- expect(@provider.groupadd_options).to eq(" -r")
+ it "should combine all the possible options" do
+ match_array = []
+ field_list.sort_by { |a| a[0] }.each do |attribute, option|
+ allow(new_resource).to receive(attribute).and_return("hola")
+ match_array << option
+ match_array << "hola"
+ end
+ match_array << "aj"
+ expect(provider.set_options).to eql(match_array)
end
- end
- describe "when we want to create a non_unique gid group" do
- it "should not set groupadd_options '-o' when non_unique is false" do
- @new_resource.non_unique(false)
- expect(@provider.groupadd_options).not_to match(/-o/)
+ describe "when we want to create a system group" do
+ it "should not set groupadd_options '-r' when system is false" do
+ new_resource.system(false)
+ expect(provider.groupadd_options).to eq([])
+ end
+
+ it "should set groupadd -r if system is true" do
+ new_resource.system(true)
+ expect(provider.groupadd_options).to eq(["-r"])
+ end
+
+ context "on Solaris" do
+ before { node.automatic["platform"] = "solaris2" }
+ it "should not set groupadd -r if system is true" do
+ new_resource.system(true)
+ expect(provider.groupadd_options).to eql([])
+ end
+ end
end
- it "should set groupadd -o if non_unique is true" do
- @new_resource.non_unique(true)
- expect(@provider.groupadd_options).to eq(" -o")
- end
- end
-end
+ describe "when we want to create a non_unique gid group" do
+ it "should not set groupadd_options '-o' when non_unique is false" do
+ new_resource.non_unique(false)
+ expect(provider.groupadd_options).to eq([])
+ end
-describe Chef::Provider::Group::Groupadd, "create_group" do
- before do
- @node = Chef::Node.new
- @new_resource = Chef::Resource::Group.new("aj")
- @provider = Chef::Provider::Group::Groupadd.new(@node, @new_resource)
- allow(@provider).to receive(:run_command).and_return(true)
- allow(@provider).to receive(:set_options).and_return(" monkey")
- allow(@provider).to receive(:groupadd_options).and_return("")
- allow(@provider).to receive(:modify_group_members).and_return(true)
+ it "should set groupadd -o if non_unique is true" do
+ new_resource.non_unique(true)
+ expect(provider.groupadd_options).to eq(["-o"])
+ end
+ end
end
- it "should run groupadd with the return of set_options" do
- expect(@provider).to receive(:run_command).with({ :command => "groupadd monkey" }).and_return(true)
- @provider.create_group
- end
+ describe "#create_group" do
+ before do
+ allow(provider).to receive(:shell_out!).and_return(true)
+ allow(provider).to receive(:set_options).and_return("monkey")
+ allow(provider).to receive(:groupadd_options).and_return([])
+ allow(provider).to receive(:modify_group_members).and_return(true)
+ end
- it "should modify the group members" do
- expect(@provider).to receive(:modify_group_members).and_return(true)
- @provider.create_group
- end
-end
+ it "should run groupadd with the return of set_options" do
+ expect(provider).to receive(:shell_out!).with("groupadd", "monkey").and_return(true)
+ provider.create_group
+ end
-describe Chef::Provider::Group::Groupadd do
- before do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::Group.new("aj")
- @provider = Chef::Provider::Group::Groupadd.new(@new_resource, @run_context)
- allow(@provider).to receive(:run_command).and_return(true)
- allow(@provider).to receive(:set_options).and_return(" monkey")
+ it "should modify the group members" do
+ expect(provider).to receive(:modify_group_members).and_return(true)
+ provider.create_group
+ end
end
- describe "manage group" do
+ describe "#manage_group" do
+ before do
+ allow(provider).to receive(:shell_out!).and_return(true)
+ allow(provider).to receive(:set_options).and_return("monkey")
+ end
it "should run groupmod with the return of set_options" do
- allow(@provider).to receive(:modify_group_members).and_return(true)
- expect(@provider).to receive(:run_command).with({ :command => "groupmod monkey" }).and_return(true)
- @provider.manage_group
+ allow(provider).to receive(:modify_group_members).and_return(true)
+ expect(provider).to receive(:shell_out!).with("groupmod", "monkey").and_return(true)
+ provider.manage_group
end
it "should modify the group members" do
- expect(@provider).to receive(:modify_group_members).and_return(true)
- @provider.manage_group
+ expect(provider).to receive(:modify_group_members).and_return(true)
+ provider.manage_group
end
end
- describe "remove_group" do
+ describe "#remove_group" do
+ before do
+ allow(provider).to receive(:shell_out!).and_return(true)
+ allow(provider).to receive(:set_options).and_return("monkey")
+ end
it "should run groupdel with the new resources group name" do
- expect(@provider).to receive(:run_command).with({ :command => "groupdel aj" }).and_return(true)
- @provider.remove_group
+ expect(provider).to receive(:shell_out!).with("groupdel", "aj").and_return(true)
+ provider.remove_group
end
end
[:add_member, :remove_member, :set_members].each do |m|
it "should raise an error when calling #{m}" do
- expect { @provider.send(m, [ ]) }.to raise_error(Chef::Exceptions::Group, "you must override #{m} in #{@provider}")
+ expect { provider.send(m, [ ]) }.to raise_error(Chef::Exceptions::Group, "you must override #{m} in #{provider}")
end
end
- describe "load_current_resource" do
+ describe "#load_current_resource" do
+ before do
+ allow(provider).to receive(:shell_out!).and_return(true)
+ allow(provider).to receive(:set_options).and_return("monkey")
+ end
+
before do
- allow(File).to receive(:exists?).and_return(false)
- @provider.define_resource_requirements
+ allow(File).to receive(:exist?).and_return(false)
+ provider.define_resource_requirements
end
+
it "should raise an error if the required binary /usr/sbin/groupadd doesn't exist" do
- expect(File).to receive(:exists?).with("/usr/sbin/groupadd").and_return(false)
- expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group)
+ expect(File).to receive(:exist?).with("/usr/sbin/groupadd").and_return(false)
+ expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group)
end
+
it "should raise an error if the required binary /usr/sbin/groupmod doesn't exist" do
- expect(File).to receive(:exists?).with("/usr/sbin/groupadd").and_return(true)
- expect(File).to receive(:exists?).with("/usr/sbin/groupmod").and_return(false)
- expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group)
+ expect(File).to receive(:exist?).with("/usr/sbin/groupadd").and_return(true)
+ expect(File).to receive(:exist?).with("/usr/sbin/groupmod").and_return(false)
+ expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group)
end
+
it "should raise an error if the required binary /usr/sbin/groupdel doesn't exist" do
- expect(File).to receive(:exists?).with("/usr/sbin/groupadd").and_return(true)
- expect(File).to receive(:exists?).with("/usr/sbin/groupmod").and_return(true)
- expect(File).to receive(:exists?).with("/usr/sbin/groupdel").and_return(false)
- expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group)
+ expect(File).to receive(:exist?).with("/usr/sbin/groupadd").and_return(true)
+ expect(File).to receive(:exist?).with("/usr/sbin/groupmod").and_return(true)
+ expect(File).to receive(:exist?).with("/usr/sbin/groupdel").and_return(false)
+ expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group)
end
end
diff --git a/spec/unit/provider/group/groupmod_spec.rb b/spec/unit/provider/group/groupmod_spec.rb
index 21d026409e..3f36a5b8a6 100644
--- a/spec/unit/provider/group/groupmod_spec.rb
+++ b/spec/unit/provider/group/groupmod_spec.rb
@@ -19,10 +19,13 @@
require "spec_helper"
describe Chef::Provider::Group::Groupmod do
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+
before do
@node = Chef::Node.new
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
+ allow(@run_context).to receive(:logger).and_return(logger)
@new_resource = Chef::Resource::Group.new("wheel")
@new_resource.gid 123
@new_resource.members %w{lobster rage fist}
@@ -33,17 +36,17 @@ describe Chef::Provider::Group::Groupmod do
describe "manage_group" do
describe "when determining the current group state" do
it "should raise an error if the required binary /usr/sbin/group doesn't exist" do
- expect(File).to receive(:exists?).with("/usr/sbin/group").and_return(false)
+ expect(File).to receive(:exist?).with("/usr/sbin/group").and_return(false)
expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Group)
end
it "should raise an error if the required binary /usr/sbin/user doesn't exist" do
- expect(File).to receive(:exists?).with("/usr/sbin/group").and_return(true)
- expect(File).to receive(:exists?).with("/usr/sbin/user").and_return(false)
+ expect(File).to receive(:exist?).with("/usr/sbin/group").and_return(true)
+ expect(File).to receive(:exist?).with("/usr/sbin/user").and_return(false)
expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Group)
end
it "shouldn't raise an error if the required binaries exist" do
- allow(File).to receive(:exists?).and_return(true)
+ allow(File).to receive(:exist?).and_return(true)
expect { @provider.load_current_resource }.not_to raise_error
end
end
@@ -61,10 +64,10 @@ describe Chef::Provider::Group::Groupmod do
end
it "logs a message and sets group's members to 'none', then removes existing group members" do
- expect(Chef::Log).to receive(:debug).with("group[wheel] setting group members to: none")
- expect(@provider).to receive(:shell_out!).with("group mod -n wheel_bak wheel")
- expect(@provider).to receive(:shell_out!).with("group add -g '123' -o wheel")
- expect(@provider).to receive(:shell_out!).with("group del wheel_bak")
+ expect(logger).to receive(:trace).with("group[wheel] setting group members to: none")
+ expect(@provider).to receive(:shell_out!).with("group", "mod", "-n", "wheel_bak", "wheel")
+ expect(@provider).to receive(:shell_out!).with("group", "add", "-g", "123", "-o", "wheel")
+ expect(@provider).to receive(:shell_out!).with("group", "del", "wheel_bak")
@provider.manage_group
end
end
@@ -76,7 +79,7 @@ describe Chef::Provider::Group::Groupmod do
end
it "logs a message and does not modify group membership" do
- expect(Chef::Log).to receive(:debug).with("group[wheel] not changing group members, the group has no members to add")
+ expect(logger).to receive(:trace).with("group[wheel] not changing group members, the group has no members to add")
expect(@provider).not_to receive(:shell_out!)
@provider.manage_group
end
@@ -89,11 +92,11 @@ describe Chef::Provider::Group::Groupmod do
end
it "updates group membership correctly" do
- allow(Chef::Log).to receive(:debug)
- expect(@provider).to receive(:shell_out!).with("group mod -n wheel_bak wheel")
- expect(@provider).to receive(:shell_out!).with("user mod -G wheel lobster")
- expect(@provider).to receive(:shell_out!).with("group add -g '123' -o wheel")
- expect(@provider).to receive(:shell_out!).with("group del wheel_bak")
+ allow(logger).to receive(:trace)
+ expect(@provider).to receive(:shell_out!).with("group", "mod", "-n", "wheel_bak", "wheel")
+ expect(@provider).to receive(:shell_out!).with("user", "mod", "-G", "wheel", "lobster")
+ expect(@provider).to receive(:shell_out!).with("group", "add", "-g", "123", "-o", "wheel")
+ expect(@provider).to receive(:shell_out!).with("group", "del", "wheel_bak")
@provider.manage_group
end
end
@@ -108,10 +111,10 @@ describe Chef::Provider::Group::Groupmod do
end
it "should run a group add command and some user mod commands" do
- expect(@provider).to receive(:shell_out!).with("group add -g '123' wheel")
- expect(@provider).to receive(:shell_out!).with("user mod -G wheel lobster")
- expect(@provider).to receive(:shell_out!).with("user mod -G wheel rage")
- expect(@provider).to receive(:shell_out!).with("user mod -G wheel fist")
+ expect(@provider).to receive(:shell_out!).with("group", "add", "-g", "123", "wheel")
+ expect(@provider).to receive(:shell_out!).with("user", "mod", "-G", "wheel", "lobster")
+ expect(@provider).to receive(:shell_out!).with("user", "mod", "-G", "wheel", "rage")
+ expect(@provider).to receive(:shell_out!).with("user", "mod", "-G", "wheel", "fist")
@provider.create_group
end
end
@@ -125,7 +128,7 @@ describe Chef::Provider::Group::Groupmod do
end
it "should run a group del command" do
- expect(@provider).to receive(:shell_out!).with("group del wheel")
+ expect(@provider).to receive(:shell_out!).with("group", "del", "wheel")
@provider.remove_group
end
end
diff --git a/spec/unit/provider/group/pw_spec.rb b/spec/unit/provider/group/pw_spec.rb
index 97ffcb3c31..736ba0671b 100644
--- a/spec/unit/provider/group/pw_spec.rb
+++ b/spec/unit/provider/group/pw_spec.rb
@@ -19,10 +19,13 @@
require "spec_helper"
describe Chef::Provider::Group::Pw do
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+
before do
@node = Chef::Node.new
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
+ allow(@run_context).to receive(:logger).and_return(logger)
@new_resource = Chef::Resource::Group.new("wheel")
@new_resource.gid 50
@@ -37,19 +40,19 @@ describe Chef::Provider::Group::Pw do
describe "when setting options for the pw command" do
it "does not set the gid option if gids match or are unmanaged" do
- expect(@provider.set_options).to eq(" wheel")
+ expect(@provider.set_options).to eq(["wheel"])
end
it "sets the option for gid if it is not nil" do
@new_resource.gid(42)
- expect(@provider.set_options).to eql(" wheel -g '42'")
+ expect(@provider.set_options).to eql(["wheel", "-g", 42])
end
end
describe "when creating a group" do
it "should run pw groupadd with the return of set_options and set_members_option" do
@new_resource.gid(23)
- expect(@provider).to receive(:run_command).with({ :command => "pw groupadd wheel -g '23' -M root,aj" }).and_return(true)
+ expect(@provider).to receive(:shell_out!).with("pw", "groupadd", "wheel", "-g", "23", "-M", "root,aj").and_return(true)
@provider.create_group
end
end
@@ -59,8 +62,8 @@ describe Chef::Provider::Group::Pw do
it "should run pw groupmod with the return of set_options" do
@new_resource.gid(42)
@new_resource.members(["someone"])
- expect(@provider).to receive(:run_command).with({ :command => "pw groupmod wheel -g '42' -m someone" }).and_return(true)
- expect(@provider).to receive(:run_command).with({ :command => "pw groupmod wheel -g '42' -d root,aj" }).and_return(true)
+ expect(@provider).to receive(:shell_out!).with("pw", "groupmod", "wheel", "-g", "42", "-m", "someone").and_return(true)
+ expect(@provider).to receive(:shell_out!).with("pw", "groupmod", "wheel", "-g", "42", "-d", "root,aj").and_return(true)
@provider.manage_group
end
@@ -68,7 +71,7 @@ describe Chef::Provider::Group::Pw do
describe "when removing the group" do
it "should run pw groupdel with the new resources group name" do
- expect(@provider).to receive(:run_command).with({ :command => "pw groupdel wheel" }).and_return(true)
+ expect(@provider).to receive(:shell_out!).with("pw", "groupdel", "wheel").and_return(true)
@provider.remove_group
end
end
@@ -77,7 +80,7 @@ describe Chef::Provider::Group::Pw do
describe "with an empty members array in both the new and current resource" do
before do
- allow(@new_resource).to receive(:members).and_return([])
+ @new_resource.members([])
allow(@current_resource).to receive(:members).and_return([])
end
@@ -88,50 +91,50 @@ describe Chef::Provider::Group::Pw do
describe "with an empty members array in the new resource and existing members in the current resource" do
before do
- allow(@new_resource).to receive(:members).and_return([])
+ @new_resource.members([])
allow(@current_resource).to receive(:members).and_return(%w{all your base})
end
it "should log an appropriate message" do
- expect(Chef::Log).to receive(:debug).with("group[wheel] removing group members: all,your,base")
+ expect(logger).to receive(:trace).with("group[wheel] removing group members: all,your,base")
@provider.set_members_options
end
it "should set the -d option with the members joined by ','" do
- expect(@provider.set_members_options).to eql([ " -d all,your,base" ])
+ expect(@provider.set_members_options).to eql([ ["-d", "all,your,base"] ])
end
end
describe "with supplied members array in the new resource and an empty members array in the current resource" do
before do
- allow(@new_resource).to receive(:members).and_return(%w{all your base})
+ @new_resource.members(%w{all your base})
allow(@current_resource).to receive(:members).and_return([])
end
it "should log an appropriate debug message" do
- expect(Chef::Log).to receive(:debug).with("group[wheel] adding group members: all,your,base")
+ expect(logger).to receive(:trace).with("group[wheel] adding group members: all,your,base")
@provider.set_members_options
end
it "should set the -m option with the members joined by ','" do
- expect(@provider.set_members_options).to eql([ " -m all,your,base" ])
+ expect(@provider.set_members_options).to eql([[ "-m", "all,your,base" ]])
end
end
end
describe "load_current_resource" do
- before (:each) do
+ before(:each) do
@provider.action = :create
@provider.load_current_resource
@provider.define_resource_requirements
end
it "should raise an error if the required binary /usr/sbin/pw doesn't exist" do
- expect(File).to receive(:exists?).with("/usr/sbin/pw").and_return(false)
+ expect(File).to receive(:exist?).with("/usr/sbin/pw").and_return(false)
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group)
end
it "shouldn't raise an error if /usr/sbin/pw exists" do
- allow(File).to receive(:exists?).and_return(true)
+ allow(File).to receive(:exist?).and_return(true)
expect { @provider.process_resource_requirements }.not_to raise_error
end
end
diff --git a/spec/unit/provider/group/suse_spec.rb b/spec/unit/provider/group/suse_spec.rb
new file mode 100644
index 0000000000..e61d865b6d
--- /dev/null
+++ b/spec/unit/provider/group/suse_spec.rb
@@ -0,0 +1,90 @@
+#
+# Author:: Tom Duffield (<tom@chef.io>)
+# Copyright:: Copyright 2016 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Provider::Group::Suse do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:new_members) { %w{root new_user} }
+ let(:new_resource) do
+ Chef::Resource::Group.new("new_group").tap do |r|
+ r.gid 50
+ r.members new_members
+ r.system false
+ r.non_unique false
+ end
+ end
+ let(:current_resource) do
+ Chef::Resource::Group.new("new_group").tap do |r|
+ r.gid 50
+ r.members %w{root}
+ r.system false
+ r.non_unique false
+ end
+ end
+ let(:provider) do
+ described_class.new(new_resource, run_context).tap do |p|
+ p.current_resource = current_resource
+ end
+ end
+
+ describe "when determining the current group state" do
+ before(:each) do
+ allow(File).to receive(:exist?).and_return(true)
+ provider.action = :create
+ provider.define_resource_requirements
+ end
+
+ # Checking for required binaries is already done in the spec
+ # for Chef::Provider::Group - no need to repeat it here. We'll
+ # include only what's specific to this provider.
+ it "should raise an error if the required binary /usr/sbin/groupmod doesn't exist" do
+ expect(File).to receive(:exist?).with("/usr/sbin/groupmod").and_return(false)
+ expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group)
+ end
+
+ it "should raise error if one of the member users does not exist" do
+ expect(Etc).to receive(:getpwnam).with("new_user").and_raise ArgumentError
+ expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group)
+ end
+ end
+
+ describe "#set_members" do
+ it "should add missing members and remove deleted members" do
+ expect(provider).not_to receive(:remove_member)
+ expect(provider).to receive(:add_member).with("new_user")
+ provider.set_members(new_members)
+ end
+ end
+
+ describe "#add_member" do
+ it "should call out to groupmod to add user" do
+ expect(provider).to receive(:shell_out!).with("groupmod", "-A", "new_user", "new_group")
+ provider.add_member("new_user")
+ end
+ end
+
+ describe "#remove_member" do
+ it "should call out to groupmod to remove user" do
+ expect(provider).to receive(:shell_out!).with("groupmod", "-R", "new_user", "new_group")
+ provider.remove_member("new_user")
+ end
+ end
+end
diff --git a/spec/unit/provider/group/usermod_spec.rb b/spec/unit/provider/group/usermod_spec.rb
index 7bd45c4f1b..da2c20b7da 100644
--- a/spec/unit/provider/group/usermod_spec.rb
+++ b/spec/unit/provider/group/usermod_spec.rb
@@ -34,8 +34,8 @@ describe Chef::Provider::Group::Usermod do
describe "with an empty members array" do
before do
- allow(@new_resource).to receive(:append).and_return(true)
- allow(@new_resource).to receive(:members).and_return([])
+ @new_resource.append(true)
+ @new_resource.members([])
end
it "should log an appropriate message" do
@@ -46,18 +46,18 @@ describe Chef::Provider::Group::Usermod do
describe "with supplied members" do
platforms = {
- "openbsd" => "-G",
- "netbsd" => "-G",
- "solaris" => "-a -G",
- "suse" => "-a -G",
- "opensuse" => "-a -G",
- "smartos" => "-G",
- "omnios" => "-G",
+ "openbsd" => [ "-G" ],
+ "netbsd" => [ "-G" ],
+ "solaris" => [ "-a", "-G" ],
+ "suse" => [ "-a", "-G" ],
+ "opensuse" => [ "-a", "-G" ],
+ "smartos" => [ "-G" ],
+ "omnios" => [ "-G" ],
}
before do
- allow(@new_resource).to receive(:members).and_return(%w{all your base})
- allow(File).to receive(:exists?).and_return(true)
+ @new_resource.members(%w{all your base})
+ allow(File).to receive(:exist?).and_return(true)
end
it "should raise an error when setting the entire group directly" do
@@ -73,8 +73,8 @@ describe Chef::Provider::Group::Usermod do
@provider.load_current_resource
@provider.instance_variable_set("@group_exists", true)
@provider.action = :modify
- allow(@new_resource).to receive(:append).and_return(true)
- allow(@new_resource).to receive(:excluded_members).and_return(["someone"])
+ @new_resource.append(true)
+ @new_resource.excluded_members(["someone"])
expect { @provider.run_action(@provider.process_resource_requirements) }.to raise_error(Chef::Exceptions::Group, "excluded_members is not supported by #{@provider}")
end
@@ -84,10 +84,10 @@ describe Chef::Provider::Group::Usermod do
current_resource.members([ ])
@provider.current_resource = current_resource
@node.automatic_attrs[:platform] = platform
- allow(@new_resource).to receive(:append).and_return(true)
- expect(@provider).to receive(:shell_out!).with("usermod #{flags} wheel all")
- expect(@provider).to receive(:shell_out!).with("usermod #{flags} wheel your")
- expect(@provider).to receive(:shell_out!).with("usermod #{flags} wheel base")
+ @new_resource.append(true)
+ expect(@provider).to receive(:shell_out!).with("usermod", *flags, "wheel", "all")
+ expect(@provider).to receive(:shell_out!).with("usermod", *flags, "wheel", "your")
+ expect(@provider).to receive(:shell_out!).with("usermod", *flags, "wheel", "base")
@provider.modify_group_members
end
end
@@ -96,19 +96,19 @@ describe Chef::Provider::Group::Usermod do
describe "when loading the current resource" do
before(:each) do
- allow(File).to receive(:exists?).and_return(false)
+ allow(File).to receive(:exist?).and_return(false)
@provider.action = :create
@provider.define_resource_requirements
end
it "should raise an error if the required binary /usr/sbin/usermod doesn't exist" do
- allow(File).to receive(:exists?).and_return(true)
- expect(File).to receive(:exists?).with("/usr/sbin/usermod").and_return(false)
+ allow(File).to receive(:exist?).and_return(true)
+ expect(File).to receive(:exist?).with("/usr/sbin/usermod").and_return(false)
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group)
end
it "shouldn't raise an error if the required binaries exist" do
- allow(File).to receive(:exists?).and_return(true)
+ allow(File).to receive(:exist?).and_return(true)
expect { @provider.process_resource_requirements }.not_to raise_error
end
end
diff --git a/spec/unit/provider/group/windows_spec.rb b/spec/unit/provider/group/windows_spec.rb
index f059f2e717..51cd3c58d6 100644
--- a/spec/unit/provider/group/windows_spec.rb
+++ b/spec/unit/provider/group/windows_spec.rb
@@ -62,19 +62,19 @@ describe Chef::Provider::Group::Windows do
end
it "should call @net_group.local_set_members" do
- allow(@new_resource).to receive(:append).and_return(false)
+ @new_resource.append(false)
expect(@net_group).to receive(:local_set_members).with(@new_resource.members)
@provider.manage_group
end
it "should call @net_group.local_add_members" do
- allow(@new_resource).to receive(:append).and_return(true)
+ @new_resource.append(true)
expect(@net_group).to receive(:local_add_members).with(@new_resource.members)
@provider.manage_group
end
it "should call @net_group.local_delete_members" do
- allow(@new_resource).to receive(:append).and_return(true)
+ @new_resource.append(true)
allow(@provider).to receive(:lookup_account_name).with("all").and_return("all")
expect(@net_group).to receive(:local_delete_members).with(@new_resource.excluded_members)
@provider.manage_group
diff --git a/spec/unit/provider/group_spec.rb b/spec/unit/provider/group_spec.rb
index 1d354ea32e..c3e0d4b345 100644
--- a/spec/unit/provider/group_spec.rb
+++ b/spec/unit/provider/group_spec.rb
@@ -103,26 +103,26 @@ describe Chef::Provider::User do
it "should return false if append is true and the group member(s) already exists" do
@current_resource.members << "extra_user"
- allow(@new_resource).to receive(:append).and_return(true)
+ @new_resource.append(true)
expect(@provider.compare_group).to be_falsey
end
it "should return true if append is true and the group member(s) do not already exist" do
@new_resource.members << "extra_user"
- allow(@new_resource).to receive(:append).and_return(true)
+ @new_resource.append(true)
expect(@provider.compare_group).to be_truthy
end
it "should return false if append is true and excluded_members include a non existing member" do
@new_resource.excluded_members << "extra_user"
- allow(@new_resource).to receive(:append).and_return(true)
+ @new_resource.append(true)
expect(@provider.compare_group).to be_falsey
end
it "should return true if the append is true and excluded_members include an existing user" do
@new_resource.members.each { |m| @new_resource.excluded_members << m }
@new_resource.members.clear
- allow(@new_resource).to receive(:append).and_return(true)
+ @new_resource.append(true)
expect(@provider.compare_group).to be_truthy
end
diff --git a/spec/unit/provider/http_request_spec.rb b/spec/unit/provider/http_request_spec.rb
index 9a3519a95a..8b5ae02b3c 100644
--- a/spec/unit/provider/http_request_spec.rb
+++ b/spec/unit/provider/http_request_spec.rb
@@ -73,7 +73,7 @@ describe Chef::Provider::HttpRequest do
end
it "should inflate a message block at runtime" do
- allow(@new_resource).to receive(:message).and_return(lambda { "return" })
+ @new_resource.message(lambda { "return" })
expect(@http).to receive(:put).with("http://www.opscode.com/", "return", {})
@provider.run_action(:put)
expect(@new_resource).to be_updated
diff --git a/spec/unit/provider/ifconfig/aix_spec.rb b/spec/unit/provider/ifconfig/aix_spec.rb
index 7847fc9a5f..7f316c952b 100644
--- a/spec/unit/provider/ifconfig/aix_spec.rb
+++ b/spec/unit/provider/ifconfig/aix_spec.rb
@@ -48,7 +48,7 @@ IFCONFIG
describe "#load_current_resource" do
before do
- @status = double(:stdout => @ifconfig_output, :exitstatus => 0)
+ @status = double(stdout: @ifconfig_output, exitstatus: 0)
allow(@provider).to receive(:shell_out).and_return(@status)
@new_resource.device "en0"
end
@@ -68,11 +68,11 @@ IFCONFIG
it "should add an interface if it does not exist" do
@new_resource.device "en10"
allow(@provider).to receive(:load_current_resource) do
- @provider.instance_variable_set("@status", double("Status", :exitstatus => 0))
+ @provider.instance_variable_set("@status", double("Status", exitstatus: 0))
@provider.instance_variable_set("@current_resource", Chef::Resource::Ifconfig.new("10.0.0.1", @run_context))
end
command = "chdev -l #{@new_resource.device} -a netaddr=#{@new_resource.name}"
- expect(@provider).to receive(:run_command).with(:command => command)
+ expect(@provider).to receive(:shell_out!).with(*command.split(" "))
@provider.run_action(:add)
expect(@new_resource).to be_updated
@@ -82,7 +82,7 @@ IFCONFIG
@new_resource.device "en0"
@new_resource.metric "1"
allow(@provider).to receive(:load_current_resource) do
- @provider.instance_variable_set("@status", double("Status", :exitstatus => 0))
+ @provider.instance_variable_set("@status", double("Status", exitstatus: 0))
@provider.instance_variable_set("@current_resource", Chef::Resource::Ifconfig.new("10.0.0.1", @run_context))
end
@@ -94,11 +94,11 @@ IFCONFIG
it "should enable an interface if it does not exist" do
@new_resource.device "en10"
allow(@provider).to receive(:load_current_resource) do
- @provider.instance_variable_set("@status", double("Status", :exitstatus => 0))
+ @provider.instance_variable_set("@status", double("Status", exitstatus: 0))
@provider.instance_variable_set("@current_resource", Chef::Resource::Ifconfig.new("10.0.0.1", @run_context))
end
command = "ifconfig #{@new_resource.device} #{@new_resource.name}"
- expect(@provider).to receive(:run_command).with(:command => command)
+ expect(@provider).to receive(:shell_out!).with(*command.split(" "))
@provider.run_action(:enable)
expect(@new_resource).to be_updated
@@ -110,11 +110,11 @@ IFCONFIG
it "should not disable an interface if it does not exist" do
@new_resource.device "en10"
allow(@provider).to receive(:load_current_resource) do
- @provider.instance_variable_set("@status", double("Status", :exitstatus => 0))
+ @provider.instance_variable_set("@status", double("Status", exitstatus: 0))
@provider.instance_variable_set("@current_resource", Chef::Resource::Ifconfig.new("10.0.0.1", @run_context))
end
- expect(@provider).not_to receive(:run_command)
+ expect(@provider).not_to receive(:shell_out!)
@provider.run_action(:disable)
expect(@new_resource).not_to be_updated
@@ -124,7 +124,7 @@ IFCONFIG
before do
@new_resource.device "en10"
allow(@provider).to receive(:load_current_resource) do
- @provider.instance_variable_set("@status", double("Status", :exitstatus => 0))
+ @provider.instance_variable_set("@status", double("Status", exitstatus: 0))
current_resource = Chef::Resource::Ifconfig.new("10.0.0.1", @run_context)
current_resource.device @new_resource.device
@provider.instance_variable_set("@current_resource", current_resource)
@@ -133,7 +133,7 @@ IFCONFIG
it "should disable an interface if it exists" do
command = "ifconfig #{@new_resource.device} down"
- expect(@provider).to receive(:run_command).with(:command => command)
+ expect(@provider).to receive(:shell_out!).with(*command.split(" "))
@provider.run_action(:disable)
expect(@new_resource).to be_updated
@@ -147,11 +147,11 @@ IFCONFIG
it "should not delete an interface if it does not exist" do
@new_resource.device "en10"
allow(@provider).to receive(:load_current_resource) do
- @provider.instance_variable_set("@status", double("Status", :exitstatus => 0))
+ @provider.instance_variable_set("@status", double("Status", exitstatus: 0))
@provider.instance_variable_set("@current_resource", Chef::Resource::Ifconfig.new("10.0.0.1", @run_context))
end
- expect(@provider).not_to receive(:run_command)
+ expect(@provider).not_to receive(:shell_out!)
@provider.run_action(:delete)
expect(@new_resource).not_to be_updated
@@ -161,7 +161,7 @@ IFCONFIG
before do
@new_resource.device "en10"
allow(@provider).to receive(:load_current_resource) do
- @provider.instance_variable_set("@status", double("Status", :exitstatus => 0))
+ @provider.instance_variable_set("@status", double("Status", exitstatus: 0))
current_resource = Chef::Resource::Ifconfig.new("10.0.0.1", @run_context)
current_resource.device @new_resource.device
@provider.instance_variable_set("@current_resource", current_resource)
@@ -170,7 +170,7 @@ IFCONFIG
it "should delete an interface if it exists" do
command = "chdev -l #{@new_resource.device} -a state=down"
- expect(@provider).to receive(:run_command).with(:command => command)
+ expect(@provider).to receive(:shell_out!).with(*command.split(" "))
@provider.run_action(:delete)
expect(@new_resource).to be_updated
diff --git a/spec/unit/provider/ifconfig/debian_spec.rb b/spec/unit/provider/ifconfig/debian_spec.rb
index 1f61957721..9a90dc1e0a 100644
--- a/spec/unit/provider/ifconfig/debian_spec.rb
+++ b/spec/unit/provider/ifconfig/debian_spec.rb
@@ -40,12 +40,12 @@ describe Chef::Provider::Ifconfig::Debian do
let(:current_resource) { Chef::Resource::Ifconfig.new("10.0.0.1", run_context) }
let(:provider) do
- status = double("Status", :exitstatus => 0)
+ status = double("Status", exitstatus: 0)
provider = Chef::Provider::Ifconfig::Debian.new(new_resource, run_context)
provider.instance_variable_set("@status", status)
provider.current_resource = current_resource
allow(provider).to receive(:load_current_resource)
- allow(provider).to receive(:run_command)
+ allow(provider).to receive(:shell_out!)
provider
end
@@ -77,12 +77,12 @@ describe Chef::Provider::Ifconfig::Debian do
context "when the interface_dot_d directory does not exist" do
before do
FileUtils.rmdir tempdir_path
- expect(File.exists?(tempdir_path)).to be_falsey
+ expect(File.exist?(tempdir_path)).to be_falsey
end
it "should create the /etc/network/interfaces.d directory" do
provider.run_action(:add)
- expect(File.exists?(tempdir_path)).to be_truthy
+ expect(File.exist?(tempdir_path)).to be_truthy
expect(File.directory?(tempdir_path)).to be_truthy
end
@@ -94,7 +94,7 @@ describe Chef::Provider::Ifconfig::Debian do
context "when the interface_dot_d directory exists" do
before do
- expect(File.exists?(tempdir_path)).to be_truthy
+ expect(File.exist?(tempdir_path)).to be_truthy
end
it "should still mark the resource as updated (we still write a file to it)" do
@@ -114,13 +114,7 @@ describe Chef::Provider::Ifconfig::Debian do
before do
stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path)
stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path)
- config_file_ifcfg = StringIO.new(<<-EOF
-iface eth0 inet static
- address 10.0.0.1
- netmask 255.255.254.0
-EOF
- )
- expect(File.exists?(tempdir_path)).to be_truthy # since the file exists, the enclosing dir must also exist
+ expect(File.exist?(tempdir_path)).to be_truthy # since the file exists, the enclosing dir must also exist
end
context "when the /etc/network/interfaces file has the source line" do
@@ -210,12 +204,12 @@ EOF
context "when the interface_dot_d directory does not exist" do
before do
FileUtils.rmdir tempdir_path
- expect(File.exists?(tempdir_path)).to be_falsey
+ expect(File.exist?(tempdir_path)).to be_falsey
end
it "should not create the /etc/network/interfaces.d directory" do
provider.run_action(:add)
- expect(File.exists?(tempdir_path)).not_to be_truthy
+ expect(File.exist?(tempdir_path)).not_to be_truthy
end
it "should mark the resource as updated" do
@@ -226,7 +220,7 @@ EOF
context "when the interface_dot_d directory exists" do
before do
- expect(File.exists?(tempdir_path)).to be_truthy
+ expect(File.exist?(tempdir_path)).to be_truthy
end
it "should still mark the resource as updated (we still write a file to it)" do
@@ -246,14 +240,8 @@ EOF
before do
stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path)
stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path)
- config_file_ifcfg = StringIO.new(<<-EOF
-iface eth0 inet static
- address 10.0.0.1
- netmask 255.255.254.0
- EOF
- )
expect(File).not_to receive(:new).with(config_filename_ifcfg, "w")
- expect(File.exists?(tempdir_path)).to be_truthy # since the file exists, the enclosing dir must also exist
+ expect(File.exist?(tempdir_path)).to be_truthy # since the file exists, the enclosing dir must also exist
end
context "when the /etc/network/interfaces file has the source line" do
diff --git a/spec/unit/provider/ifconfig/redhat_spec.rb b/spec/unit/provider/ifconfig/redhat_spec.rb
index 0088cef9f9..d81f963450 100644
--- a/spec/unit/provider/ifconfig/redhat_spec.rb
+++ b/spec/unit/provider/ifconfig/redhat_spec.rb
@@ -25,16 +25,20 @@ describe Chef::Provider::Ifconfig::Redhat do
@cookbook_collection = Chef::CookbookCollection.new([])
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, @cookbook_collection, @events)
- #This new_resource can be called anything --> it is not the same as in ifconfig.rb
+ # This new_resource can be called anything --> it is not the same as in ifconfig.rb
@new_resource = Chef::Resource::Ifconfig.new("10.0.0.1", @run_context)
@new_resource.mask "255.255.254.0"
@new_resource.metric "1"
@new_resource.mtu "1500"
@new_resource.device "eth0"
+ @new_resource.ethtool_opts "-A eth0 autoneg off"
+ @new_resource.bonding_opts "mode=active-backup miimon=100"
+ @new_resource.master "bond0"
+ @new_resource.slave "yes"
@provider = Chef::Provider::Ifconfig::Redhat.new(@new_resource, @run_context)
@current_resource = Chef::Resource::Ifconfig.new("10.0.0.1", @run_context)
- status = double("Status", :exitstatus => 0)
+ status = double("Status", exitstatus: 0)
@provider.instance_variable_set("@status", status)
@provider.current_resource = @current_resource
@@ -47,11 +51,15 @@ describe Chef::Provider::Ifconfig::Redhat do
it "should write network-script for centos" do
allow(@provider).to receive(:load_current_resource)
- allow(@provider).to receive(:run_command)
+ allow(@provider).to receive(:shell_out!)
expect(@config).to receive(:content) do |arg|
expect(arg).to match(/^\s*DEVICE=eth0\s*$/)
expect(arg).to match(/^\s*IPADDR=10\.0\.0\.1\s*$/)
expect(arg).to match(/^\s*NETMASK=255\.255\.254\.0\s*$/)
+ expect(arg).to match(/^\s*ETHTOOL_OPTS="-A eth0 autoneg off"\s*$/)
+ expect(arg).to match(/^\s*BONDING_OPTS="mode=active-backup miimon=100"\s*$/)
+ expect(arg).to match(/^\s*MASTER=bond0\s*$/)
+ expect(arg).to match(/^\s*SLAVE=yes\s*$/)
end
expect(@config).to receive(:run_action).with(:create)
expect(@config).to receive(:updated?).and_return(true)
@@ -64,7 +72,7 @@ describe Chef::Provider::Ifconfig::Redhat do
it "should delete network-script if it exists for centos" do
@current_resource.device @new_resource.device
allow(@provider).to receive(:load_current_resource)
- allow(@provider).to receive(:run_command)
+ allow(@provider).to receive(:shell_out!)
expect(@config).to receive(:run_action).with(:delete)
expect(@config).to receive(:updated?).and_return(true)
@provider.run_action(:delete)
diff --git a/spec/unit/provider/ifconfig_spec.rb b/spec/unit/provider/ifconfig_spec.rb
index db45640169..3732d75cc9 100644
--- a/spec/unit/provider/ifconfig_spec.rb
+++ b/spec/unit/provider/ifconfig_spec.rb
@@ -16,7 +16,7 @@
# limitations under the License.
#
-#require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
+# require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
require "spec_helper"
require "chef/exceptions"
@@ -26,7 +26,7 @@ describe Chef::Provider::Ifconfig do
@cookbook_collection = Chef::CookbookCollection.new([])
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, @cookbook_collection, @events)
- #This new_resource can be called anything --> it is not the same as in ifconfig.rb
+ # This new_resource can be called anything --> it is not the same as in ifconfig.rb
@new_resource = Chef::Resource::Ifconfig.new("10.0.0.1", @run_context)
@new_resource.mask "255.255.254.0"
@new_resource.metric "1"
@@ -35,15 +35,22 @@ describe Chef::Provider::Ifconfig do
@provider = Chef::Provider::Ifconfig.new(@new_resource, @run_context)
@current_resource = Chef::Resource::Ifconfig.new("10.0.0.1", @run_context)
- status = double("Status", :exitstatus => 0)
+ status = double("Status", exitstatus: 0)
@provider.instance_variable_set("@status", status)
@provider.current_resource = @current_resource
-
end
+
describe Chef::Provider::Ifconfig, "load_current_resource" do
+ let(:net_tools_version) { StringIO.new <<-EOS }
+net-tools 1.60
+ifconfig 1.42 (2001-04-13)
+EOS
+
before do
- @status = double(:stdout => "", :exitstatus => 1)
- allow(@provider).to receive(:shell_out).and_return(@status)
+ ifconfig = double(stdout: "", exitstatus: 1)
+ allow(@provider).to receive(:shell_out).and_return(ifconfig)
+ ifconfig_version = double(stdout: "", stderr: net_tools_version, exitstatus: 4)
+ allow(@provider).to receive(:shell_out).with("ifconfig --version").and_return(ifconfig_version)
@provider.load_current_resource
end
it "should track state of ifconfig failure" do
@@ -57,11 +64,10 @@ describe Chef::Provider::Ifconfig do
describe Chef::Provider::Ifconfig, "action_add" do
it "should add an interface if it does not exist" do
- #@provider.stub(:run_command).and_return(true)
allow(@provider).to receive(:load_current_resource)
@current_resource.inet_addr nil
command = "ifconfig eth0 10.0.0.1 netmask 255.255.254.0 metric 1 mtu 1500"
- expect(@provider).to receive(:run_command).with(:command => command)
+ expect(@provider).to receive(:shell_out!).with(*command.split(" "))
expect(@provider).to receive(:generate_config)
@provider.run_action(:add)
@@ -72,7 +78,7 @@ describe Chef::Provider::Ifconfig do
allow(@provider).to receive(:load_current_resource)
@new_resource.target "172.16.32.2"
command = "ifconfig eth0 172.16.32.2 netmask 255.255.254.0 metric 1 mtu 1500"
- expect(@provider).to receive(:run_command).with(:command => command)
+ expect(@provider).to receive(:shell_out!).with(*command.split(" "))
@provider.run_action(:add)
expect(@new_resource).to be_updated
@@ -80,7 +86,7 @@ describe Chef::Provider::Ifconfig do
it "should not add an interface if it already exists" do
allow(@provider).to receive(:load_current_resource)
- expect(@provider).not_to receive(:run_command)
+ expect(@provider).not_to receive(:shell_out!)
@current_resource.inet_addr "10.0.0.1"
expect(@provider).to receive(:generate_config)
@@ -88,9 +94,9 @@ describe Chef::Provider::Ifconfig do
expect(@new_resource).not_to be_updated
end
- #We are not testing this case with the assumption that anyone writing the cookbook would not make a typo == lo
- #it "should add a blank command if the #{@new_resource.device} == lo" do
- #end
+ # We are not testing this case with the assumption that anyone writing the cookbook would not make a typo == lo
+ # it "should add a blank command if the #{@new_resource.device} == lo" do
+ # end
end
describe Chef::Provider::Ifconfig, "action_enable" do
@@ -99,7 +105,7 @@ describe Chef::Provider::Ifconfig do
allow(@provider).to receive(:load_current_resource)
@current_resource.inet_addr nil
command = "ifconfig eth0 10.0.0.1 netmask 255.255.254.0 metric 1 mtu 1500"
- expect(@provider).to receive(:run_command).with(:command => command)
+ expect(@provider).to receive(:shell_out!).with(*command.split(" "))
expect(@provider).not_to receive(:generate_config)
@provider.run_action(:enable)
@@ -110,7 +116,7 @@ describe Chef::Provider::Ifconfig do
allow(@provider).to receive(:load_current_resource)
@new_resource.target "172.16.32.2"
command = "ifconfig eth0 172.16.32.2 netmask 255.255.254.0 metric 1 mtu 1500"
- expect(@provider).to receive(:run_command).with(:command => command)
+ expect(@provider).to receive(:shell_out!).with(*command.split(" "))
@provider.run_action(:enable)
expect(@new_resource).to be_updated
@@ -133,7 +139,7 @@ describe Chef::Provider::Ifconfig do
allow(@provider).to receive(:load_current_resource)
@current_resource.device "eth0"
command = "ifconfig #{@new_resource.device} down"
- expect(@provider).to receive(:run_command).with(:command => command)
+ expect(@provider).to receive(:shell_out!).with(*command.split(" "))
expect(@provider).to receive(:delete_config)
@provider.run_action(:delete)
@@ -142,7 +148,7 @@ describe Chef::Provider::Ifconfig do
it "should not delete interface if it does not exist" do
allow(@provider).to receive(:load_current_resource)
- expect(@provider).not_to receive(:run_command)
+ expect(@provider).not_to receive(:shell_out!)
expect(@provider).to receive(:delete_config)
@provider.run_action(:delete)
@@ -156,7 +162,7 @@ describe Chef::Provider::Ifconfig do
allow(@provider).to receive(:load_current_resource)
@current_resource.device "eth0"
command = "ifconfig #{@new_resource.device} down"
- expect(@provider).to receive(:run_command).with(:command => command)
+ expect(@provider).to receive(:shell_out!).with(*command.split(" "))
expect(@provider).not_to receive(:delete_config)
@provider.run_action(:disable)
@@ -165,7 +171,7 @@ describe Chef::Provider::Ifconfig do
it "should not delete interface if it does not exist" do
allow(@provider).to receive(:load_current_resource)
- expect(@provider).not_to receive(:run_command)
+ expect(@provider).not_to receive(:shell_out!)
expect(@provider).not_to receive(:delete_config)
@provider.run_action(:disable)
@@ -179,7 +185,7 @@ describe Chef::Provider::Ifconfig do
allow(@provider).to receive(:load_current_resource)
@current_resource.device "eth0"
command = "ifconfig #{@new_resource.device} down"
- expect(@provider).to receive(:run_command).with(:command => command)
+ expect(@provider).to receive(:shell_out!).with(*command.split(" "))
expect(@provider).to receive(:delete_config)
@provider.run_action(:delete)
@@ -190,7 +196,7 @@ describe Chef::Provider::Ifconfig do
# This is so that our fake values do not get overwritten
allow(@provider).to receive(:load_current_resource)
# This is so that nothing actually runs
- expect(@provider).not_to receive(:run_command)
+ expect(@provider).not_to receive(:shell_out!)
expect(@provider).to receive(:delete_config)
@provider.run_action(:delete)
diff --git a/spec/unit/provider/launchd_spec.rb b/spec/unit/provider/launchd_spec.rb
index 4286405ef5..693801f99b 100644
--- a/spec/unit/provider/launchd_spec.rb
+++ b/spec/unit/provider/launchd_spec.rb
@@ -40,7 +40,7 @@ describe Chef::Provider::Launchd do
\t<string>/Library/scripts/call_mom.sh</string>
\t<key>StartCalendarInterval</key>
\t<dict>
-\t\t<key>Hourly</key>
+\t\t<key>Hour</key>
\t\t<integer>10</integer>
\t\t<key>Weekday</key>
\t\t<integer>7</integer>
@@ -50,12 +50,42 @@ describe Chef::Provider::Launchd do
</dict>
</plist>
XML
+ let(:test_plist_multiple_intervals) { String.new <<-XML }
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+\t<key>Label</key>
+\t<string>call.mom.weekly</string>
+\t<key>Program</key>
+\t<string>/Library/scripts/call_mom.sh</string>
+\t<key>StartCalendarInterval</key>
+\t<array>
+\t\t<dict>
+\t\t\t<key>Hour</key>
+\t\t\t<integer>11</integer>
+\t\t\t<key>Weekday</key>
+\t\t\t<integer>1</integer>
+\t\t</dict>
+\t\t<dict>
+\t\t\t<key>Hour</key>
+\t\t\t<integer>12</integer>
+\t\t\t<key>Weekday</key>
+\t\t\t<integer>2</integer>
+\t\t</dict>
+\t</array>
+\t<key>TimeOut</key>
+\t<integer>300</integer>
+</dict>
+</plist>
+XML
- let(:test_hash) do {
+ let(:test_hash) do
+ {
"Label" => "call.mom.weekly",
"Program" => "/Library/scripts/call_mom.sh",
"StartCalendarInterval" => {
- "Hourly" => 10,
+ "Hour" => 10,
"Weekday" => 7,
},
"TimeOut" => 300,
@@ -99,15 +129,64 @@ XML
it "should produce the test_plist from properties" do
new_resource.program "/Library/scripts/call_mom.sh"
new_resource.time_out 300
- new_resource.start_calendar_interval "Hourly" => 10, "Weekday" => 7
+ new_resource.start_calendar_interval "Hour" => 10, "Weekday" => 7
expect(provider.content?).to be_truthy
expect(provider.content).to eql(test_plist)
end
end
+ describe "start_calendar_interval is passed" do
+ it "should allow array of Hashes" do
+ allowed = (1..2).collect do |num|
+ {
+ "Hour" => 10 + num,
+ "Weekday" => num,
+ }
+ end
+ new_resource.program "/Library/scripts/call_mom.sh"
+ new_resource.time_out 300
+ new_resource.start_calendar_interval allowed
+ expect(provider.content?).to be_truthy
+ expect(provider.content).to eql(test_plist_multiple_intervals)
+ end
+
+ it "should allow all StartCalendarInterval keys" do
+ allowed = {
+ "Minute" => 1,
+ "Hour" => 1,
+ "Day" => 1,
+ "Weekday" => 1,
+ "Month" => 1,
+ }
+ new_resource.program "/Library/scripts/call_mom.sh"
+ new_resource.time_out 300
+ new_resource.start_calendar_interval allowed
+ expect(provider.content?).to be_truthy
+ %w{Minute Hour Day Weekday Month}.each do |key|
+ expect(provider.content).to include("<key>#{key}</key>")
+ end
+ end
+
+ it "should not allow invalid ShowCalendarInterval keys" do
+ new_resource.program "/Library/scripts/call_mom.sh"
+ new_resource.time_out 300
+ expect do
+ new_resource.start_calendar_interval "Hourly" => 1
+ end.to raise_error(/Hourly are invalid/)
+ end
+
+ it "should not allow non-integer values" do
+ new_resource.program "/Library/scripts/call_mom.sh"
+ new_resource.time_out 300
+ expect do
+ new_resource.start_calendar_interval "Weekday" => "1-2"
+ end.to raise_error(/Invalid value.*\(1-2\)/)
+ end
+ end
+
describe "hash is passed" do
- it "should produce the test_plist from the hash" do
- new_resource.hash test_hash
+ it "should produce the test_plist content from the plist_hash property" do
+ new_resource.plist_hash test_hash
expect(provider.content?).to be_truthy
expect(provider.content).to eql(test_plist)
end
diff --git a/spec/unit/provider/link_spec.rb b/spec/unit/provider/link_spec.rb
index 6bb08551a2..10dbdc1766 100644
--- a/spec/unit/provider/link_spec.rb
+++ b/spec/unit/provider/link_spec.rb
@@ -25,11 +25,13 @@ if Chef::Platform.windows?
require "chef/win32/file" #probably need this in spec_helper
end
-describe Chef::Resource::Link, :not_supported_on_win2k3 do
+describe Chef::Resource::Link do
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
let(:provider) do
node = Chef::Node.new
@events = Chef::EventDispatch::Dispatcher.new
run_context = Chef::RunContext.new(node, {}, @events)
+ allow(run_context).to receive(:logger).and_return(logger)
Chef::Provider::Link.new(new_resource, run_context)
end
let(:new_resource) do
@@ -249,4 +251,159 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
end
end
end
+
+ describe "action_delete" do
+ before(:each) do
+ stat = double("stats", :ino => 5)
+ allow(stat).to receive(:uid).and_return(501)
+ allow(stat).to receive(:gid).and_return(501)
+ allow(stat).to receive(:mode).and_return(0755)
+ allow(provider.file_class).to receive(:stat).with(
+ "#{CHEF_SPEC_DATA}/fofile-link").and_return(stat)
+
+ provider.load_current_resource
+ end
+
+ shared_context "delete link to directories on Windows" do
+ before do
+ allow(::File).to receive(:directory?).with(
+ "#{CHEF_SPEC_DATA}/fofile-link").and_return(true)
+ end
+
+ it "invokes Dir.delete method to delete the link" do
+ expect(::Dir).to receive(:delete).with(provider.new_resource.target_file)
+ expect(logger).to receive(:info).with("#{provider.new_resource} deleted")
+ provider.run_action(:delete)
+ end
+ end
+
+ shared_context "delete link to directories on Linux" do
+ before do
+ allow(::File).to receive(:directory?).with(
+ "#{CHEF_SPEC_DATA}/fofile-link").and_return(true)
+ end
+
+ it "invokes File.delete method to delete the link" do
+ expect(::File).to receive(:delete).with(provider.new_resource.target_file)
+ expect(logger).to receive(:info).with("#{provider.new_resource} deleted")
+ provider.run_action(:delete)
+ end
+ end
+
+ shared_context "delete link to files" do
+ before do
+ allow(::File).to receive(:directory?).with(
+ "#{CHEF_SPEC_DATA}/fofile-link").and_return(false)
+ end
+
+ it "invokes File.delete method to delete the link" do
+ expect(::File).to receive(:delete).with(provider.new_resource.target_file)
+ expect(logger).to receive(:info).with("#{provider.new_resource} deleted")
+ provider.run_action(:delete)
+ end
+ end
+
+ shared_context "soft links prerequisites" do
+ before(:each) do
+ allow(provider.file_class).to receive(:symlink?).with(
+ "#{CHEF_SPEC_DATA}/fofile-link").and_return(true)
+ allow(provider.file_class).to receive(:readlink).with(
+ "#{CHEF_SPEC_DATA}/fofile-link").and_return("#{CHEF_SPEC_DATA}/fofile")
+ end
+ end
+
+ shared_context "hard links prerequisites" do
+ let(:new_resource) do
+ result = Chef::Resource::Link.new("#{CHEF_SPEC_DATA}/fofile-link")
+ result.to "#{CHEF_SPEC_DATA}/fofile"
+ result.link_type :hard
+ result
+ end
+
+ before(:each) do
+ stat = double("stats", :ino => 5)
+ allow(stat).to receive(:uid).and_return(502)
+ allow(stat).to receive(:gid).and_return(502)
+ allow(stat).to receive(:mode).and_return(0644)
+
+ allow(provider.file_class).to receive(:symlink?).with(
+ "#{CHEF_SPEC_DATA}/fofile-link").and_return(false)
+
+ allow(File).to receive(:exists?).with(
+ "#{CHEF_SPEC_DATA}/fofile-link").and_return(true)
+ allow(File).to receive(:exists?).with(
+ "#{CHEF_SPEC_DATA}/fofile").and_return(true)
+
+ allow(provider.file_class).to receive(:stat).with(
+ "#{CHEF_SPEC_DATA}/fofile").and_return(stat)
+ end
+ end
+
+ context "on Windows platform" do
+ let(:resource_link) do
+ Chef::Resource::Link.new(provider.new_resource.name)
+ end
+
+ before(:each) do
+ allow(Chef::Resource::Link).to receive(:new).with(
+ provider.new_resource.name).and_return(resource_link)
+ allow(resource_link).to receive(:verify_links_supported!)
+ allow(Chef::Platform).to receive(:windows?).and_return(true)
+ end
+
+ context "soft links" do
+ include_context "soft links prerequisites"
+
+ context "to directories" do
+ include_context "delete link to directories on Windows"
+ end
+
+ context "to files" do
+ include_context "delete link to files"
+ end
+ end
+
+ context "hard links" do
+ include_context "hard links prerequisites"
+
+ context "to directories" do
+ include_context "delete link to directories on Windows"
+ end
+
+ context "to files" do
+ include_context "delete link to files"
+ end
+ end
+ end
+
+ context "on Linux platform" do
+ before(:each) do
+ allow(Chef::Platform).to receive(:windows?).and_return(false)
+ end
+
+ context "soft links" do
+ include_context "soft links prerequisites"
+
+ context "to directories" do
+ include_context "delete link to directories on Linux"
+ end
+
+ context "to files" do
+ include_context "delete link to files"
+ end
+ end
+
+ context "hard links" do
+ include_context "hard links prerequisites"
+
+ context "to directories" do
+ include_context "delete link to directories on Linux"
+ end
+
+ context "to files" do
+ include_context "delete link to files"
+ end
+ end
+ end
+ end
end
diff --git a/spec/unit/provider/log_spec.rb b/spec/unit/provider/log_spec.rb
index 2e4f9c4e52..deb2024640 100644
--- a/spec/unit/provider/log_spec.rb
+++ b/spec/unit/provider/log_spec.rb
@@ -32,44 +32,63 @@ describe Chef::Provider::Log::ChefLog do
let(:provider) { Chef::Provider::Log::ChefLog.new(new_resource, run_context) }
- it "should write the string to the Chef::Log object at default level (info)" do
- expect(Chef::Log).to receive(:info).with(log_str).and_return(true)
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+ before do
+ allow(run_context).to receive(:logger).and_return(logger)
+ end
+
+ it "should write the string to the logger object at default level (info)" do
+ expect(logger).to receive(:info).with(log_str).and_return(true)
provider.run_action(:write)
end
- it "should write the string to the Chef::Log object at debug level" do
+ it "should write the string to the logger object at debug level" do
new_resource.level :debug
- expect(Chef::Log).to receive(:debug).with(log_str).and_return(true)
+ expect(logger).to receive(:debug).with(log_str).and_return(true)
provider.run_action(:write)
end
- it "should write the string to the Chef::Log object at info level" do
+ it "should write the string to the logger object at info level" do
new_resource.level :info
- expect(Chef::Log).to receive(:info).with(log_str).and_return(true)
+ expect(logger).to receive(:info).with(log_str).and_return(true)
provider.run_action(:write)
end
- it "should write the string to the Chef::Log object at warn level" do
+ it "should write the string to the logger object at warn level" do
new_resource.level :warn
- expect(Chef::Log).to receive(:warn).with(log_str).and_return(true)
+ expect(logger).to receive(:warn).with(log_str).and_return(true)
provider.run_action(:write)
end
- it "should write the string to the Chef::Log object at error level" do
+ it "should write the string to the logger object at error level" do
new_resource.level :error
- expect(Chef::Log).to receive(:error).with(log_str).and_return(true)
+ expect(logger).to receive(:error).with(log_str).and_return(true)
provider.run_action(:write)
end
- it "should write the string to the Chef::Log object at fatal level" do
+ it "should write the string to the logger object at fatal level" do
new_resource.level :fatal
- expect(Chef::Log).to receive(:fatal).with(log_str).and_return(true)
+ expect(logger).to receive(:fatal).with(log_str).and_return(true)
provider.run_action(:write)
end
it "should print the string in why-run mode" do
Chef::Config[:why_run] = true
- expect(Chef::Log).to receive(:info).with(log_str).and_return(true)
+ expect(logger).to receive(:info).with(log_str).and_return(true)
provider.run_action(:write)
end
+
+ context "when count_log_resource_updates is passed in knife.rb" do
+ it "updates the resource count if count_log_resource_updates=true" do
+ Chef::Config[:count_log_resource_updates] = true
+ expect(new_resource).to receive(:updated_by_last_action)
+ provider.run_action(:write)
+ end
+
+ it "doesn't update the resource count if count_log_resource_updates=false" do
+ Chef::Config[:count_log_resource_updates] = false
+ expect(new_resource).not_to receive(:updated_by_last_action)
+ provider.run_action(:write)
+ end
+ end
end
diff --git a/spec/unit/provider/mount/aix_spec.rb b/spec/unit/provider/mount/aix_spec.rb
index 3371c270c5..615f3c3304 100644
--- a/spec/unit/provider/mount/aix_spec.rb
+++ b/spec/unit/provider/mount/aix_spec.rb
@@ -44,6 +44,11 @@ MOUNT
#MountPoint:Device:Vfs:Nodename:Type:Size:Options:AutoMount:Acct
/tmp/foo:/dev/sdz1:jfs2::bootfs:10485760:rw:yes:no
ENABLED
+
+ @test_wrong_output = <<-WRONG
+#MountPoint:Device:Vfs:Nodename:Type:Size:Options:AutoMount:Acct
+/tmp/foo::/dev/sdz1:jfs2:bootfs:10485760:rw:yes:no
+WRONG
end
before(:each) do
@@ -102,6 +107,25 @@ ENABLED
expect(@provider.current_resource.mounted).to be_falsey
end
+
+ context "mount_options_unchanged?" do
+ it "should return true if mounted device is the same" do
+ stub_mounted_enabled(@provider, @mounted_output, @enabled_output)
+ @provider.load_current_resource
+
+ allow(@provider.current_resource).to receive(:fstype).and_return("jfs2")
+ expect(@provider.send :mount_options_unchanged?).to be true
+ end
+
+ it "should return false if mounted device has changed" do
+ stub_mounted_enabled(@provider, @mounted_output, @enabled_output)
+ @provider.load_current_resource
+
+ allow(@provider.current_resource).to receive(:fstype).and_return("XXXX")
+ expect(@provider.send :mount_options_unchanged?).to be false
+ end
+ end
+
end
# tests for #enabled?
@@ -188,12 +212,20 @@ ENABLED
it "should not enable mount if it is mounted and already enabled and mount options are unchanged" do
stub_mounted_enabled(@provider, @mounted_output, @enabled_output)
- @new_resource.options "rw"
expect(@provider).not_to receive(:enable_fs)
@provider.run_action(:enable)
end
+
+ it "should return false if enabled_output is given in wrong syntax" do
+ stub_mounted_enabled(@provider, @mounted_output, @test_wrong_output)
+
+ expect(@provider).to receive(:enable_fs)
+
+ @provider.run_action(:enable)
+ end
+
end
describe "disable_fs" do
diff --git a/spec/unit/provider/mount/mount_spec.rb b/spec/unit/provider/mount/mount_spec.rb
index 42585d9e3e..af7916e5bd 100644
--- a/spec/unit/provider/mount/mount_spec.rb
+++ b/spec/unit/provider/mount/mount_spec.rb
@@ -53,13 +53,13 @@ describe Chef::Provider::Mount::Mount do
expect(@provider.current_resource.device).to eq("/dev/sdz1")
end
- it "should accecpt device_type :uuid", :not_supported_on_solaris do
+ it "should accept device_type :uuid", :not_supported_on_solaris do
@status = double(:stdout => "/dev/sdz1\n", :exitstatus => 1)
@new_resource.device_type :uuid
@new_resource.device "d21afe51-a0fe-4dc6-9152-ac733763ae0a"
@stdout_findfs = double("STDOUT", :first => "/dev/sdz1")
expect(@provider).to receive(:shell_out).with("/sbin/findfs UUID=d21afe51-a0fe-4dc6-9152-ac733763ae0a").and_return(@status)
- @provider.load_current_resource()
+ @provider.load_current_resource
@provider.mountable?
end
@@ -82,7 +82,7 @@ describe Chef::Provider::Mount::Mount do
it "should raise an error if the mount device does not exist" do
allow(::File).to receive(:exists?).with("/dev/sdz1").and_return false
- expect { @provider.load_current_resource();@provider.mountable? }.to raise_error(Chef::Exceptions::Mount)
+ expect { @provider.load_current_resource(); @provider.mountable? }.to raise_error(Chef::Exceptions::Mount)
end
it "should not call mountable? with load_current_resource - CHEF-1565" do
@@ -99,25 +99,25 @@ describe Chef::Provider::Mount::Mount do
@new_resource.device "d21afe51-a0fe-4dc6-9152-ac733763ae0a"
expect(@provider).to receive(:shell_out).with("/sbin/findfs UUID=d21afe51-a0fe-4dc6-9152-ac733763ae0a").and_return(status)
expect(::File).to receive(:exists?).with("").and_return(false)
- expect { @provider.load_current_resource();@provider.mountable? }.to raise_error(Chef::Exceptions::Mount)
+ expect { @provider.load_current_resource(); @provider.mountable? }.to raise_error(Chef::Exceptions::Mount)
end
it "should raise an error if the mount point does not exist" do
allow(::File).to receive(:exists?).with("/tmp/foo").and_return false
- expect { @provider.load_current_resource();@provider.mountable? }.to raise_error(Chef::Exceptions::Mount)
+ expect { @provider.load_current_resource(); @provider.mountable? }.to raise_error(Chef::Exceptions::Mount)
end
- %w{tmpfs fuse cgroup}.each do |fstype|
+ %w{tmpfs fuse cgroup vboxsf zfs}.each do |fstype|
it "does not expect the device to exist for #{fstype}" do
@new_resource.fstype(fstype)
@new_resource.device("whatever")
- expect { @provider.load_current_resource();@provider.mountable? }.not_to raise_error
+ expect { @provider.load_current_resource(); @provider.mountable? }.not_to raise_error
end
end
it "does not expect the device to exist if it's none" do
@new_resource.device("none")
- expect { @provider.load_current_resource();@provider.mountable? }.not_to raise_error
+ expect { @provider.load_current_resource(); @provider.mountable? }.not_to raise_error
end
it "should set mounted true if the mount point is found in the mounts list" do
@@ -374,6 +374,28 @@ describe Chef::Provider::Mount::Mount do
end
end
+ describe "default_mount_options" do
+ it "should return the correct default mount options for Linux" do
+ @provider.node.override[:os] = "linux"
+ expect(@provider.default_mount_options).to eq("defaults")
+ end
+
+ it "should return the correct default mount options for AIX" do
+ @provider.node.override[:os] = "aix"
+ expect(@provider.default_mount_options).to eq("rw")
+ end
+
+ it "should return the correct default mount options for Darwin" do
+ @provider.node.override[:os] = "darwin"
+ expect(@provider.default_mount_options).to eq("rw")
+ end
+
+ it "should return the correct default mount options for FreeBSD" do
+ @provider.node.override[:os] = "freebsd"
+ expect(@provider.default_mount_options).to eq("rw")
+ end
+ end
+
describe "when enabling the fs" do
it "should enable if enabled isn't true" do
@current_resource.enabled(false)
@@ -470,5 +492,35 @@ describe Chef::Provider::Mount::Mount do
@provider.disable_fs
end
end
+
+ # the fstab might contain the mount with the device as a device but the resource has a label.
+ # we should not create two mount lines, but update the existing one
+ # not supported on solaris because it can't cope with a UUID device type
+ context "when the device is described differently", :not_supported_on_solaris do
+ it "should update the existing line" do
+ @current_resource.enabled(true)
+ status = double(:stdout => "/dev/sdz1\n", :exitstatus => 1)
+ expect(@provider).to receive(:shell_out).with("/sbin/findfs UUID=d21afe51-a0fe-4dc6-9152-ac733763ae0a").and_return(status)
+
+ filesystems = [%q{/dev/sdy1 /tmp/foo ext3 defaults 1 2},
+ %q{/dev/sdz1 /tmp/foo ext3 defaults 1 2}].join("\n")
+ fstab = StringIO.new filesystems
+
+ fstab_write = StringIO.new
+
+ allow(::File).to receive(:readlines).with("/etc/fstab").and_return(fstab.readlines)
+ allow(::File).to receive(:open).with("/etc/fstab", "w").and_yield(fstab_write)
+ allow(::File).to receive(:open).with("/etc/fstab", "a").and_yield(fstab_write)
+
+ @new_resource.device_type :uuid
+ @new_resource.device "d21afe51-a0fe-4dc6-9152-ac733763ae0a"
+ @new_resource.dump 1
+
+ @provider.enable_fs
+ expect(fstab_write.string).to match(%r{/dev/sdy1\s+/tmp/foo\s+ext3\s+defaults\s+1\s+2})
+ expect(fstab_write.string).to match(%r{UUID=d21afe51-a0fe-4dc6-9152-ac733763ae0a\s+/tmp/foo\s+ext3\s+defaults\s+1\s+2})
+ expect(fstab_write.string).not_to match(%r{/dev/sdz1\s+/tmp/foo\s+ext3\s+defaults\s+1\s+2})
+ end
+ end
end
end
diff --git a/spec/unit/provider/mount/solaris_spec.rb b/spec/unit/provider/mount/solaris_spec.rb
index 51ace83470..2ec9feaf3b 100644
--- a/spec/unit/provider/mount/solaris_spec.rb
+++ b/spec/unit/provider/mount/solaris_spec.rb
@@ -41,7 +41,7 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
let(:options) { nil }
- let(:new_resource) {
+ let(:new_resource) do
new_resource = Chef::Resource::Mount.new(mountpoint)
new_resource.device device
new_resource.device_type device_type
@@ -50,13 +50,13 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
new_resource.options options
new_resource.supports :remount => false
new_resource
- }
+ end
- let(:provider) {
+ let(:provider) do
Chef::Provider::Mount::Solaris.new(new_resource, run_context)
- }
+ end
- let(:vfstab_file_contents) {
+ let(:vfstab_file_contents) do
<<-EOF.gsub /^\s*/, ""
#device device mount FS fsck mount mount
#to mount to fsck point type pass at boot options
@@ -74,21 +74,21 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
# ufs
/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
EOF
- }
+ end
- let(:vfstab_file) {
+ let(:vfstab_file) do
t = Tempfile.new("rspec-vfstab")
t.write(vfstab_file_contents)
t.close
t
- }
+ end
- let(:mount_output) {
+ let(:mount_output) do
<<-EOF.gsub /^\s*/, ""
/dev/dsk/c0t0d0s0 on / type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200000 on Tue Jul 31 22:34:46 2012
/dev/dsk/c0t2d0s7 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
EOF
- }
+ end
before do
stub_const("Chef::Provider::Mount::Solaris::VFSTAB", vfstab_file.path )
@@ -214,7 +214,7 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
describe "#load_current_resource" do
context "when loading a normal UFS filesystem with noauto, don't mount at boot" do
- let(:vfstab_file_contents) {
+ let(:vfstab_file_contents) do
<<-EOF.gsub /^\s*/, ""
#device device mount FS fsck mount mount
#to mount to fsck point type pass at boot options
@@ -232,7 +232,7 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
# ufs
/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 no -
EOF
- }
+ end
before do
provider.load_current_resource
@@ -244,16 +244,16 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
end
context "when the device is an smbfs mount" do
- let(:mount_output) {
+ let(:mount_output) do
<<-EOF.gsub /^\s*/, ""
//solarsystem/tmp on /mnt type smbfs read/write/setuid/devices/dev=5080000 on Tue Mar 29 11:40:18 2011
EOF
- }
- let(:vfstab_file_contents) {
+ end
+ let(:vfstab_file_contents) do
<<-EOF.gsub /^\s*/, ""
//WORKGROUP;username:password@host/share - /mountpoint smbfs - no fileperms=0777,dirperms=0777
EOF
- }
+ end
let(:fsck_device) { "-" }
@@ -263,17 +263,17 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
end
context "when the device is an NFS mount" do
- let(:mount_output) {
+ let(:mount_output) do
<<-EOF.gsub /^\s*/, ""
cartman:/share2 on /cartman type nfs rsize=32768,wsize=32768,NFSv4,dev=4000004 on Tue Mar 29 11:40:18 2011
EOF
- }
+ end
- let(:vfstab_file_contents) {
+ let(:vfstab_file_contents) do
<<-EOF.gsub /^\s*/, ""
cartman:/share2 - /cartman nfs - yes rw,soft
EOF
- }
+ end
let(:fsck_device) { "-" }
@@ -334,17 +334,17 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
let(:target) { "/dev/mapper/target" }
- let(:mount_output) {
+ let(:mount_output) do
<<-EOF.gsub /^\s*/, ""
#{target} on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
EOF
- }
+ end
- let(:vfstab_file_contents) {
+ let(:vfstab_file_contents) do
<<-EOF.gsub /^\s*/, ""
#{target} /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
EOF
- }
+ end
before do
expect(File).to receive(:symlink?).with(device).at_least(:once).and_return(true)
@@ -371,17 +371,17 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
let(:absolute_target) { File.expand_path(target, File.dirname(device)) }
- let(:mount_output) {
+ let(:mount_output) do
<<-EOF.gsub /^\s*/, ""
#{absolute_target} on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
EOF
- }
+ end
- let(:vfstab_file_contents) {
+ let(:vfstab_file_contents) do
<<-EOF.gsub /^\s*/, ""
#{absolute_target} /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
EOF
- }
+ end
before do
expect(File).to receive(:symlink?).with(device).at_least(:once).and_return(true)
@@ -404,12 +404,12 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
end
context "when the matching mount point is last in the mounts list" do
- let(:mount_output) {
+ let(:mount_output) do
<<-EOF.gsub /^\s*/, ""
/dev/dsk/c0t0d0s0 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200000 on Tue Jul 31 22:34:46 2012
/dev/dsk/c0t2d0s7 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
EOF
- }
+ end
it "should set mounted true" do
provider.load_current_resource()
expect(provider.current_resource.mounted).to be_truthy
@@ -417,12 +417,12 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
end
context "when the matching mount point is not last in the mounts list" do
- let(:mount_output) {
+ let(:mount_output) do
<<-EOF.gsub /^\s*/, ""
/dev/dsk/c0t2d0s7 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
/dev/dsk/c0t0d0s0 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200000 on Tue Jul 31 22:34:46 2012
EOF
- }
+ end
it "should set mounted false" do
provider.load_current_resource()
expect(provider.current_resource.mounted).to be_falsey
@@ -430,11 +430,11 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
end
context "when the matching mount point is not in the mounts list (mountpoint wrong)" do
- let(:mount_output) {
+ let(:mount_output) do
<<-EOF.gsub /^\s*/, ""
/dev/dsk/c0t2d0s7 on /mnt/foob type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
EOF
- }
+ end
it "should set mounted false" do
provider.load_current_resource()
expect(provider.current_resource.mounted).to be_falsey
@@ -442,11 +442,11 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
end
context "when the matching mount point is not in the mounts list (raw device wrong)" do
- let(:mount_output) {
+ let(:mount_output) do
<<-EOF.gsub /^\s*/, ""
/dev/dsk/c0t2d0s72 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
EOF
- }
+ end
it "should set mounted false" do
provider.load_current_resource()
expect(provider.current_resource.mounted).to be_falsey
@@ -454,12 +454,12 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
end
context "when the mount point is last in fstab" do
- let(:vfstab_file_contents) {
+ let(:vfstab_file_contents) do
<<-EOF.gsub /^\s*/, ""
/dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
EOF
- }
+ end
it "should set enabled to true" do
provider.load_current_resource
@@ -468,12 +468,12 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
end
context "when the mount point is not last in fstab and is a substring of another mount" do
- let(:vfstab_file_contents) {
+ let(:vfstab_file_contents) do
<<-EOF.gsub /^\s*/, ""
/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
/dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s7 /mnt/foo/bar ufs 2 yes -
EOF
- }
+ end
it "should set enabled to true" do
provider.load_current_resource
@@ -482,12 +482,12 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
end
context "when the mount point is not last in fstab" do
- let(:vfstab_file_contents) {
+ let(:vfstab_file_contents) do
<<-EOF.gsub /^\s*/, ""
/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
/dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s72 /mnt/foo ufs 2 yes -
EOF
- }
+ end
it "should set enabled to false" do
provider.load_current_resource
@@ -496,11 +496,11 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
end
context "when the mount point is not in fstab, but the mountpoint is a substring of one that is" do
- let(:vfstab_file_contents) {
+ let(:vfstab_file_contents) do
<<-EOF.gsub /^\s*/, ""
/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foob ufs 2 yes -
EOF
- }
+ end
it "should set enabled to false" do
provider.load_current_resource
@@ -509,11 +509,11 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
end
context "when the mount point is not in fstab, but the device is a substring of one that is" do
- let(:vfstab_file_contents) {
+ let(:vfstab_file_contents) do
<<-EOF.gsub /^\s*/, ""
/dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
EOF
- }
+ end
it "should set enabled to false" do
provider.load_current_resource
@@ -522,11 +522,11 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
end
context "when the mountpoint line is commented out" do
- let(:vfstab_file_contents) {
+ let(:vfstab_file_contents) do
<<-EOF.gsub /^\s*/, ""
#/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
EOF
- }
+ end
it "should set enabled to false" do
provider.load_current_resource
@@ -538,7 +538,7 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
context "after the mount's state has been discovered" do
describe "mount_fs" do
it "should mount the filesystem" do
- expect(provider).to receive(:shell_out!).with("mount -F #{fstype} -o defaults #{device} #{mountpoint}")
+ expect(provider).to receive(:shell_out!).with("mount -F #{fstype} #{device} #{mountpoint}")
provider.mount_fs()
end
@@ -600,7 +600,7 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
context "in the typical case" do
let(:other_mount) { "/dev/dsk/c0t2d0s0 /dev/rdsk/c0t2d0s0 / ufs 2 yes -" }
- let(:this_mount) { "/dev/dsk/c0t2d0s7\t/dev/rdsk/c0t2d0s7\t/mnt/foo\tufs\t2\tyes\tdefaults\n" }
+ let(:this_mount) { "/dev/dsk/c0t2d0s7\t/dev/rdsk/c0t2d0s7\t/mnt/foo\tufs\t2\tyes\t-\n" }
let(:vfstab_file_contents) { [other_mount].join("\n") }
diff --git a/spec/unit/provider/mount_spec.rb b/spec/unit/provider/mount_spec.rb
index 19967e8496..b2497cf1b2 100644
--- a/spec/unit/provider/mount_spec.rb
+++ b/spec/unit/provider/mount_spec.rb
@@ -70,6 +70,13 @@ describe Chef::Provider::Mount do
expect(new_resource).to be_updated_by_last_action
end
+ it "should unmount the filesystem if it is mounted" do
+ allow(current_resource).to receive(:mounted).and_return(true)
+ expect(provider).to receive(:umount_fs).and_return(true)
+ provider.run_action(:unmount)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
it "should not umount the filesystem if it is not mounted" do
allow(current_resource).to receive(:mounted).and_return(false)
expect(provider).not_to receive(:umount_fs)
diff --git a/spec/unit/provider/osx_profile_spec.rb b/spec/unit/provider/osx_profile_spec.rb
index e307bc2c22..a1dcf3ecd6 100644
--- a/spec/unit/provider/osx_profile_spec.rb
+++ b/spec/unit/provider/osx_profile_spec.rb
@@ -238,6 +238,12 @@ describe Chef::Provider::OsxProfile do
).to eql(new_resource.identifier)
end
+ it "should work with spaces in the identifier" do
+ provider.action = :remove
+ provider.define_resource_requirements
+ expect { provider.process_resource_requirements }.not_to raise_error
+ end
+
it "should build the shellout remove command correctly" do
new_resource.identifier "com.testprofile.screensaver"
new_resource.action(:remove)
diff --git a/spec/unit/provider/package/apt_spec.rb b/spec/unit/provider/package/apt_spec.rb
index b5f0646b79..0700f69eb4 100644
--- a/spec/unit/provider/package/apt_spec.rb
+++ b/spec/unit/provider/package/apt_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,51 +20,52 @@ require "spec_helper"
require "ostruct"
describe Chef::Provider::Package::Apt do
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
# XXX: sorry this is ugly and was done quickly to get 12.0.2 out, this file needs a rewrite to use
# let blocks and shared examples
- [ Chef::Resource::Package, Chef::Resource::AptPackage ].each do |resource_klass|
- describe "when the new_resource is a #{resource_klass}" do
- before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = resource_klass.new("irssi", @run_context)
-
- @status = double("Status", :exitstatus => 0)
- @provider = Chef::Provider::Package::Apt.new(@new_resource, @run_context)
- @stdin = StringIO.new
- @stdout = <<-PKG_STATUS
+ before(:each) do
+ @node = Chef::Node.new
+ @events = Chef::EventDispatch::Dispatcher.new
+ @run_context = Chef::RunContext.new(@node, {}, @events)
+ allow(@run_context).to receive(:logger).and_return(logger)
+ @new_resource = Chef::Resource::AptPackage.new("irssi", @run_context)
+
+ @status = double("Status", :exitstatus => 0)
+ @provider = Chef::Provider::Package::Apt.new(@new_resource, @run_context)
+ @stdin = StringIO.new
+ @stdout = <<-PKG_STATUS
irssi:
Installed: (none)
Candidate: 0.8.14-1ubuntu4
Version table:
0.8.14-1ubuntu4 0
500 http://us.archive.ubuntu.com/ubuntu/ lucid/main Packages
- PKG_STATUS
- @stderr = ""
- @shell_out = OpenStruct.new(:stdout => @stdout, :stdin => @stdin, :stderr => @stderr, :status => @status, :exitstatus => 0)
- @timeout = 900
- end
-
- describe "when loading current resource" do
-
- it "should create a current resource with the name of the new_resource" do
- expect(@provider).to receive(:shell_out!).with(
- "apt-cache policy #{@new_resource.package_name}",
- :timeout => @timeout
- ).and_return(@shell_out)
- @provider.load_current_resource
+ PKG_STATUS
+ @stderr = ""
+ @shell_out = OpenStruct.new(:stdout => @stdout, :stdin => @stdin, :stderr => @stderr, :status => @status, :exitstatus => 0)
+ @timeout = 900
+ end
- current_resource = @provider.current_resource
- expect(current_resource).to be_a(Chef::Resource::Package)
- expect(current_resource.name).to eq("irssi")
- expect(current_resource.package_name).to eq("irssi")
- expect(current_resource.version).to be_nil
- end
+ describe "when loading current resource" do
+
+ it "should create a current resource with the name of the new_resource" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache", "policy", @new_resource.package_name,
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ ).and_return(@shell_out)
+ @provider.load_current_resource
+
+ current_resource = @provider.current_resource
+ expect(current_resource).to be_a(Chef::Resource::Package)
+ expect(current_resource.name).to eq("irssi")
+ expect(current_resource.package_name).to eq("irssi")
+ expect(current_resource.version).to eql([nil])
+ end
- it "should set the installed version if package has one" do
- @stdout.replace(<<-INSTALLED)
+ it "should set the installed version if package has one" do
+ @stdout.replace(<<-INSTALLED)
sudo:
Installed: 1.7.2p1-1ubuntu5.3
Candidate: 1.7.2p1-1ubuntu5.3
@@ -75,29 +76,54 @@ sudo:
100 /var/lib/dpkg/status
1.7.2p1-1ubuntu5 0
500 http://us.archive.ubuntu.com/ubuntu/ lucid/main Packages
- INSTALLED
- expect(@provider).to receive(:shell_out!).and_return(@shell_out)
- @provider.load_current_resource
- expect(@provider.current_resource.version).to eq("1.7.2p1-1ubuntu5.3")
- expect(@provider.candidate_version).to eql("1.7.2p1-1ubuntu5.3")
- end
-
- # libmysqlclient-dev is a real package in newer versions of debian + ubuntu
- # list of virtual packages: http://www.debian.org/doc/packaging-manuals/virtual-package-names-list.txt
- it "should not install the virtual package there is a single provider package and it is installed" do
- @new_resource.package_name("libmysqlclient15-dev")
- virtual_package_out = <<-VPKG_STDOUT
+ INSTALLED
+ expect(@provider).to receive(:shell_out!).and_return(@shell_out)
+ @provider.load_current_resource
+ expect(@provider.current_resource.version).to eq(["1.7.2p1-1ubuntu5.3"])
+ expect(@provider.candidate_version).to eql(["1.7.2p1-1ubuntu5.3"])
+ end
+
+ # it is the superclasses responsibility to throw most exceptions
+ it "if the package does not exist in the cache sets installed + candidate version to nil" do
+ @new_resource.package_name("conic-smarms")
+ policy_out = <<-POLICY_STDOUT
+N: Unable to locate package conic-smarms
+ POLICY_STDOUT
+ policy = double(:stdout => policy_out, :exitstatus => 0)
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache", "policy", "conic-smarms",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ ).and_return(policy)
+ showpkg_out = <<-SHOWPKG_STDOUT
+N: Unable to locate package conic-smarms
+ SHOWPKG_STDOUT
+ showpkg = double(:stdout => showpkg_out, :exitstatus => 0)
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache", "showpkg", "conic-smarms",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ ).and_return(showpkg)
+ @provider.load_current_resource
+ end
+
+ # libmysqlclient-dev is a real package in newer versions of debian + ubuntu
+ # list of virtual packages: http://www.debian.org/doc/packaging-manuals/virtual-package-names-list.txt
+ it "should not install the virtual package there is a single provider package and it is installed" do
+ @new_resource.package_name("libmysqlclient15-dev")
+ virtual_package_out = <<-VPKG_STDOUT
libmysqlclient15-dev:
Installed: (none)
Candidate: (none)
Version table:
- VPKG_STDOUT
- virtual_package = double(:stdout => virtual_package_out, :exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with(
- "apt-cache policy libmysqlclient15-dev",
- :timeout => @timeout
- ).and_return(virtual_package)
- showpkg_out = <<-SHOWPKG_STDOUT
+ VPKG_STDOUT
+ virtual_package = double(:stdout => virtual_package_out, :exitstatus => 0)
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache", "policy", "libmysqlclient15-dev",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ ).and_return(virtual_package)
+ showpkg_out = <<-SHOWPKG_STDOUT
Package: libmysqlclient15-dev
Versions:
@@ -114,13 +140,14 @@ Reverse Provides:
libmysqlclient-dev 5.1.41-3ubuntu12.7
libmysqlclient-dev 5.1.41-3ubuntu12.10
libmysqlclient-dev 5.1.41-3ubuntu12
- SHOWPKG_STDOUT
- showpkg = double(:stdout => showpkg_out, :exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with(
- "apt-cache showpkg libmysqlclient15-dev",
- :timeout => @timeout
- ).and_return(showpkg)
- real_package_out = <<-RPKG_STDOUT
+ SHOWPKG_STDOUT
+ showpkg = double(:stdout => showpkg_out, :exitstatus => 0)
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache", "showpkg", "libmysqlclient15-dev",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ ).and_return(showpkg)
+ real_package_out = <<-RPKG_STDOUT
libmysqlclient-dev:
Installed: 5.1.41-3ubuntu12.10
Candidate: 5.1.41-3ubuntu12.10
@@ -132,29 +159,31 @@ libmysqlclient-dev:
500 http://security.ubuntu.com/ubuntu/ lucid-security/main Packages
5.1.41-3ubuntu12 0
500 http://us.archive.ubuntu.com/ubuntu/ lucid/main Packages
- RPKG_STDOUT
- real_package = double(:stdout => real_package_out, :exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with(
- "apt-cache policy libmysqlclient-dev",
- :timeout => @timeout
- ).and_return(real_package)
- @provider.load_current_resource
- end
-
- it "should raise an exception if you specify a virtual package with multiple provider packages" do
- @new_resource.package_name("mp3-decoder")
- virtual_package_out = <<-VPKG_STDOUT
+ RPKG_STDOUT
+ real_package = double(:stdout => real_package_out, :exitstatus => 0)
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache", "policy", "libmysqlclient-dev",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ ).and_return(real_package)
+ @provider.load_current_resource
+ end
+
+ it "should raise an exception if you specify a virtual package with multiple provider packages" do
+ @new_resource.package_name("mp3-decoder")
+ virtual_package_out = <<-VPKG_STDOUT
mp3-decoder:
Installed: (none)
Candidate: (none)
Version table:
- VPKG_STDOUT
- virtual_package = double(:stdout => virtual_package_out, :exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with(
- "apt-cache policy mp3-decoder",
- :timeout => @timeout
- ).and_return(virtual_package)
- showpkg_out = <<-SHOWPKG_STDOUT
+ VPKG_STDOUT
+ virtual_package = double(:stdout => virtual_package_out, :exitstatus => 0)
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache", "policy", "mp3-decoder",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ ).and_return(virtual_package)
+ showpkg_out = <<-SHOWPKG_STDOUT
Package: mp3-decoder
Versions:
@@ -174,212 +203,330 @@ vlc 1.0.6-1ubuntu1
opencubicplayer 1:0.1.17-2
mpg321 0.2.10.6
mpg123 1.12.1-0ubuntu1
- SHOWPKG_STDOUT
- showpkg = double(:stdout => showpkg_out, :exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with(
- "apt-cache showpkg mp3-decoder",
- :timeout => @timeout
- ).and_return(showpkg)
- expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package)
- end
-
- it "should run apt-cache policy with the default_release option, if there is one on the resource" do
- @new_resource = Chef::Resource::AptPackage.new("irssi", @run_context)
- @provider = Chef::Provider::Package::Apt.new(@new_resource, @run_context)
-
- allow(@new_resource).to receive(:default_release).and_return("lenny-backports")
- allow(@new_resource).to receive(:provider).and_return(nil)
- expect(@provider).to receive(:shell_out!).with(
- "apt-cache -o APT::Default-Release=lenny-backports policy irssi",
- :timeout => @timeout
- ).and_return(@shell_out)
- @provider.load_current_resource
- end
-
- it "raises an exception if a source is specified (CHEF-5113)" do
- @new_resource.source "pluto"
- expect(@provider).to receive(:shell_out!).with(
- "apt-cache policy #{@new_resource.package_name}",
- :timeout => @timeout
- ).and_return(@shell_out)
- @provider.load_current_resource
- @provider.define_resource_requirements
- expect(@provider).to receive(:shell_out!).with("apt-cache policy irssi", { :timeout => 900 }).and_return(@shell_out)
- expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
- end
+ SHOWPKG_STDOUT
+ showpkg = double(:stdout => showpkg_out, :exitstatus => 0)
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache", "showpkg", "mp3-decoder",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ ).and_return(showpkg)
+ expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ it "should run apt-cache policy with the default_release option, if there is one on the resource" do
+ @new_resource = Chef::Resource::AptPackage.new("irssi", @run_context)
+ @provider = Chef::Provider::Package::Apt.new(@new_resource, @run_context)
+
+ @new_resource.default_release("lenny-backports")
+ @new_resource.provider(nil)
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache", "-o", "APT::Default-Release=lenny-backports", "policy", "irssi",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ ).and_return(@shell_out)
+ @provider.load_current_resource
+ end
+
+ it "raises an exception if a source is specified (CHEF-5113)" do
+ @new_resource.source "pluto"
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache", "policy", @new_resource.package_name,
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" } ,
+ :timeout => @timeout
+ ).and_return(@shell_out)
+ expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
+ end
+ end
+
+ context "after loading the current resource" do
+ before do
+ @current_resource = Chef::Resource::AptPackage.new("irssi", @run_context)
+ @provider.current_resource = @current_resource
+ allow(@provider).to receive(:package_data).and_return({
+ "irssi" => {
+ virtual: false,
+ candidate_version: "0.8.12-7",
+ installed_version: nil,
+ },
+ "libmysqlclient15-dev" => {
+ virtual: true,
+ candidate_version: nil,
+ installed_version: nil,
+ },
+ })
+ end
+
+ describe "install_package" do
+ it "should run apt-get install with the package name and version" do
+ expect(@provider).to receive(:shell_out!). with(
+ "apt-get", "-q", "-y", "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confold", "install", "irssi=0.8.12-7",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @provider.install_package(["irssi"], ["0.8.12-7"])
+ end
+
+ it "should run apt-get install with the package name and version and options if specified" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get", "-q", "-y", "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confold", "--force-yes", "install", "irssi=0.8.12-7",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @new_resource.options("--force-yes")
+ @provider.install_package(["irssi"], ["0.8.12-7"])
+ end
+
+ it "should run apt-get install with the package name and version and default_release if there is one and provider is explicitly defined" do
+ @new_resource = nil
+ @new_resource = Chef::Resource::AptPackage.new("irssi", @run_context)
+ @new_resource.default_release("lenny-backports")
+ @new_resource.provider = nil
+ @provider.new_resource = @new_resource
+
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get", "-q", "-y", "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confold", "-o", "APT::Default-Release=lenny-backports", "install", "irssi=0.8.12-7",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+
+ @provider.install_package(["irssi"], ["0.8.12-7"])
+ end
+
+ it "should run apt-get install with the package name and quotes options if specified" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get", "-q", "-y", "--force-yes", "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confnew", "install", "irssi=0.8.12-7",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @new_resource.options('--force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confnew"')
+ @provider.install_package(["irssi"], ["0.8.12-7"])
+ end
+ end
+
+ describe Chef::Resource::AptPackage, "upgrade_package" do
+
+ it "should run install_package with the name and version" do
+ expect(@provider).to receive(:install_package).with(["irssi"], ["0.8.12-7"])
+ @provider.upgrade_package(["irssi"], ["0.8.12-7"])
+ end
+ end
+
+ describe Chef::Resource::AptPackage, "remove_package" do
+
+ it "should run apt-get remove with the package name" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get", "-q", "-y", "remove", "irssi",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @provider.remove_package(["irssi"], ["0.8.12-7"])
+ end
+
+ it "should run apt-get remove with the package name and options if specified" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get", "-q", "-y", "--force-yes", "remove", "irssi",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @new_resource.options("--force-yes")
+
+ @provider.remove_package(["irssi"], ["0.8.12-7"])
+ end
+ end
+
+ describe "when purging a package" do
+
+ it "should run apt-get purge with the package name" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get", "-q", "-y", "purge", "irssi",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @provider.purge_package(["irssi"], ["0.8.12-7"])
+ end
+
+ it "should run apt-get purge with the package name and options if specified" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get", "-q", "-y", "--force-yes", "purge", "irssi",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @new_resource.options("--force-yes")
+
+ @provider.purge_package(["irssi"], ["0.8.12-7"])
+ end
+ end
+
+ describe "when preseeding a package" do
+ before(:each) do
+ allow(@provider).to receive(:get_preseed_file).and_return("/tmp/irssi-0.8.12-7.seed")
+ end
+
+ it "should get the full path to the preseed response file" do
+ file = "/tmp/irssi-0.8.12-7.seed"
+
+ expect(@provider).to receive(:shell_out!).with(
+ "debconf-set-selections", "/tmp/irssi-0.8.12-7.seed",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+
+ @provider.preseed_package(file)
+ end
+
+ it "should run debconf-set-selections on the preseed file if it has changed" do
+ expect(@provider).to receive(:shell_out!).with(
+ "debconf-set-selections", "/tmp/irssi-0.8.12-7.seed",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ file = @provider.get_preseed_file("irssi", "0.8.12-7")
+ @provider.preseed_package(file)
end
- context "after loading the current resource" do
- before do
- @current_resource = resource_klass.new("irssi", @run_context)
- @provider.current_resource = @current_resource
- end
-
- describe "install_package" do
- it "should run apt-get install with the package name and version" do
- expect(@provider).to receive(:shell_out!). with(
- "apt-get -q -y install irssi=0.8.12-7",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
- @provider.install_package("irssi", "0.8.12-7")
- end
-
- it "should run apt-get install with the package name and version and options if specified" do
- expect(@provider).to receive(:shell_out!).with(
- "apt-get -q -y --force-yes install irssi=0.8.12-7",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
- @new_resource.options("--force-yes")
- @provider.install_package("irssi", "0.8.12-7")
- end
-
- it "should run apt-get install with the package name and version and default_release if there is one and provider is explicitly defined" do
- @new_resource = nil
- @new_resource = Chef::Resource::AptPackage.new("irssi", @run_context)
- @new_resource.default_release("lenny-backports")
- @new_resource.provider = nil
- @provider.new_resource = @new_resource
-
- expect(@provider).to receive(:shell_out!).with(
- "apt-get -q -y -o APT::Default-Release=lenny-backports install irssi=0.8.12-7",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
-
- @provider.install_package("irssi", "0.8.12-7")
- end
- end
-
- describe resource_klass, "upgrade_package" do
-
- it "should run install_package with the name and version" do
- expect(@provider).to receive(:install_package).with("irssi", "0.8.12-7")
- @provider.upgrade_package("irssi", "0.8.12-7")
- end
- end
-
- describe resource_klass, "remove_package" do
-
- it "should run apt-get remove with the package name" do
- expect(@provider).to receive(:shell_out!).with(
- "apt-get -q -y remove irssi",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
- @provider.remove_package("irssi", "0.8.12-7")
- end
-
- it "should run apt-get remove with the package name and options if specified" do
- expect(@provider).to receive(:shell_out!).with(
- "apt-get -q -y --force-yes remove irssi",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
- @new_resource.options("--force-yes")
-
- @provider.remove_package("irssi", "0.8.12-7")
- end
- end
-
- describe "when purging a package" do
-
- it "should run apt-get purge with the package name" do
- expect(@provider).to receive(:shell_out!).with(
- "apt-get -q -y purge irssi",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
- @provider.purge_package("irssi", "0.8.12-7")
- end
-
- it "should run apt-get purge with the package name and options if specified" do
- expect(@provider).to receive(:shell_out!).with(
- "apt-get -q -y --force-yes purge irssi",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
- @new_resource.options("--force-yes")
-
- @provider.purge_package("irssi", "0.8.12-7")
- end
- end
-
- describe "when preseeding a package" do
- before(:each) do
- allow(@provider).to receive(:get_preseed_file).and_return("/tmp/irssi-0.8.12-7.seed")
- end
-
- it "should get the full path to the preseed response file" do
- file = "/tmp/irssi-0.8.12-7.seed"
-
- expect(@provider).to receive(:shell_out!).with(
- "debconf-set-selections /tmp/irssi-0.8.12-7.seed",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
-
- @provider.preseed_package(file)
- end
-
- it "should run debconf-set-selections on the preseed file if it has changed" do
- expect(@provider).to receive(:shell_out!).with(
- "debconf-set-selections /tmp/irssi-0.8.12-7.seed",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
- file = @provider.get_preseed_file("irssi", "0.8.12-7")
- @provider.preseed_package(file)
- end
-
- it "should not run debconf-set-selections if the preseed file has not changed" do
- allow(@provider).to receive(:check_all_packages_state)
- @current_resource.version "0.8.11"
- @new_resource.response_file "/tmp/file"
- allow(@provider).to receive(:get_preseed_file).and_return(false)
- expect(@provider).not_to receive(:shell_out!)
- @provider.run_action(:reconfig)
- end
- end
-
- describe "when reconfiguring a package" do
- it "should run dpkg-reconfigure package" do
- expect(@provider).to receive(:shell_out!).with(
- "dpkg-reconfigure irssi",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
- @provider.reconfig_package("irssi", "0.8.12-7")
- end
- end
-
- describe "when installing a virtual package" do
- it "should install the package without specifying a version" do
- @provider.is_virtual_package["libmysqlclient-dev"] = true
- expect(@provider).to receive(:shell_out!).with(
- "apt-get -q -y install libmysqlclient-dev",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
- @provider.install_package("libmysqlclient-dev", "not_a_real_version")
- end
- end
-
- describe "when installing multiple packages" do
- it "can install a virtual package followed by a non-virtual package" do
- # https://github.com/chef/chef/issues/2914
- @provider.is_virtual_package["libmysqlclient-dev"] = true
- @provider.is_virtual_package["irssi"] = false
- expect(@provider).to receive(:shell_out!).with(
- "apt-get -q -y install libmysqlclient-dev irssi=0.8.12-7",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
- @provider.install_package(["libmysqlclient-dev", "irssi"], ["not_a_real_version", "0.8.12-7"])
- end
- end
+ it "should not run debconf-set-selections if the preseed file has not changed" do
+ allow(@provider).to receive(:check_all_packages_state)
+ @current_resource.version "0.8.11"
+ @new_resource.response_file "/tmp/file"
+ allow(@provider).to receive(:get_preseed_file).and_return(false)
+ expect(@provider).not_to receive(:shell_out!)
+ @provider.run_action(:reconfig)
+ end
+ end
+
+ describe "when reconfiguring a package" do
+ it "should run dpkg-reconfigure package" do
+ expect(@provider).to receive(:shell_out!).with(
+ "dpkg-reconfigure", "irssi",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @provider.reconfig_package("irssi", "0.8.12-7")
+ end
+ end
+
+ describe "when locking a package" do
+ it "should run apt-mark hold package" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-mark", "hold", "irssi",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @provider.lock_package("irssi", "0.8.12-7")
+ end
+ it "should not lock if the package is already locked" do
+ allow(@provider).to receive(:shell_out_compact_timeout!).with(
+ "apt-mark", "showhold"
+ ).and_return(instance_double(
+ Mixlib::ShellOut, stdout: "irssi")
+ )
+ expect(logger).to receive(:trace).with("#{@provider.new_resource} is already locked")
+
+ @provider.action_lock
+ end
+ end
+
+ describe "when unlocking a package" do
+ it "should run apt-mark unhold package" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-mark", "unhold", "irssi",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @provider.unlock_package("irssi", "0.8.12-7")
+ end
+ it "should not unlock if the package is already unlocked" do
+ allow(@provider).to receive(:shell_out_compact_timeout!).with(
+ "apt-mark", "showhold"
+ ).and_return(instance_double(
+ Mixlib::ShellOut, stdout: "")
+ )
+ expect(logger).to receive(:trace).with("#{@provider.new_resource} is already unlocked")
+
+ @provider.action_unlock
+ end
+ end
+
+ describe "when installing a virtual package" do
+ it "should install the package without specifying a version" do
+ @provider.package_data["libmysqlclient15-dev"][:virtual] = true
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get", "-q", "-y", "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confold", "install", "libmysqlclient15-dev",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @provider.install_package(["libmysqlclient15-dev"], ["not_a_real_version"])
+ end
+ end
+
+ describe "when removing a virtual package" do
+ it "should remove the resolved name instead of the virtual package name" do
+ expect(@provider).to receive(:resolve_virtual_package_name).with("libmysqlclient15-dev").and_return("libmysqlclient-dev")
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get", "-q", "-y", "remove", "libmysqlclient-dev",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @provider.remove_package(["libmysqlclient15-dev"], ["not_a_real_version"])
+ end
+ end
+
+ describe "when purging a virtual package" do
+ it "should purge the resolved name instead of the virtual package name" do
+ expect(@provider).to receive(:resolve_virtual_package_name).with("libmysqlclient15-dev").and_return("libmysqlclient-dev")
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get", "-q", "-y", "purge", "libmysqlclient-dev",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @provider.purge_package(["libmysqlclient15-dev"], ["not_a_real_version"])
+ end
+ end
+
+ describe "when installing multiple packages" do
+ it "can install a virtual package followed by a non-virtual package" do
+ # https://github.com/chef/chef/issues/2914
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get", "-q", "-y", "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confold", "install", "libmysqlclient15-dev", "irssi=0.8.12-7",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @provider.install_package(["libmysqlclient15-dev", "irssi"], ["not_a_real_version", "0.8.12-7"])
+ end
+ end
+
+ describe "#action_install" do
+ it "should run dpkg to compare versions if an existing version is installed" do
+ allow(@provider).to receive(:get_current_versions).and_return("1.4.0")
+ allow(@new_resource).to receive(:allow_downgrade).and_return(false)
+ expect(@provider).to receive(:shell_out_compact_timeout).with(
+ "dpkg", "--compare-versions", "1.4.0", "gt", "0.8.12-7"
+ ).and_return(double(error?: false))
+ @provider.run_action(:upgrade)
+ end
+
+ it "should install the package if the installed version is older" do
+ allow(@provider).to receive(:get_current_versions).and_return("0.4.0")
+ allow(@new_resource).to receive(:allow_downgrade).and_return(false)
+ expect(@provider).to receive(:version_compare).and_return(-1)
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get", "-q", "-y", "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confold", "install", "irssi=0.8.12-7",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @provider.run_action(:upgrade)
+ end
+ it "should not compare versions if an existing version is not installed" do
+ allow(@provider).to receive(:get_current_versions).and_return(nil)
+ allow(@new_resource).to receive(:allow_downgrade).and_return(false)
+ expect(@provider).not_to receive(:version_compare)
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get", "-q", "-y", "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confold", "install", "irssi=0.8.12-7",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive" },
+ :timeout => @timeout
+ )
+ @provider.run_action(:upgrade)
end
end
end
diff --git a/spec/unit/provider/package/aix_spec.rb b/spec/unit/provider/package/bff_spec.rb
index 6940874a43..abe1d4155c 100644
--- a/spec/unit/provider/package/aix_spec.rb
+++ b/spec/unit/provider/package/bff_spec.rb
@@ -18,17 +18,19 @@
#
require "spec_helper"
-describe Chef::Provider::Package::Aix do
+describe Chef::Provider::Package::Bff do
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
before(:each) do
@node = Chef::Node.new
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
+ allow(@run_context).to receive(:logger).and_return(logger)
@new_resource = Chef::Resource::Package.new("samba.base")
@new_resource.source("/tmp/samba.base")
- @provider = Chef::Provider::Package::Aix.new(@new_resource, @run_context)
- allow(::File).to receive(:exists?).and_return(true)
+ @provider = Chef::Provider::Package::Bff.new(@new_resource, @run_context)
+ allow(::File).to receive(:exist?).with(@new_resource.source).and_return(true)
end
describe "assessing the current package status" do
@@ -41,23 +43,23 @@ describe Chef::Provider::Package::Aix do
it "should create a current resource with the name of new_resource" do
status = double("Status", :stdout => @bffinfo, :exitstatus => 0)
- expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base", timeout: 900).and_return(status)
- expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base", timeout: 900).and_return(@empty_status)
+ expect(@provider).to receive(:shell_out).with("installp", "-L", "-d", "/tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out).with("lslpp", "-lcq", "samba.base", timeout: 900).and_return(@empty_status)
@provider.load_current_resource
expect(@provider.current_resource.name).to eq("samba.base")
end
it "should set the current resource bff package name to the new resource bff package name" do
status = double("Status", :stdout => @bffinfo, :exitstatus => 0)
- expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base", timeout: 900).and_return(status)
- expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base", timeout: 900).and_return(@empty_status)
+ expect(@provider).to receive(:shell_out).with("installp", "-L", "-d", "/tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out).with("lslpp", "-lcq", "samba.base", timeout: 900).and_return(@empty_status)
@provider.load_current_resource
expect(@provider.current_resource.package_name).to eq("samba.base")
end
it "should raise an exception if a source is supplied but not found" do
allow(@provider).to receive(:shell_out).and_return(@empty_status)
- allow(::File).to receive(:exists?).and_return(false)
+ allow(::File).to receive(:exist?).with(@new_resource.source).and_return(false)
@provider.load_current_resource
@provider.define_resource_requirements
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Package)
@@ -65,8 +67,21 @@ describe Chef::Provider::Package::Aix do
it "should get the source package version from lslpp if provided" do
status = double("Status", :stdout => @bffinfo, :exitstatus => 0)
- expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base", timeout: 900).and_return(status)
- expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base", timeout: 900).and_return(@empty_status)
+ expect(@provider).to receive(:shell_out).with("installp", "-L", "-d", "/tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out).with("lslpp", "-lcq", "samba.base", timeout: 900).and_return(@empty_status)
+ @provider.load_current_resource
+
+ expect(@provider.current_resource.package_name).to eq("samba.base")
+ expect(@new_resource.version).to eq("3.3.12.0")
+ end
+
+ it "should warn if the package is not a fileset" do
+ info = "samba.base:samba.base.samples:3.3.12.0::COMMITTED:I:Samba for AIX:
+ /etc/objrepos:samba.base:3.3.12.0::COMMITTED:I:Samba for AIX:"
+ status = double("Status", :stdout => info, :exitstatus => 0)
+ expect(@provider).to receive(:shell_out).with("installp", "-L", "-d", "/tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out).with("lslpp", "-lcq", "samba.base", timeout: 900).and_return(@empty_status)
+ expect(logger).to receive(:warn).once.with(%r{bff package by product name})
@provider.load_current_resource
expect(@provider.current_resource.package_name).to eq("samba.base")
@@ -77,8 +92,8 @@ describe Chef::Provider::Package::Aix do
status = double("Status", :stdout => @bffinfo, :exitstatus => 0)
@stdout = StringIO.new(@bffinfo)
@stdin, @stderr = StringIO.new, StringIO.new
- expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base", timeout: 900).and_return(status)
- expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out).with("installp", "-L", "-d", "/tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out).with("lslpp", "-lcq", "samba.base", timeout: 900).and_return(status)
@provider.load_current_resource
expect(@provider.current_resource.version).to eq("3.3.12.0")
end
@@ -86,7 +101,7 @@ describe Chef::Provider::Package::Aix do
it "should raise an exception if the source is not set but we are installing" do
status = double("Status", :stdout => "", :exitstatus => 1, :format_for_exception => "")
@new_resource = Chef::Resource::Package.new("samba.base")
- @provider = Chef::Provider::Package::Aix.new(@new_resource, @run_context)
+ @provider = Chef::Provider::Package::Bff.new(@new_resource, @run_context)
allow(@provider).to receive(:shell_out).and_return(status)
expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
end
@@ -99,8 +114,8 @@ describe Chef::Provider::Package::Aix do
it "should return a current resource with a nil version if the package is not found" do
status = double("Status", :stdout => @bffinfo, :exitstatus => 0)
- expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base", timeout: 900).and_return(status)
- expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base", timeout: 900).and_return(@empty_status)
+ expect(@provider).to receive(:shell_out).with("installp", "-L", "-d", "/tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out).with("lslpp", "-lcq", "samba.base", timeout: 900).and_return(@empty_status)
@provider.load_current_resource
expect(@provider.current_resource.version).to be_nil
end
@@ -109,7 +124,7 @@ describe Chef::Provider::Package::Aix do
wrongbffinfo = "/usr/lib/objrepos:openssl.base:0.9.8.2400::COMMITTED:I:Open Secure Socket Layer:
/etc/objrepos:openssl.base:0.9.8.2400::COMMITTED:I:Open Secure Socket Layer:"
status = double("Status", :stdout => wrongbffinfo, :exitstatus => 0)
- expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out).with("installp", "-L", "-d", "/tmp/samba.base", timeout: 900).and_return(status)
expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package)
end
end
@@ -117,7 +132,7 @@ describe Chef::Provider::Package::Aix do
describe "candidate_version" do
it "should return the candidate_version variable if already setup" do
@provider.candidate_version = "3.3.12.0"
- expect(@provider).not_to receive(:shell_out )
+ expect(@provider).not_to receive(:shell_out)
@provider.candidate_version
end
@@ -137,34 +152,34 @@ describe Chef::Provider::Package::Aix do
describe "install and upgrade" do
it "should run installp -aYF -d with the package source to install" do
- expect(@provider).to receive(:shell_out!).with("installp -aYF -d /tmp/samba.base samba.base", timeout: 900)
+ expect(@provider).to receive(:shell_out!).with("installp", "-aYF", "-d", "/tmp/samba.base", "samba.base", timeout: 900)
@provider.install_package("samba.base", "3.3.12.0")
end
- it "should run when the package is a path to install" do
+ it "should run installp -aYF -d when the package is a path to install" do
@new_resource = Chef::Resource::Package.new("/tmp/samba.base")
- @provider = Chef::Provider::Package::Aix.new(@new_resource, @run_context)
+ @provider = Chef::Provider::Package::Bff.new(@new_resource, @run_context)
expect(@new_resource.source).to eq("/tmp/samba.base")
- expect(@provider).to receive(:shell_out!).with("installp -aYF -d /tmp/samba.base /tmp/samba.base", timeout: 900)
+ expect(@provider).to receive(:shell_out!).with("installp", "-aYF", "-d", "/tmp/samba.base", "/tmp/samba.base", timeout: 900)
@provider.install_package("/tmp/samba.base", "3.3.12.0")
end
it "should run installp with -eLogfile option." do
- allow(@new_resource).to receive(:options).and_return("-e/tmp/installp.log")
- expect(@provider).to receive(:shell_out!).with("installp -aYF -e/tmp/installp.log -d /tmp/samba.base samba.base", timeout: 900)
+ @new_resource.options("-e/tmp/installp.log")
+ expect(@provider).to receive(:shell_out!).with("installp", "-aYF", "-e/tmp/installp.log", "-d", "/tmp/samba.base", "samba.base", timeout: 900)
@provider.install_package("samba.base", "3.3.12.0")
end
end
describe "remove" do
it "should run installp -u samba.base to remove the package" do
- expect(@provider).to receive(:shell_out!).with("installp -u samba.base", timeout: 900)
+ expect(@provider).to receive(:shell_out!).with("installp", "-u", "samba.base", timeout: 900)
@provider.remove_package("samba.base", "3.3.12.0")
end
it "should run installp -u -e/tmp/installp.log with options -e/tmp/installp.log" do
- allow(@new_resource).to receive(:options).and_return("-e/tmp/installp.log")
- expect(@provider).to receive(:shell_out!).with("installp -u -e/tmp/installp.log samba.base", timeout: 900)
+ @new_resource.options("-e/tmp/installp.log")
+ expect(@provider).to receive(:shell_out!).with("installp", "-u", "-e/tmp/installp.log", "samba.base", timeout: 900)
@provider.remove_package("samba.base", "3.3.12.0")
end
diff --git a/spec/unit/provider/package/cab_spec.rb b/spec/unit/provider/package/cab_spec.rb
new file mode 100644
index 0000000000..5c86f781f3
--- /dev/null
+++ b/spec/unit/provider/package/cab_spec.rb
@@ -0,0 +1,272 @@
+#
+# Author:: Vasundhara Jagdale (<vasundhara.jagdale@msystechnologies.com>)
+# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Provider::Package::Cab do
+ let(:timeout) {}
+
+ let(:new_resource) { Chef::Resource::CabPackage.new("windows_test_pkg") }
+
+ let(:provider) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ Chef::Provider::Package::Cab.new(new_resource, run_context)
+ end
+
+ let(:installed_package_list_stdout) do
+ <<-EOF
+Packages listing:
+Package Identity : Package_for_KB2999486~31bf3856ad364e35~amd64~~6.1.9768.0
+Package Identity : Package_for_KB2994825~31bf3856ad364e35~amd64~~6.1.7601.0
+ EOF
+ end
+
+ let(:package_version_stdout) do
+ <<-EOF
+Package information:
+Package Identity : Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0
+State : Installed
+Dependency : Language Pack
+The operation completed successfully
+ EOF
+ end
+
+ before do
+ new_resource.source = File.join("#{ENV['TEMP']}", "test6.1-kb2664825-v3-x64.cab")
+ installed_package_list_obj = double(stdout: installed_package_list_stdout)
+ allow(provider).to receive(:dism_command).with("/Get-Packages").and_return(installed_package_list_obj)
+ package_version_obj = double(stdout: package_version_stdout)
+ allow(provider).to receive(:dism_command).with("/Get-PackageInfo /PackagePath:\"#{new_resource.source}\"").and_return(package_version_obj)
+ end
+
+ def allow_package_info(package_path = nil, package_name = nil)
+ get_package_info_stdout = <<-EOF
+Deployment Image Servicing and Management tool
+Version: 6.1.7600.16385
+
+Image Version: 6.1.7600.16385
+
+Package information:
+Package Identity : Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0
+Applicable : Yes
+Copyright : Microsoft Corporation
+Company : Microsoft Corporation
+State : Installed
+Dependency : Language Pack
+The operation completed successfully
+ EOF
+ get_package_info_obj = double(stdout: get_package_info_stdout)
+ if package_path
+ allow(provider).to receive(:dism_command).with("/Get-PackageInfo /PackagePath:\"#{package_path}\"").and_return(get_package_info_obj)
+ else
+ allow(provider).to receive(:dism_command).with("/Get-PackageInfo /PackageName:\"#{package_name}\"").and_return(get_package_info_obj)
+ end
+ end
+
+ def allow_get_packages
+ get_packages_stdout = <<-EOF
+Deployment Image Servicing and Management tool
+Version: 6.1.7600.16385
+
+Image Version: 6.1.7600.16385
+
+Packages listing:
+
+Package Identity : Package_for_KB2999486~31bf3856ad364e35~amd64~~6.1.9768.0
+State : Installed
+Release Type : Language Pack
+Install Time : 2/11/2015 11:33 PM
+
+Package Identity : Package_for_KB2994825~31bf3856ad364e35~amd64~~6.1.7601.0
+State : Installed
+Release Type : Language Pack
+Install Time : 2/11/2015 11:33 PM
+
+Package Identity : Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0
+State : Installed
+Release Type : Feature Pack
+Install Time : 11/21/2010 3:40 AM
+
+The operation completed successfully.
+ EOF
+ get_packages_obj = double(stdout: get_packages_stdout)
+ allow(provider).to receive(:dism_command).with("/Get-Packages").and_return(get_packages_obj)
+ end
+
+ describe "#load_current_resource" do
+ it "returns a current_resource" do
+ expect(provider.load_current_resource).to be_kind_of(Chef::Resource::CabPackage)
+ end
+
+ it "sets the current_resource.version to nil when the package is not installed" do
+ provider.load_current_resource
+ expect(provider.current_resource.version).to eql(nil)
+ end
+
+ it "sets the new resource package version" do
+ provider.load_current_resource
+ expect(provider.new_resource.version).to eql("6.1.3.0")
+ end
+ end
+
+ describe "#source_resource" do
+ before do
+ new_resource.source = "https://www.something.com/Test6.1-KB2664825-v3-x64.cab"
+ new_resource.cookbook_name = "cab_package"
+ end
+
+ it "sets the desired parameters of downloades cab file" do
+ allow(provider).to receive(:default_download_cache_path).and_return("C:\\chef\\cache\\package")
+ source_resource = provider.source_resource
+ expect(source_resource.path).to be == "C:\\chef\\cache\\package"
+ expect(source_resource.name).to be == "windows_test_pkg"
+ expect(source_resource.source).to be == [new_resource.source]
+ expect(source_resource.cookbook_name).to be == "cab_package"
+ end
+ end
+
+ describe "#default_download_cache_path" do
+ before do
+ new_resource.source = "https://www.something.com/Test6.1-KB2664825-v3-x64.cab"
+ end
+
+ it "returns a clean cache path where the cab file is downloaded" do
+ allow(Chef::FileCache).to receive(:create_cache_path).and_return(ENV["TEMP"])
+ path = provider.default_download_cache_path
+ if windows?
+ expect(path).to be == File.join("#{ENV['TEMP']}", "\\", "Test6.1-KB2664825-v3-x64.cab")
+ else
+ expect(path).to be == File.join("#{ENV['TEMP']}", "Test6.1-KB2664825-v3-x64.cab")
+ end
+ end
+ end
+
+ describe "#cab_file_source" do
+ context "when local file path is set" do
+ it "returns local cab file source path" do
+ new_resource.source = File.join("#{ENV['TEMP']}", "test6.1-kb2664825-v3-x64.cab")
+ path = provider.cab_file_source
+ if windows?
+ expect(path).to be == File.join("#{ENV['TEMP'].downcase}", "\\", "test6.1-kb2664825-v3-x64.cab")
+ else
+ expect(path).to be == File.join("#{ENV['TEMP']}", "test6.1-kb2664825-v3-x64.cab")
+ end
+ end
+ end
+ context "when url is set" do
+ it "calls download_source_file method" do
+ new_resource.source = "https://www.something.com/test6.1-kb2664825-v3-x64.cab"
+ if windows?
+ expect(provider).to receive(:download_source_file).and_return(File.join("#{ENV['TEMP'].downcase}", "\\", "test6.1-kb2664825-v3-x64.cab"))
+ else
+ expect(provider).to receive(:download_source_file).and_return(File.join("#{ENV['TEMP']}", "test6.1-kb2664825-v3-x64.cab"))
+ end
+ provider.cab_file_source
+ end
+ end
+ end
+
+ describe "#initialize" do
+ it "returns the correct class" do
+ expect(provider).to be_kind_of(Chef::Provider::Package::Cab)
+ end
+ end
+
+ describe "#package_version" do
+ it "returns the new package version" do
+ allow_package_info(new_resource.source, nil)
+ expect(provider.package_version).to eql("6.1.3.0")
+ end
+ end
+
+ describe "#installed_version" do
+ it "returns the current installed version of package" do
+ allow_package_info(new_resource.source, nil)
+ allow_get_packages
+ allow_package_info(nil, "Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0")
+ expect(provider.installed_version).to eql("6.1.3.0")
+ end
+ end
+
+ describe "#action_remove" do
+ it "does nothing when the package is already removed" do
+ provider.load_current_resource
+ expect(provider).not_to receive(:remove_package)
+ provider.run_action(:remove)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "removes packages if package is installed" do
+ allow_package_info(new_resource.source, nil)
+ allow_get_packages
+ allow_package_info(nil, "Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0")
+ provider.load_current_resource
+ expect(provider.installed_version).not_to eql(nil)
+ expect(provider).to receive(:remove_package)
+ provider.run_action(:remove)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+
+ describe "#action_install" do
+ it "installs package if already not installed" do
+ provider.load_current_resource
+ expect(provider.installed_version).to eql(nil)
+ expect(provider).to receive(:install_package)
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "does not install package if already installed" do
+ allow_package_info(new_resource.source, nil)
+ allow_get_packages
+ allow_package_info(nil, "Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0")
+ provider.load_current_resource
+ expect(provider.installed_version).not_to eql(nil)
+ expect(provider).not_to receive(:install_package)
+ provider.run_action(:install)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+
+ context "Invalid package source" do
+ def package_version_stdout
+ package_version_stdout = <<-EOF
+Deployment Image Servicing and Management tool
+Version: 6.1.7600.16385
+Image Version: 6.1.7600.16385
+An error occurred trying to open - c:\\temp\\test6.1-KB2664825-v3-x64.cab Error: 0x80070003
+Error: 3
+The system cannot find the path specified.
+The DISM log file can be found at C:\\Windows\\Logs\\DISM\\dism.log.
+ EOF
+ end
+
+ before do
+ new_resource.source = "#{ENV['TEMP']}/test6.1-kb2664825-v3-x64.cab"
+ installed_package_list_obj = double(stdout: installed_package_list_stdout)
+ allow(provider).to receive(:dism_command).with("/Get-Packages").and_return(installed_package_list_obj)
+ end
+
+ it "raises error for invalid source path or file" do
+ expect { provider.load_current_resource }.to raise_error(Chef::Exceptions::Package, "DISM: The system cannot find the path or file specified.")
+ end
+ end
+end
diff --git a/spec/unit/provider/package/chocolatey_spec.rb b/spec/unit/provider/package/chocolatey_spec.rb
index 8a69cf3da4..ff9dc3ca6b 100644
--- a/spec/unit/provider/package/chocolatey_spec.rb
+++ b/spec/unit/provider/package/chocolatey_spec.rb
@@ -36,6 +36,7 @@ describe Chef::Provider::Package::Chocolatey do
# installed packages (ConEmu is upgradable)
let(:local_list_stdout) do
<<-EOF
+Chocolatey v0.9.9.11
chocolatey|0.9.9.11
ConEmu|15.10.25.0
EOF
@@ -45,11 +46,12 @@ ConEmu|15.10.25.0
allow(provider).to receive(:choco_install_path).and_return(choco_install_path)
allow(provider).to receive(:choco_exe).and_return(choco_exe)
local_list_obj = double(:stdout => local_list_stdout)
- allow(provider).to receive(:shell_out!).with("#{choco_exe} list -l -r", { :timeout => timeout }).and_return(local_list_obj)
+ allow(provider).to receive(:shell_out!).with("#{choco_exe} list -l -r", { :returns => [0], :timeout => timeout }).and_return(local_list_obj)
end
def allow_remote_list(package_names, args = nil)
remote_list_stdout = <<-EOF
+Chocolatey v0.9.9.11
chocolatey|0.9.9.11
ConEmu|15.10.25.1
Git|2.6.1
@@ -57,7 +59,9 @@ Git|2.6.2
munin-node|1.6.1.20130823
EOF
remote_list_obj = double(stdout: remote_list_stdout)
- allow(provider).to receive(:shell_out!).with("#{choco_exe} list -ar #{package_names.join ' '}#{args}", { timeout: timeout }).and_return(remote_list_obj)
+ package_names.each do |pkg|
+ allow(provider).to receive(:shell_out!).with("#{choco_exe} list -r #{pkg}#{args}", { :returns => [0], timeout: timeout }).and_return(remote_list_obj)
+ end
end
describe "#initialize" do
@@ -82,12 +86,6 @@ munin-node|1.6.1.20130823
expect(provider.candidate_version).to eql(["2.6.1"])
end
- it "should set the candidate_version to nill if pinning to bogus version" do
- allow_remote_list(["git"])
- new_resource.version("2.5.0")
- expect(provider.candidate_version).to eql([nil])
- end
-
it "should set the candidate_version to nil if there is no candidate" do
allow_remote_list(["vim"])
new_resource.package_name("vim")
@@ -184,7 +182,7 @@ munin-node|1.6.1.20130823
it "should install a single package" do
allow_remote_list(["git"])
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y git", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y git", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -195,7 +193,7 @@ munin-node|1.6.1.20130823
allow_remote_list(["git"])
new_resource.timeout(timeout)
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y git", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y git", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -224,7 +222,7 @@ munin-node|1.6.1.20130823
new_resource.package_name("ConEmu")
new_resource.version("15.10.25.1")
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y -version 15.10.25.1 conemu", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y --version 15.10.25.1 conemu", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -237,7 +235,7 @@ munin-node|1.6.1.20130823
new_resource.package_name(%w{chocolatey ConEmu})
new_resource.version([nil, "15.10.25.1"])
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y -version 15.10.25.1 conemu", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y --version 15.10.25.1 conemu", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -247,7 +245,7 @@ munin-node|1.6.1.20130823
new_resource.package_name("conemu")
new_resource.version("15.10.25.1")
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y -version 15.10.25.1 conemu", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y --version 15.10.25.1 conemu", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -257,8 +255,8 @@ munin-node|1.6.1.20130823
new_resource.package_name(%w{ConEmu git})
new_resource.version(["15.10.25.1", nil])
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y -version 15.10.25.1 conemu", { :timeout => timeout }).and_return(double)
- expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y git", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y --version 15.10.25.1 conemu", { :returns => [0], :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y git", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -267,7 +265,7 @@ munin-node|1.6.1.20130823
allow_remote_list(["git", "munin-node"])
new_resource.package_name(["git", "munin-node"])
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y git munin-node", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y git munin-node", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -277,7 +275,7 @@ munin-node|1.6.1.20130823
allow_remote_list(["git"], " -source localpackages")
new_resource.source("localpackages")
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y -source localpackages git", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y -source localpackages git", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -287,7 +285,7 @@ munin-node|1.6.1.20130823
allow_remote_list(["git"])
new_resource.options("-force")
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y -force git", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} install -y -force git", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -299,14 +297,6 @@ munin-node|1.6.1.20130823
expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
end
- it "installing a package version that does not exist throws an error" do
- allow_remote_list(["git"])
- new_resource.package_name("git")
- new_resource.version("2.7.0")
- provider.load_current_resource
- expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
- end
-
it "installing multiple packages with a package that does not exist throws an error" do
allow_remote_list(["git", "package-does-not-exist"])
new_resource.package_name(["git", "package-does-not-exist"])
@@ -329,7 +319,7 @@ munin-node|1.6.1.20130823
it "should install a package that is not installed" do
allow_remote_list(["git"])
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} upgrade -y git", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} upgrade -y git", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:upgrade)
expect(new_resource).to be_updated_by_last_action
end
@@ -338,7 +328,7 @@ munin-node|1.6.1.20130823
allow_remote_list(["ConEmu"])
new_resource.package_name("ConEmu")
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} upgrade -y conemu", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} upgrade -y conemu", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:upgrade)
expect(new_resource).to be_updated_by_last_action
end
@@ -347,7 +337,7 @@ munin-node|1.6.1.20130823
allow_remote_list(["conemu"])
new_resource.package_name("conemu")
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} upgrade -y conemu", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} upgrade -y conemu", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:upgrade)
expect(new_resource).to be_updated_by_last_action
end
@@ -356,7 +346,7 @@ munin-node|1.6.1.20130823
allow_remote_list(["chocolatey"])
new_resource.package_name("chocolatey")
provider.load_current_resource
- expect(provider).not_to receive(:shell_out!).with("#{choco_exe} upgrade -y chocolatey", { :timeout => timeout })
+ expect(provider).not_to receive(:shell_out!).with("#{choco_exe} upgrade -y chocolatey", { :returns => [0], :timeout => timeout })
provider.run_action(:upgrade)
expect(new_resource).not_to be_updated_by_last_action
end
@@ -365,7 +355,7 @@ munin-node|1.6.1.20130823
allow_remote_list(["git"])
new_resource.version("2.6.2")
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} upgrade -y -version 2.6.2 git", { :timeout => timeout })
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} upgrade -y --version 2.6.2 git", { :returns => [0], :timeout => timeout })
provider.run_action(:upgrade)
expect(new_resource).to be_updated_by_last_action
end
@@ -373,7 +363,7 @@ munin-node|1.6.1.20130823
it "upgrading multiple packages uses a single command" do
allow_remote_list(%w{conemu git})
new_resource.package_name(%w{conemu git})
- expect(provider).to receive(:shell_out!).with("#{choco_exe} upgrade -y conemu git", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} upgrade -y conemu git", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:upgrade)
expect(new_resource).to be_updated_by_last_action
end
@@ -425,7 +415,7 @@ munin-node|1.6.1.20130823
allow_remote_list(["ConEmu"])
new_resource.package_name("ConEmu")
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} uninstall -y ConEmu", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} uninstall -y ConEmu", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:remove)
expect(new_resource).to be_updated_by_last_action
end
@@ -434,7 +424,7 @@ munin-node|1.6.1.20130823
allow_remote_list(["conemu"])
new_resource.package_name("conemu")
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} uninstall -y conemu", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} uninstall -y conemu", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:remove)
expect(new_resource).to be_updated_by_last_action
end
@@ -444,24 +434,11 @@ munin-node|1.6.1.20130823
allow_remote_list(%w{git conemu})
new_resource.package_name(%w{git conemu})
provider.load_current_resource
- expect(provider).to receive(:shell_out!).with("#{choco_exe} uninstall -y conemu", { :timeout => timeout }).and_return(double)
+ expect(provider).to receive(:shell_out!).with("#{choco_exe} uninstall -y conemu", { :returns => [0], :timeout => timeout }).and_return(double)
provider.run_action(:remove)
expect(new_resource).to be_updated_by_last_action
end
end
-
- describe "#action_uninstall" do
- it "should call :remove with a deprecation warning" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- expect(Chef::Log).to receive(:deprecation).with(/please use :remove/)
- allow_remote_list(["ConEmu"])
- new_resource.package_name("ConEmu")
- provider.load_current_resource
- expect(provider).to receive(:remove_package)
- provider.run_action(:uninstall)
- expect(new_resource).to be_updated_by_last_action
- end
- end
end
describe "behavior when Chocolatey is not installed" do
@@ -474,18 +451,18 @@ describe "behavior when Chocolatey is not installed" do
Chef::Provider::Package::Chocolatey.new(new_resource, run_context)
end
- before {
+ before do
# the shellout sometimes returns "", but test nil to be safe.
allow(provider).to receive(:choco_install_path).and_return(nil)
provider.instance_variable_set("@choco_install_path", nil)
# we don't care what this returns, but we have to let it be called.
allow(provider).to receive(:shell_out!).and_return(double(:stdout => ""))
- }
+ end
- let(:error_regex) {
+ let(:error_regex) do
/Could not locate.*install.*cookbook.*PowerShell.*GetEnvironmentVariable/m
- }
+ end
context "#choco_exe" do
it "triggers a MissingLibrary exception when Chocolatey is not installed" do
diff --git a/spec/unit/provider/package/dnf/python_helper_spec.rb b/spec/unit/provider/package/dnf/python_helper_spec.rb
new file mode 100644
index 0000000000..505217bf90
--- /dev/null
+++ b/spec/unit/provider/package/dnf/python_helper_spec.rb
@@ -0,0 +1,29 @@
+#
+# Copyright:: Copyright 2017-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+# NOTE: most of the tests of this functionality are baked into the func tests for the dnf package provider
+
+describe Chef::Provider::Package::Dnf::PythonHelper do
+ let(:helper) { Chef::Provider::Package::Dnf::PythonHelper.instance }
+
+ it "propagates stacktraces on stderr from the forked subprocess" do
+ allow(helper).to receive(:dnf_command).and_return("ruby -e 'raise \"your hands in the air\"'")
+ expect { helper.query(:whatprovides, "tcpdump") }.to raise_error(/your hands in the air/)
+ end
+end
diff --git a/spec/unit/provider/package/dpkg_spec.rb b/spec/unit/provider/package/dpkg_spec.rb
index 613f7a326e..e01e1d9cce 100644
--- a/spec/unit/provider/package/dpkg_spec.rb
+++ b/spec/unit/provider/package/dpkg_spec.rb
@@ -51,8 +51,8 @@ Conflicts: wget-ssl
end
before(:each) do
- allow(provider).to receive(:shell_out!).with("dpkg-deb -W #{source}", timeout: 900).and_return(dpkg_deb_status)
- allow(provider).to receive(:shell_out!).with("dpkg -s #{package}", timeout: 900, returns: [0, 1]).and_return(double(stdout: "", exitstatus: 1))
+ allow(provider).to receive(:shell_out!).with("dpkg-deb", "-W", source, timeout: 900).and_return(dpkg_deb_status)
+ allow(provider).to receive(:shell_out!).with("dpkg", "-s", package, timeout: 900, returns: [0, 1]).and_return(double(stdout: "", exitstatus: 1))
allow(::File).to receive(:exist?).with(source).and_return(true)
end
@@ -113,7 +113,7 @@ Conflicts: wget-ssl
describe "gets the source package version from dpkg-deb" do
def check_version(version)
status = double(:stdout => "wget\t#{version}", :exitstatus => 0)
- expect(provider).to receive(:shell_out!).with("dpkg-deb -W #{source}", timeout: 900).and_return(status)
+ expect(provider).to receive(:shell_out!).with("dpkg-deb", "-W", source, timeout: 900).and_return(status)
provider.load_current_resource
expect(provider.current_resource.package_name).to eq(["wget"])
expect(provider.candidate_version).to eq([version])
@@ -165,7 +165,7 @@ Conflicts: wget-ssl
end
it "should return the current version installed if found by dpkg" do
- allow(provider).to receive(:shell_out!).with("dpkg -s #{package}", timeout: 900, returns: [0, 1]).and_return(dpkg_s_status)
+ allow(provider).to receive(:shell_out!).with("dpkg", "-s", package, timeout: 900, returns: [0, 1]).and_return(dpkg_s_status)
provider.load_current_resource
expect(provider.current_resource.version).to eq(["1.11.4-1ubuntu1"])
end
@@ -178,7 +178,7 @@ Use dpkg --info (= dpkg-deb --info) to examine archive files,
and dpkg --contents (= dpkg-deb --contents) to list their contents.
EOF
)
- expect(provider).to receive(:shell_out!).with("dpkg -s #{package}", returns: [0, 1], timeout: 900).and_return(dpkg_s_status)
+ expect(provider).to receive(:shell_out!).with("dpkg", "-s", package, returns: [0, 1], timeout: 900).and_return(dpkg_s_status)
provider.load_current_resource
expect(provider.current_resource.version).to eq([nil])
end
@@ -192,7 +192,7 @@ Priority: extra
Section: ruby
EOF
)
- expect(provider).to receive(:shell_out!).with("dpkg -s #{package}", returns: [0, 1], timeout: 900).and_return(dpkg_s_status)
+ expect(provider).to receive(:shell_out!).with("dpkg", "-s", package, returns: [0, 1], timeout: 900).and_return(dpkg_s_status)
provider.load_current_resource
expect(provider.current_resource.version).to eq([nil])
end
@@ -201,13 +201,13 @@ Section: ruby
dpkg_s_status = double(
exitstatus: 3, stderr: "i am very, very angry with you. i'm very, very cross. go to your room.", stdout: ""
)
- expect(provider).to receive(:shell_out!).with("dpkg -s #{package}", returns: [0, 1], timeout: 900).and_raise(Mixlib::ShellOut::ShellCommandFailed)
+ expect(provider).to receive(:shell_out!).with("dpkg", "-s", package, returns: [0, 1], timeout: 900).and_raise(Mixlib::ShellOut::ShellCommandFailed)
expect { provider.load_current_resource }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
end
it "should raise an exception if dpkg-deb -W fails to run" do
status = double(:stdout => "", :exitstatus => -1)
- expect(provider).to receive(:shell_out_with_timeout!).with("dpkg-deb -W /tmp/wget_1.11.4-1ubuntu1_amd64.deb").and_raise(Mixlib::ShellOut::ShellCommandFailed)
+ expect(provider).to receive(:shell_out_compact_timeout!).with("dpkg-deb", "-W", "/tmp/wget_1.11.4-1ubuntu1_amd64.deb").and_raise(Mixlib::ShellOut::ShellCommandFailed)
expect { provider.load_current_resource }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
end
end
@@ -215,7 +215,7 @@ Section: ruby
describe Chef::Provider::Package::Dpkg, "install and upgrade" do
it "should run dpkg -i with the package source" do
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -i", nil, "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
+ "dpkg", "-i", "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
)
provider.load_current_resource
provider.run_action(:install)
@@ -224,7 +224,7 @@ Section: ruby
it "should run dpkg -i if the package is a path and the source is nil" do
new_resource.name "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -i", nil, "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
+ "dpkg", "-i", "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
)
provider.run_action(:install)
end
@@ -232,7 +232,7 @@ Section: ruby
it "should run dpkg -i if the package is a path and the source is nil for an upgrade" do
new_resource.name "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -i", nil, "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
+ "dpkg", "-i", "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
)
provider.run_action(:upgrade)
end
@@ -240,7 +240,7 @@ Section: ruby
it "should run dpkg -i with the package source and options if specified" do
new_resource.options "--force-yes"
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -i", "--force-yes", "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
+ "dpkg", "-i", "--force-yes", "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
)
provider.run_action(:install)
end
@@ -254,14 +254,14 @@ Section: ruby
describe Chef::Provider::Package::Dpkg, "remove and purge" do
it "should run dpkg -r to remove the package" do
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -r", nil, "wget"
+ "dpkg", "-r", "wget"
)
provider.remove_package(["wget"], ["1.11.4-1ubuntu1"])
end
it "should run dpkg -r to remove the package with options if specified" do
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -r", "--force-yes", "wget"
+ "dpkg", "-r", "--force-yes", "wget"
)
allow(new_resource).to receive(:options).and_return("--force-yes")
@@ -270,14 +270,14 @@ Section: ruby
it "should run dpkg -P to purge the package" do
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -P", nil, "wget"
+ "dpkg", "-P", "wget"
)
provider.purge_package(["wget"], ["1.11.4-1ubuntu1"])
end
it "should run dpkg -P to purge the package with options if specified" do
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -P", "--force-yes", "wget"
+ "dpkg", "-P", "--force-yes", "wget"
)
allow(new_resource).to receive(:options).and_return("--force-yes")
diff --git a/spec/unit/provider/package/easy_install_spec.rb b/spec/unit/provider/package/easy_install_spec.rb
deleted file mode 100644
index fa5eea00a2..0000000000
--- a/spec/unit/provider/package/easy_install_spec.rb
+++ /dev/null
@@ -1,114 +0,0 @@
-#
-# Author:: Joe Williams (<joe@joetify.com>)
-# Copyright:: Copyright 2009-2016, Joe Williams
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-
-describe Chef::Provider::Package::EasyInstall do
- before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::EasyInstallPackage.new("boto")
- @new_resource.version("1.8d")
-
- @provider = Chef::Provider::Package::EasyInstall.new(@new_resource, @run_context)
-
- @stdin = StringIO.new
- @stdout = StringIO.new
- @status = double("Status", :exitstatus => 0)
- @stderr = StringIO.new
- @pid = 2342
- allow(@provider).to receive(:popen4).and_return(@status)
- end
-
- describe "easy_install_binary_path" do
- it "should return a Chef::Provider::EasyInstall object" do
- provider = Chef::Provider::Package::EasyInstall.new(@node, @new_resource)
- expect(provider).to be_a_kind_of(Chef::Provider::Package::EasyInstall)
- end
-
- it "should set the current resources package name to the new resources package name" do
- allow($stdout).to receive(:write)
- @provider.load_current_resource
- expect(@provider.current_resource.package_name).to eq(@new_resource.package_name)
- end
-
- it "should return a relative path to easy_install if no easy_install_binary is given" do
- expect(@provider.easy_install_binary_path).to eql("easy_install")
- end
-
- it "should return a specific path to easy_install if a easy_install_binary is given" do
- expect(@new_resource).to receive(:easy_install_binary).and_return("/opt/local/bin/custom/easy_install")
- expect(@provider.easy_install_binary_path).to eql("/opt/local/bin/custom/easy_install")
- end
-
- end
-
- describe "actions_on_package" do
- it "should run easy_install with the package name and version" do
- expect(Chef).to receive(:log_deprecation).with(/easy_install package provider is deprecated/)
- expect(@provider).to receive(:run_command).with({
- :command => "easy_install \"boto==1.8d\"",
- })
- @provider.install_package("boto", "1.8d")
- end
-
- it "should run easy_install with the package name and version and specified options" do
- expect(Chef).to receive(:log_deprecation).with(/easy_install package provider is deprecated/)
- expect(@provider).to receive(:run_command).with({
- :command => "easy_install --always-unzip \"boto==1.8d\"",
- })
- allow(@new_resource).to receive(:options).and_return("--always-unzip")
- @provider.install_package("boto", "1.8d")
- end
-
- it "should run easy_install with the package name and version" do
- expect(Chef).to receive(:log_deprecation).with(/easy_install package provider is deprecated/)
- expect(@provider).to receive(:run_command).with({
- :command => "easy_install \"boto==1.8d\"",
- })
- @provider.upgrade_package("boto", "1.8d")
- end
-
- it "should run easy_install -m with the package name and version" do
- expect(Chef).to receive(:log_deprecation).with(/easy_install package provider is deprecated/)
- expect(@provider).to receive(:run_command).with({
- :command => "easy_install -m boto",
- })
- @provider.remove_package("boto", "1.8d")
- end
-
- it "should run easy_install -m with the package name and version and specified options" do
- expect(Chef).to receive(:log_deprecation).with(/easy_install package provider is deprecated/)
- expect(@provider).to receive(:run_command).with({
- :command => "easy_install -x -m boto",
- })
- allow(@new_resource).to receive(:options).and_return("-x")
- @provider.remove_package("boto", "1.8d")
- end
-
- it "should run easy_install -m with the package name and version" do
- expect(Chef).to receive(:log_deprecation).with(/easy_install package provider is deprecated/)
- expect(@provider).to receive(:run_command).with({
- :command => "easy_install -m boto",
- })
- @provider.purge_package("boto", "1.8d")
- end
-
- end
-end
diff --git a/spec/unit/provider/package/freebsd/pkg_spec.rb b/spec/unit/provider/package/freebsd/pkg_spec.rb
index 8890f62f73..4b3a94aa33 100644
--- a/spec/unit/provider/package/freebsd/pkg_spec.rb
+++ b/spec/unit/provider/package/freebsd/pkg_spec.rb
@@ -31,6 +31,8 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
@provider = Chef::Provider::Package::Freebsd::Pkg.new(@new_resource, @run_context)
@provider.current_resource = @current_resource
allow(::File).to receive(:exist?).with("/usr/ports/Makefile").and_return(false)
+ allow(::File).to receive(:exist?).with("/usr/ports/www/wordpress").and_return(false)
+ allow(::File).to receive(:exist?).with("www/wordpress").and_return(false)
end
describe "when determining the current package state" do
@@ -77,23 +79,21 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
it "should return the version number when it is installed" do
pkg_info = OpenStruct.new(:stdout => "zsh-4.3.6_7")
- expect(@provider).to receive(:shell_out!).with('pkg_info -E "zsh*"', env: nil, returns: [0, 1], timeout: 900).and_return(pkg_info)
- #@provider.should_receive(:popen4).with('pkg_info -E "zsh*"').and_yield(@pid, @stdin, ["zsh-4.3.6_7"], @stderr).and_return(@status)
+ expect(@provider).to receive(:shell_out!).with("pkg_info", "-E", "zsh*", env: nil, returns: [0, 1], timeout: 900).and_return(pkg_info)
allow(@provider).to receive(:package_name).and_return("zsh")
expect(@provider.current_installed_version).to eq("4.3.6_7")
end
it "does not set the current version number when the package is not installed" do
pkg_info = OpenStruct.new(:stdout => "")
- expect(@provider).to receive(:shell_out!).with('pkg_info -E "zsh*"', env: nil, returns: [0, 1], timeout: 900).and_return(pkg_info)
+ expect(@provider).to receive(:shell_out!).with("pkg_info", "-E", "zsh*", env: nil, returns: [0, 1], timeout: 900).and_return(pkg_info)
allow(@provider).to receive(:package_name).and_return("zsh")
expect(@provider.current_installed_version).to be_nil
end
it "should return the port path for a valid port name" do
whereis = OpenStruct.new(:stdout => "zsh: /usr/ports/shells/zsh")
- expect(@provider).to receive(:shell_out!).with("whereis -s zsh", env: nil, timeout: 900).and_return(whereis)
- #@provider.should_receive(:popen4).with("whereis -s zsh").and_yield(@pid, @stdin, ["zsh: /usr/ports/shells/zsh"], @stderr).and_return(@status)
+ expect(@provider).to receive(:shell_out!).with("whereis", "-s", "zsh", env: nil, timeout: 900).and_return(whereis)
allow(@provider).to receive(:port_name).and_return("zsh")
expect(@provider.port_path).to eq("/usr/ports/shells/zsh")
end
@@ -102,7 +102,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
it "should return the ports candidate version when given a valid port path" do
allow(@provider).to receive(:port_path).and_return("/usr/ports/shells/zsh")
make_v = OpenStruct.new(:stdout => "4.3.6\n", :exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with("make -V PORTVERSION", { cwd: "/usr/ports/shells/zsh", returns: [0, 1], env: nil, timeout: 900 }).and_return(make_v)
+ expect(@provider).to receive(:shell_out!).with("make", "-V", "PORTVERSION", { cwd: "/usr/ports/shells/zsh", returns: [0, 1], env: nil, timeout: 900 }).and_return(make_v)
expect(@provider.ports_candidate_version).to eq("4.3.6")
end
@@ -110,7 +110,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
allow(::File).to receive(:exist?).with("/usr/ports/Makefile").and_return(true)
allow(@provider).to receive(:port_path).and_return("/usr/ports/shells/zsh")
make_v = OpenStruct.new(:stdout => "zsh-4.3.6_7\n", :exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with("make -V PKGNAME", { cwd: "/usr/ports/shells/zsh", env: nil, returns: [0, 1], timeout: 900 }).and_return(make_v)
+ expect(@provider).to receive(:shell_out!).with("make", "-V", "PKGNAME", { cwd: "/usr/ports/shells/zsh", env: nil, returns: [0, 1], timeout: 900 }).and_return(make_v)
#@provider.should_receive(:ports_makefile_variable_value).with("PKGNAME").and_return("zsh-4.3.6_7")
expect(@provider.package_name).to eq("zsh")
end
@@ -127,7 +127,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
end
it "should run pkg_add -r with the package name" do
- expect(@provider).to receive(:shell_out!).with("pkg_add -r zsh", env: nil, timeout: 900).and_return(@cmd_result)
+ expect(@provider).to receive(:shell_out!).with("pkg_add", "-r", "zsh", env: nil, timeout: 900).and_return(@cmd_result)
@provider.install_package("zsh", "4.3.6_7")
end
end
@@ -142,7 +142,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
it "should figure out the port path from the package_name using whereis" do
whereis = OpenStruct.new(:stdout => "zsh: /usr/ports/shells/zsh")
- expect(@provider).to receive(:shell_out!).with("whereis -s zsh", env: nil, timeout: 900).and_return(whereis)
+ expect(@provider).to receive(:shell_out!).with("whereis", "-s", "zsh", env: nil, timeout: 900).and_return(whereis)
expect(@provider.port_path).to eq("/usr/ports/shells/zsh")
end
@@ -178,7 +178,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
end
it "should run pkg_add -r with the package name" do
- expect(@provider).to receive(:shell_out!).with("pkg_add -r ruby18-iconv", env: nil, timeout: 900).and_return(@install_result)
+ expect(@provider).to receive(:shell_out!).with("pkg_add", "-r", "ruby18-iconv", env: nil, timeout: 900).and_return(@install_result)
@provider.install_package("ruby-iconv", "1.0")
end
end
@@ -193,7 +193,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
end
it "should run pkg_delete with the package name and version" do
- expect(@provider).to receive(:shell_out!).with("pkg_delete zsh-4.3.6_7", env: nil, timeout: 900).and_return(@pkg_delete)
+ expect(@provider).to receive(:shell_out!).with("pkg_delete", "zsh-4.3.6_7", env: nil, timeout: 900).and_return(@pkg_delete)
@provider.remove_package("zsh", "4.3.6_7")
end
end
@@ -213,14 +213,14 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
it "should return the port path for a valid port name" do
whereis = OpenStruct.new(:stdout => "bonnie++: /usr/ports/benchmarks/bonnie++")
- expect(@provider).to receive(:shell_out!).with("whereis -s bonnie++", env: nil, timeout: 900).and_return(whereis)
+ expect(@provider).to receive(:shell_out!).with("whereis", "-s", "bonnie++", env: nil, timeout: 900).and_return(whereis)
allow(@provider).to receive(:port_name).and_return("bonnie++")
expect(@provider.port_path).to eq("/usr/ports/benchmarks/bonnie++")
end
it "should return the version number when it is installed" do
pkg_info = OpenStruct.new(:stdout => "bonnie++-1.96")
- expect(@provider).to receive(:shell_out!).with('pkg_info -E "bonnie++*"', env: nil, returns: [0, 1], timeout: 900).and_return(pkg_info)
+ expect(@provider).to receive(:shell_out!).with("pkg_info", "-E", "bonnie++*", env: nil, returns: [0, 1], timeout: 900).and_return(pkg_info)
allow(@provider).to receive(:package_name).and_return("bonnie++")
expect(@provider.current_installed_version).to eq("1.96")
end
@@ -253,7 +253,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
allow(@provider).to receive(:latest_link_name).and_return("perl")
cmd = OpenStruct.new(:status => true)
- expect(@provider).to receive(:shell_out!).with("pkg_add -r perl", env: nil, timeout: 900).and_return(cmd)
+ expect(@provider).to receive(:shell_out!).with("pkg_add", "-r", "perl", env: nil, timeout: 900).and_return(cmd)
@provider.install_package("perl5.8", "5.8.8_1")
end
@@ -267,7 +267,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
allow(@provider).to receive(:latest_link_name).and_return("mysql50-server")
cmd = OpenStruct.new(:status => true)
- expect(@provider).to receive(:shell_out!).with("pkg_add -r mysql50-server", env: nil, timeout: 900).and_return(cmd)
+ expect(@provider).to receive(:shell_out!).with("pkg_add", "-r", "mysql50-server", env: nil, timeout: 900).and_return(cmd)
@provider.install_package("mysql50-server", "5.0.45_1")
end
end
diff --git a/spec/unit/provider/package/freebsd/pkgng_spec.rb b/spec/unit/provider/package/freebsd/pkgng_spec.rb
index a2bd833d9e..098052a057 100644
--- a/spec/unit/provider/package/freebsd/pkgng_spec.rb
+++ b/spec/unit/provider/package/freebsd/pkgng_spec.rb
@@ -67,7 +67,7 @@ describe Chef::Provider::Package::Freebsd::Port do
end
it "should query pkg database" do
- expect(@provider).to receive(:shell_out!).with('pkg info "zsh"', env: nil, returns: [0, 70], timeout: 900).and_return(@pkg_info)
+ expect(@provider).to receive(:shell_out!).with("pkg", "info", "zsh", env: nil, returns: [0, 70], timeout: 900).and_return(@pkg_info)
expect(@provider.current_installed_version).to eq("3.1.7")
end
end
@@ -75,14 +75,14 @@ describe Chef::Provider::Package::Freebsd::Port do
describe "determining candidate version" do
it "should query repository" do
pkg_query = OpenStruct.new(:stdout => "5.0.5\n", :exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with("pkg rquery '%v' zsh", env: nil, timeout: 900).and_return(pkg_query)
+ expect(@provider).to receive(:shell_out!).with("pkg", "rquery", "%v", "zsh", env: nil, timeout: 900).and_return(pkg_query)
expect(@provider.candidate_version).to eq("5.0.5")
end
it "should query specified repository when given option" do
@provider.new_resource.options("-r LocalMirror") # This requires LocalMirror repo configuration.
pkg_query = OpenStruct.new(:stdout => "5.0.3\n", :exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with("pkg rquery -r LocalMirror '%v' zsh", env: nil, timeout: 900).and_return(pkg_query)
+ expect(@provider).to receive(:shell_out!).with("pkg", "rquery", "-r", "LocalMirror", "%v", "zsh", env: nil, timeout: 900).and_return(pkg_query)
expect(@provider.candidate_version).to eq("5.0.3")
end
@@ -100,7 +100,7 @@ describe Chef::Provider::Package::Freebsd::Port do
it "should handle package source from file" do
@provider.new_resource.source("/nas/pkg/repo/zsh-5.0.1.txz")
expect(@provider).to receive(:shell_out!).
- with("pkg add /nas/pkg/repo/zsh-5.0.1.txz", env: { "LC_ALL" => nil }, timeout: 900).
+ with("pkg", "add", "/nas/pkg/repo/zsh-5.0.1.txz", env: { "LC_ALL" => nil }, timeout: 900).
and_return(@install_result)
@provider.install_package("zsh", "5.0.1")
end
@@ -108,21 +108,21 @@ describe Chef::Provider::Package::Freebsd::Port do
it "should handle package source over ftp or http" do
@provider.new_resource.source("http://repo.example.com/zsh-5.0.1.txz")
expect(@provider).to receive(:shell_out!).
- with("pkg add http://repo.example.com/zsh-5.0.1.txz", env: { "LC_ALL" => nil }, timeout: 900).
+ with("pkg", "add", "http://repo.example.com/zsh-5.0.1.txz", env: { "LC_ALL" => nil }, timeout: 900).
and_return(@install_result)
@provider.install_package("zsh", "5.0.1")
end
it "should handle a package name" do
expect(@provider).to receive(:shell_out!).
- with("pkg install -y zsh", env: { "LC_ALL" => nil }, timeout: 900).and_return(@install_result)
+ with("pkg", "install", "-y", "zsh", env: { "LC_ALL" => nil }, timeout: 900).and_return(@install_result)
@provider.install_package("zsh", "5.0.1")
end
it "should handle a package name with a specified repo" do
@provider.new_resource.options("-r LocalMirror") # This requires LocalMirror repo configuration.
expect(@provider).to receive(:shell_out!).
- with("pkg install -y -r LocalMirror zsh", env: { "LC_ALL" => nil }, timeout: 900).and_return(@install_result)
+ with("pkg", "install", "-y", "-r", "LocalMirror", "zsh", env: { "LC_ALL" => nil }, timeout: 900).and_return(@install_result)
@provider.install_package("zsh", "5.0.1")
end
end
@@ -134,14 +134,14 @@ describe Chef::Provider::Package::Freebsd::Port do
it "should call pkg delete" do
expect(@provider).to receive(:shell_out!).
- with("pkg delete -y zsh-5.0.1", env: nil, timeout: 900).and_return(@install_result)
+ with("pkg", "delete", "-y", "zsh-5.0.1", env: nil, timeout: 900).and_return(@install_result)
@provider.remove_package("zsh", "5.0.1")
end
it "should not include repo option in pkg delete" do
@provider.new_resource.options("-r LocalMirror") # This requires LocalMirror repo configuration.
expect(@provider).to receive(:shell_out!).
- with("pkg delete -y zsh-5.0.1", env: nil, timeout: 900).and_return(@install_result)
+ with("pkg", "delete", "-y", "zsh-5.0.1", env: nil, timeout: 900).and_return(@install_result)
@provider.remove_package("zsh", "5.0.1")
end
end
diff --git a/spec/unit/provider/package/freebsd/port_spec.rb b/spec/unit/provider/package/freebsd/port_spec.rb
index b593da4ff1..5c87483fa6 100644
--- a/spec/unit/provider/package/freebsd/port_spec.rb
+++ b/spec/unit/provider/package/freebsd/port_spec.rb
@@ -68,24 +68,24 @@ describe Chef::Provider::Package::Freebsd::Port do
it "should check 'pkg_info' if system uses pkg_* tools" do
allow(@new_resource).to receive(:supports_pkgng?)
expect(@new_resource).to receive(:supports_pkgng?).and_return(false)
- expect(@provider).to receive(:shell_out!).with('pkg_info -E "zsh*"', env: nil, returns: [0, 1], timeout: 900).and_return(@pkg_info)
+ expect(@provider).to receive(:shell_out!).with("pkg_info", "-E", "zsh*", env: nil, returns: [0, 1], timeout: 900).and_return(@pkg_info)
expect(@provider.current_installed_version).to eq("3.1.7")
end
it "should check 'pkg info' if make supports WITH_PKGNG if freebsd version is < 1000017" do
pkg_enabled = OpenStruct.new(:stdout => "yes\n")
- [1000016, 1000000, 901503, 902506, 802511].each do |__freebsd_version|
- @node.automatic_attrs[:os_version] = __freebsd_version
- expect(@new_resource).to receive(:shell_out!).with("make -V WITH_PKGNG", env: nil).and_return(pkg_enabled)
- expect(@provider).to receive(:shell_out!).with('pkg info "zsh"', env: nil, returns: [0, 70], timeout: 900).and_return(@pkg_info)
+ [1000016, 1000000, 901503, 902506, 802511].each do |freebsd_version|
+ @node.automatic_attrs[:os_version] = freebsd_version
+ expect(@new_resource).to receive(:shell_out!).with("make", "-V", "WITH_PKGNG", env: nil).and_return(pkg_enabled)
+ expect(@provider).to receive(:shell_out!).with("pkg", "info", "zsh", env: nil, returns: [0, 70], timeout: 900).and_return(@pkg_info)
expect(@provider.current_installed_version).to eq("3.1.7")
end
end
it "should check 'pkg info' if the freebsd version is greater than or equal to 1000017" do
- __freebsd_version = 1000017
- @node.automatic_attrs[:os_version] = __freebsd_version
- expect(@provider).to receive(:shell_out!).with('pkg info "zsh"', env: nil, returns: [0, 70], timeout: 900).and_return(@pkg_info)
+ freebsd_version = 1000017
+ @node.automatic_attrs[:os_version] = freebsd_version
+ expect(@provider).to receive(:shell_out!).with("pkg", "info", "zsh", env: nil, returns: [0, 70], timeout: 900).and_return(@pkg_info)
expect(@provider.current_installed_version).to eq("3.1.7")
end
end
@@ -98,7 +98,7 @@ describe Chef::Provider::Package::Freebsd::Port do
it "should return candidate version if port exists" do
allow(::File).to receive(:exist?).with("/usr/ports/Makefile").and_return(true)
allow(@provider).to receive(:port_dir).and_return("/usr/ports/shells/zsh")
- expect(@provider).to receive(:shell_out!).with("make -V PORTVERSION", cwd: "/usr/ports/shells/zsh", env: nil, returns: [0, 1], timeout: 900).
+ expect(@provider).to receive(:shell_out!).with("make", "-V", "PORTVERSION", cwd: "/usr/ports/shells/zsh", env: nil, returns: [0, 1], timeout: 900).
and_return(@port_version)
expect(@provider.candidate_version).to eq("5.0.5")
end
@@ -122,13 +122,13 @@ describe Chef::Provider::Package::Freebsd::Port do
it "should query system for path given just a name" do
whereis = OpenStruct.new(:stdout => "zsh: /usr/ports/shells/zsh\n")
- expect(@provider).to receive(:shell_out!).with("whereis -s zsh", env: nil, timeout: 900).and_return(whereis)
+ expect(@provider).to receive(:shell_out!).with("whereis", "-s", "zsh", env: nil, timeout: 900).and_return(whereis)
expect(@provider.port_dir).to eq("/usr/ports/shells/zsh")
end
it "should raise exception if not found" do
whereis = OpenStruct.new(:stdout => "zsh:\n")
- expect(@provider).to receive(:shell_out!).with("whereis -s zsh", env: nil, timeout: 900).and_return(whereis)
+ expect(@provider).to receive(:shell_out!).with("whereis", "-s", "zsh", env: nil, timeout: 900).and_return(whereis)
expect { @provider.port_dir }.to raise_error(Chef::Exceptions::Package, "Could not find port with the name zsh")
end
end
@@ -141,7 +141,7 @@ describe Chef::Provider::Package::Freebsd::Port do
it "should run make install in port directory" do
allow(@provider).to receive(:port_dir).and_return("/usr/ports/shells/zsh")
expect(@provider).to receive(:shell_out!).
- with("make -DBATCH install clean", :timeout => 1800, :cwd => "/usr/ports/shells/zsh", :env => nil).
+ with("make", "-DBATCH", "install", "clean", :timeout => 1800, :cwd => "/usr/ports/shells/zsh", :env => nil).
and_return(@install_result)
@provider.install_package("zsh", "5.0.5")
end
@@ -155,7 +155,7 @@ describe Chef::Provider::Package::Freebsd::Port do
it "should run make deinstall in port directory" do
allow(@provider).to receive(:port_dir).and_return("/usr/ports/shells/zsh")
expect(@provider).to receive(:shell_out!).
- with("make deinstall", :timeout => 300, :cwd => "/usr/ports/shells/zsh", :env => nil).
+ with("make", "deinstall", :timeout => 300, :cwd => "/usr/ports/shells/zsh", :env => nil).
and_return(@install_result)
@provider.remove_package("zsh", "5.0.5")
end
diff --git a/spec/unit/provider/package/homebrew_spec.rb b/spec/unit/provider/package/homebrew_spec.rb
index 17ed5ccc41..833fa7bd3e 100644
--- a/spec/unit/provider/package/homebrew_spec.rb
+++ b/spec/unit/provider/package/homebrew_spec.rb
@@ -20,7 +20,8 @@ require "spec_helper"
describe Chef::Provider::Package::Homebrew do
let(:node) { Chef::Node.new }
let(:events) { double("Chef::Events").as_null_object }
- let(:run_context) { double("Chef::RunContext", node: node, events: events) }
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+ let(:run_context) { double("Chef::RunContext", node: node, events: events, logger: logger) }
let(:new_resource) { Chef::Resource::HomebrewPackage.new("emacs") }
let(:current_resource) { Chef::Resource::HomebrewPackage.new("emacs") }
@@ -209,7 +210,7 @@ describe Chef::Provider::Package::Homebrew do
allow(provider.new_resource).to receive(:version).and_return("24.3")
allow(provider.current_resource).to receive(:version).and_return(nil)
allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info)
- expect(provider).to receive(:get_response_from_command).with("brew install emacs")
+ expect(provider).to receive(:get_response_from_command).with("brew", "install", nil, "emacs")
provider.install_package("emacs", "24.3")
end
@@ -221,9 +222,9 @@ describe Chef::Provider::Package::Homebrew do
end
it "uses options to the brew command if specified" do
- allow(provider.new_resource).to receive(:options).and_return("--cocoa")
+ new_resource.options "--cocoa"
allow(provider.current_resource).to receive(:version).and_return("24.3")
- allow(provider).to receive(:get_response_from_command).with("brew install --cocoa emacs")
+ allow(provider).to receive(:get_response_from_command).with("brew", "install", "--cocoa", "emacs")
provider.install_package("emacs", "24.3")
end
end
@@ -232,7 +233,7 @@ describe Chef::Provider::Package::Homebrew do
it "uses brew upgrade to upgrade the package if it is installed" do
allow(provider.current_resource).to receive(:version).and_return("24")
allow(provider).to receive(:brew_info).and_return(installed_brew_info)
- expect(provider).to receive(:get_response_from_command).with("brew upgrade emacs")
+ expect(provider).to receive(:get_response_from_command).with("brew", "upgrade", nil, "emacs")
provider.upgrade_package("emacs", "24.3")
end
@@ -246,15 +247,15 @@ describe Chef::Provider::Package::Homebrew do
it "uses brew install to install the package if it is not installed" do
allow(provider.current_resource).to receive(:version).and_return(nil)
allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info)
- expect(provider).to receive(:get_response_from_command).with("brew install emacs")
+ expect(provider).to receive(:get_response_from_command).with("brew", "install", nil, "emacs")
provider.upgrade_package("emacs", "24.3")
end
it "uses options to the brew command if specified" do
allow(provider.current_resource).to receive(:version).and_return("24")
allow(provider).to receive(:brew_info).and_return(installed_brew_info)
- allow(provider.new_resource).to receive(:options).and_return("--cocoa")
- expect(provider).to receive(:get_response_from_command).with("brew upgrade --cocoa emacs")
+ new_resource.options "--cocoa"
+ expect(provider).to receive(:get_response_from_command).with("brew", "upgrade", [ "--cocoa" ], "emacs")
provider.upgrade_package("emacs", "24.3")
end
end
@@ -263,7 +264,7 @@ describe Chef::Provider::Package::Homebrew do
it "uninstalls the package with brew uninstall" do
allow(provider.current_resource).to receive(:version).and_return("24.3")
allow(provider).to receive(:brew_info).and_return(installed_brew_info)
- expect(provider).to receive(:get_response_from_command).with("brew uninstall emacs")
+ expect(provider).to receive(:get_response_from_command).with("brew", "uninstall", nil, "emacs")
provider.remove_package("emacs", "24.3")
end
@@ -278,7 +279,7 @@ describe Chef::Provider::Package::Homebrew do
it "uninstalls the package with brew uninstall --force" do
allow(provider.current_resource).to receive(:version).and_return("24.3")
allow(provider).to receive(:brew_info).and_return(installed_brew_info)
- expect(provider).to receive(:get_response_from_command).with("brew uninstall --force emacs")
+ expect(provider).to receive(:get_response_from_command).with("brew", "uninstall", "--force", nil, "emacs")
provider.purge_package("emacs", "24.3")
end
diff --git a/spec/unit/provider/package/ips_spec.rb b/spec/unit/provider/package/ips_spec.rb
index f47385da09..45111601fa 100644
--- a/spec/unit/provider/package/ips_spec.rb
+++ b/spec/unit/provider/package/ips_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@chef.io>
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright 2012-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,9 +26,9 @@ describe Chef::Provider::Package::Ips do
@node = Chef::Node.new
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::Package.new("crypto/gnupg", @run_context)
- @current_resource = Chef::Resource::Package.new("crypto/gnupg", @run_context)
- allow(Chef::Resource::Package).to receive(:new).and_return(@current_resource)
+ @new_resource = Chef::Resource::IpsPackage.new("crypto/gnupg", @run_context)
+ @current_resource = Chef::Resource::IpsPackage.new("crypto/gnupg", @run_context)
+ allow(Chef::Resource::IpsPackage).to receive(:new).and_return(@current_resource)
@provider = Chef::Provider::Package::Ips.new(@new_resource, @run_context)
end
@@ -41,7 +41,7 @@ installed on the system. Try specifying -r to query remotely:
crypto/gnupg
PKG_STATUS
- return OpenStruct.new(:stdout => stdout, :stdin => stdin, :stderr => stderr, :status => @status, :exitstatus => 1)
+ OpenStruct.new(:stdout => stdout, :stdin => stdin, :stderr => stderr, :status => @status, :exitstatus => 1)
end
def remote_output
@@ -59,33 +59,33 @@ Packaging Date: April 1, 2012 05:55:52 PM
PKG_STATUS
stdin = StringIO.new
stderr = ""
- return OpenStruct.new(:stdout => stdout, :stdin => stdin, :stderr => stderr, :status => @status, :exitstatus => 0)
+ OpenStruct.new(:stdout => stdout, :stdin => stdin, :stderr => stderr, :status => @status, :exitstatus => 0)
end
context "when loading current resource" do
it "should create a current resource with the name of the new_resource" do
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local_output)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote_output)
- expect(Chef::Resource::Package).to receive(:new).and_return(@current_resource)
+ expect(@provider).to receive(:shell_out).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out!).with("pkg", "info", "-r", @new_resource.package_name, timeout: 900).and_return(remote_output)
+ expect(Chef::Resource::IpsPackage).to receive(:new).and_return(@current_resource)
@provider.load_current_resource
end
it "should set the current resources package name to the new resources package name" do
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local_output)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote_output)
+ expect(@provider).to receive(:shell_out).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out!).with("pkg", "info", "-r", @new_resource.package_name, timeout: 900).and_return(remote_output)
@provider.load_current_resource
expect(@current_resource.package_name).to eq(@new_resource.package_name)
end
it "should run pkg info with the package name" do
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local_output)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote_output)
+ expect(@provider).to receive(:shell_out).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out!).with("pkg", "info", "-r", @new_resource.package_name, timeout: 900).and_return(remote_output)
@provider.load_current_resource
end
it "should set the installed version to nil on the current resource if package state is not installed" do
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local_output)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote_output)
+ expect(@provider).to receive(:shell_out).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out!).with("pkg", "info", "-r", @new_resource.package_name, timeout: 900).and_return(remote_output)
@provider.load_current_resource
expect(@current_resource.version).to be_nil
end
@@ -107,31 +107,37 @@ Packaging Date: October 19, 2011 09:14:50 AM
Size: 8.07 MB
FMRI: pkg://solaris/crypto/gnupg@2.0.17,5.11-0.175.0.0.0.2.537:20111019T091450Z
INSTALLED
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote_output)
+ expect(@provider).to receive(:shell_out).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local)
+ expect(@provider).to receive(:shell_out!).with("pkg", "info", "-r", @new_resource.package_name, timeout: 900).and_return(remote_output)
@provider.load_current_resource
expect(@current_resource.version).to eq("2.0.17")
end
it "should return the current resource" do
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local_output)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote_output)
+ expect(@provider).to receive(:shell_out).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out!).with("pkg", "info", "-r", @new_resource.package_name, timeout: 900).and_return(remote_output)
expect(@provider.load_current_resource).to eql(@current_resource)
end
end
context "when installing a package" do
it "should run pkg install with the package name and version" do
- expect(@provider).to receive(:shell_out).with("pkg install -q crypto/gnupg@2.0.17", timeout: 900)
+ expect(@provider).to receive(:shell_out!).with("pkg", "install", "-q", "crypto/gnupg@2.0.17", timeout: 900)
@provider.install_package("crypto/gnupg", "2.0.17")
end
it "should run pkg install with the package name and version and options if specified" do
- expect(@provider).to receive(:shell_out).with("pkg --no-refresh install -q crypto/gnupg@2.0.17", timeout: 900)
- allow(@new_resource).to receive(:options).and_return("--no-refresh")
+ expect(@provider).to receive(:shell_out!).with("pkg", "--no-refresh", "install", "-q", "crypto/gnupg@2.0.17", timeout: 900)
+ @new_resource.options "--no-refresh"
@provider.install_package("crypto/gnupg", "2.0.17")
end
+ it "raises an error if package fails to install" do
+ expect(@provider).to receive(:shell_out!).with("pkg", "--no-refresh", "install", "-q", "crypto/gnupg@2.0.17", timeout: 900).and_raise(Mixlib::ShellOut::ShellCommandFailed)
+ @new_resource.options("--no-refresh")
+ expect { @provider.install_package("crypto/gnupg", "2.0.17") }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
+ end
+
it "should not include the human-readable version in the candidate_version" do
remote = remote_output
remote.stdout = <<-PKG_STATUS
@@ -146,8 +152,8 @@ Packaging Date: April 1, 2012 05:55:52 PM
Size: 2.57 MB
FMRI: pkg://omnios/security/sudo@1.8.4.1,5.11-0.151002:20120401T175552Z
PKG_STATUS
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local_output)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote)
+ expect(@provider).to receive(:shell_out).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out!).with("pkg", "info", "-r", @new_resource.package_name, timeout: 900).and_return(remote)
@provider.load_current_resource
expect(@current_resource.version).to be_nil
expect(@provider.candidate_version).to eql("1.8.4.1")
@@ -187,19 +193,19 @@ Packaging Date: October 19, 2011 09:14:50 AM
FMRI: pkg://solaris/crypto/gnupg@2.0.18,5.11-0.175.0.0.0.2.537:20111019T091450Z
REMOTE
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote)
+ expect(@provider).to receive(:shell_out).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local)
+ expect(@provider).to receive(:shell_out!).with("pkg", "info", "-r", @new_resource.package_name, timeout: 900).and_return(remote)
expect(@provider).to receive(:install_package).exactly(0).times
@provider.run_action(:install)
end
context "when accept_license is true" do
before do
- allow(@new_resource).to receive(:accept_license).and_return(true)
+ @new_resource.accept_license(true)
end
it "should run pkg install with the --accept flag" do
- expect(@provider).to receive(:shell_out).with("pkg install -q --accept crypto/gnupg@2.0.17", timeout: 900)
+ expect(@provider).to receive(:shell_out).with("pkg", "install", "-q", "--accept", "crypto/gnupg@2.0.17", timeout: 900).and_return(local_output)
@provider.install_package("crypto/gnupg", "2.0.17")
end
end
@@ -207,20 +213,20 @@ REMOTE
context "when upgrading a package" do
it "should run pkg install with the package name and version" do
- expect(@provider).to receive(:shell_out).with("pkg install -q crypto/gnupg@2.0.17", timeout: 900)
+ expect(@provider).to receive(:shell_out).with("pkg", "install", "-q", "crypto/gnupg@2.0.17", timeout: 900).and_return(local_output)
@provider.upgrade_package("crypto/gnupg", "2.0.17")
end
end
context "when uninstalling a package" do
it "should run pkg uninstall with the package name and version" do
- expect(@provider).to receive(:shell_out!).with("pkg uninstall -q crypto/gnupg@2.0.17", timeout: 900)
+ expect(@provider).to receive(:shell_out!).with("pkg", "uninstall", "-q", "crypto/gnupg@2.0.17", timeout: 900)
@provider.remove_package("crypto/gnupg", "2.0.17")
end
it "should run pkg uninstall with the package name and version and options if specified" do
- expect(@provider).to receive(:shell_out!).with("pkg --no-refresh uninstall -q crypto/gnupg@2.0.17", timeout: 900)
- allow(@new_resource).to receive(:options).and_return("--no-refresh")
+ expect(@provider).to receive(:shell_out!).with("pkg", "--no-refresh", "uninstall", "-q", "crypto/gnupg@2.0.17", timeout: 900)
+ @new_resource.options "--no-refresh"
@provider.remove_package("crypto/gnupg", "2.0.17")
end
end
diff --git a/spec/unit/provider/package/macports_spec.rb b/spec/unit/provider/package/macports_spec.rb
index b90cf89047..4961f4c467 100644
--- a/spec/unit/provider/package/macports_spec.rb
+++ b/spec/unit/provider/package/macports_spec.rb
@@ -105,7 +105,7 @@ EOF
it "should run the port install command with the correct version" do
expect(@current_resource).to receive(:version).and_return("4.1.6")
@provider.current_resource = @current_resource
- expect(@provider).to receive(:shell_out!).with("port install zsh @4.2.7", timeout: 900)
+ expect(@provider).to receive(:shell_out!).with("port", "install", "zsh", "@4.2.7", timeout: 900)
@provider.install_package("zsh", "4.2.7")
end
@@ -121,8 +121,8 @@ EOF
it "should add options to the port command when specified" do
expect(@current_resource).to receive(:version).and_return("4.1.6")
@provider.current_resource = @current_resource
- allow(@new_resource).to receive(:options).and_return("-f")
- expect(@provider).to receive(:shell_out!).with("port -f install zsh @4.2.7", timeout: 900)
+ @new_resource.options("-f")
+ expect(@provider).to receive(:shell_out!).with("port", "-f", "install", "zsh", "@4.2.7", timeout: 900)
@provider.install_package("zsh", "4.2.7")
end
@@ -130,36 +130,36 @@ EOF
describe "purge_package" do
it "should run the port uninstall command with the correct version" do
- expect(@provider).to receive(:shell_out!).with("port uninstall zsh @4.2.7", timeout: 900)
+ expect(@provider).to receive(:shell_out!).with("port", "uninstall", "zsh", "@4.2.7", timeout: 900)
@provider.purge_package("zsh", "4.2.7")
end
it "should purge the currently active version if no explicit version is passed in" do
- expect(@provider).to receive(:shell_out!).with("port uninstall zsh", timeout: 900)
+ expect(@provider).to receive(:shell_out!).with("port", "uninstall", "zsh", timeout: 900)
@provider.purge_package("zsh", nil)
end
it "should add options to the port command when specified" do
- allow(@new_resource).to receive(:options).and_return("-f")
- expect(@provider).to receive(:shell_out!).with("port -f uninstall zsh @4.2.7", timeout: 900)
+ @new_resource.options("-f")
+ expect(@provider).to receive(:shell_out!).with("port", "-f", "uninstall", "zsh", "@4.2.7", timeout: 900)
@provider.purge_package("zsh", "4.2.7")
end
end
describe "remove_package" do
it "should run the port deactivate command with the correct version" do
- expect(@provider).to receive(:shell_out!).with("port deactivate zsh @4.2.7", timeout: 900)
+ expect(@provider).to receive(:shell_out!).with("port", "deactivate", "zsh", "@4.2.7", timeout: 900)
@provider.remove_package("zsh", "4.2.7")
end
it "should remove the currently active version if no explicit version is passed in" do
- expect(@provider).to receive(:shell_out!).with("port deactivate zsh", timeout: 900)
+ expect(@provider).to receive(:shell_out!).with("port", "deactivate", "zsh", timeout: 900)
@provider.remove_package("zsh", nil)
end
it "should add options to the port command when specified" do
- allow(@new_resource).to receive(:options).and_return("-f")
- expect(@provider).to receive(:shell_out!).with("port -f deactivate zsh @4.2.7", timeout: 900)
+ @new_resource.options("-f")
+ expect(@provider).to receive(:shell_out!).with("port", "-f", "deactivate", "zsh", "@4.2.7", timeout: 900)
@provider.remove_package("zsh", "4.2.7")
end
end
@@ -169,7 +169,7 @@ EOF
expect(@current_resource).to receive(:version).at_least(:once).and_return("4.1.6")
@provider.current_resource = @current_resource
- expect(@provider).to receive(:shell_out!).with("port upgrade zsh @4.2.7", timeout: 900)
+ expect(@provider).to receive(:shell_out!).with("port", "upgrade", "zsh", "@4.2.7", timeout: 900)
@provider.upgrade_package("zsh", "4.2.7")
end
@@ -191,11 +191,11 @@ EOF
end
it "should add options to the port command when specified" do
- allow(@new_resource).to receive(:options).and_return("-f")
+ @new_resource.options("-f")
expect(@current_resource).to receive(:version).at_least(:once).and_return("4.1.6")
@provider.current_resource = @current_resource
- expect(@provider).to receive(:shell_out!).with("port -f upgrade zsh @4.2.7", timeout: 900)
+ expect(@provider).to receive(:shell_out!).with("port", "-f", "upgrade", "zsh", "@4.2.7", timeout: 900)
@provider.upgrade_package("zsh", "4.2.7")
end
diff --git a/spec/unit/provider/package/msu_spec.rb b/spec/unit/provider/package/msu_spec.rb
new file mode 100644
index 0000000000..b9091d757a
--- /dev/null
+++ b/spec/unit/provider/package/msu_spec.rb
@@ -0,0 +1,283 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Provider::Package::Msu, :windows_only do
+ let(:timeout) {}
+
+ let(:new_resource) { Chef::Resource::MsuPackage.new("windows_test_pkg") }
+
+ let(:provider) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ Chef::Provider::Package::Msu.new(new_resource, run_context)
+ end
+
+ let(:installed_package_list_stdout) do
+ <<-EOF
+Packages listing:
+Package Identity : Package_for_KB2999486~31bf3856ad364e35~amd64~~6.1.9768.0
+Package Identity : Package_for_KB2994825~31bf3856ad364e35~amd64~~6.1.7601.0
+ EOF
+ end
+
+ let(:package_version_stdout) do
+ <<-EOF
+Package information:
+Package Identity : Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0
+State : Installed
+Dependency : Language Pack
+The operation completed successfully
+ EOF
+ end
+
+ let(:get_package_info_stdout) do
+ <<-EOF
+Deployment Image Servicing and Management tool
+Version: 6.1.7600.16385
+
+Image Version: 6.1.7600.16385
+
+Package information:
+Package Identity : Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0
+Applicable : Yes
+Copyright : Microsoft Corporation
+Company : Microsoft Corporation
+State : Installed
+Dependency : Language Pack
+The operation completed successfully
+ EOF
+ end
+
+ def allow_get_packages
+ get_packages_stdout = <<-EOF
+Deployment Image Servicing and Management tool
+Version: 6.1.7600.16385
+
+Image Version: 6.1.7600.16385
+
+Packages listing:
+
+Package Identity : Package_for_KB2999486~31bf3856ad364e35~amd64~~6.1.9768.0
+State : Installed
+Release Type : Language Pack
+Install Time : 2/11/2015 11:33 PM
+
+Package Identity : Package_for_KB2994825~31bf3856ad364e35~amd64~~6.1.7601.0
+State : Installed
+Release Type : Language Pack
+Install Time : 2/11/2015 11:33 PM
+
+Package Identity : Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0
+State : Installed
+Release Type : Feature Pack
+Install Time : 11/21/2010 3:40 AM
+
+The operation completed successfully.
+ EOF
+ get_packages_obj = double(stdout: get_packages_stdout)
+ allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-Packages").and_return(get_packages_obj)
+ end
+
+ before do
+ allow(Dir).to receive(:mktmpdir)
+ allow(provider).to receive(:cleanup_after_converge)
+ end
+
+ describe "#initialize" do
+ it "returns the correct class" do
+ expect(provider).to be_kind_of(Chef::Provider::Package::Msu)
+ end
+ end
+
+ describe "#load_current_resource" do
+ before do
+ new_resource.source = "C:\\Temp\\Test6.1-KB2664825-v3-x64.msu"
+ cab_file = "c:\\temp\\test6.1-kb2664825-v3-x64.cab"
+ allow(provider).to receive(:extract_msu_contents)
+ allow(provider).to receive(:read_cab_files_from_xml).and_return([cab_file])
+ installed_package_list_obj = double(stdout: installed_package_list_stdout)
+ allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-Packages").and_return(installed_package_list_obj)
+ package_version_obj = double(stdout: package_version_stdout)
+ allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-PackageInfo /PackagePath:\"#{cab_file}\"").and_return(package_version_obj)
+ end
+
+ it "returns a current_resource" do
+ expect(provider.load_current_resource).to be_kind_of(Chef::Resource::MsuPackage)
+ end
+
+ it "sets the current_resource.version to nil when the package is not installed" do
+ provider.load_current_resource
+ expect(provider.current_resource.version).to eql([nil])
+ end
+
+ it "calls download_source_file method if source is a URL" do
+ new_resource.source = "https://www.something.com/Test6.1-KB2664825-v3-x64.msu"
+ expect(provider).to receive(:download_source_file)
+ provider.load_current_resource
+ end
+ end
+
+ describe "#source_resource" do
+ before do
+ new_resource.source = "C:\\Temp\\Test6.1-KB2664825-v3-x64.msu"
+ new_resource.cookbook_name = "Msu_package"
+ end
+
+ it "sets the desired parameters of downloades msu file" do
+ allow(provider).to receive(:default_download_cache_path).and_return("C:\\chef\\cache\\package")
+ source_resource = provider.source_resource
+ expect(source_resource.path).to be == "C:\\chef\\cache\\package"
+ expect(source_resource.name).to be == "windows_test_pkg"
+ expect(source_resource.source).to be == [new_resource.source]
+ expect(source_resource.cookbook_name).to be == "Msu_package"
+ expect(source_resource.checksum).to be nil
+ end
+ end
+
+ describe "#default_download_cache_path" do
+ before do
+ new_resource.source = "https://www.something.com/Test6.1-KB2664825-v3-x64.msu"
+ end
+
+ it "returns a clean cache path where the msu file is downloaded" do
+ allow(Chef::FileCache).to receive(:create_cache_path).and_return("C:\\chef\\abc\\package")
+ path = provider.default_download_cache_path
+ expect(path).to be == "C:\\chef\\abc\\package\\Test6.1-KB2664825-v3-x64.msu"
+ end
+ end
+
+ describe "action specs" do
+ before do
+ new_resource.source = "C:\\Temp\\Test6.1-KB2664825-v3-x64.msu"
+ cab_file = "c:\\temp\\test6.1-kb2664825-v3-x64.cab"
+ allow(provider).to receive(:extract_msu_contents)
+ allow(provider).to receive(:read_cab_files_from_xml).and_return([cab_file])
+ installed_package_list_obj = double(stdout: installed_package_list_stdout)
+ allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-Packages").and_return(installed_package_list_obj)
+ package_version_obj = double(stdout: package_version_stdout)
+ allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-PackageInfo /PackagePath:\"#{cab_file}\"").and_return(package_version_obj)
+ end
+
+ describe "#action_install" do
+ it "installs package if not already installed" do
+ provider.load_current_resource
+ expect(provider.current_resource.version).to eql([nil])
+ expect(provider).to receive(:install_package)
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "does not install package if already installed" do
+ get_package_info_obj = double(stdout: get_package_info_stdout)
+ allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-PackageInfo /PackagePath:\"#{new_resource.source}\"").and_return(get_package_info_obj)
+ allow_get_packages
+ allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-PackageInfo /PackageName:\"Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0\"").and_return(get_package_info_obj)
+ provider.load_current_resource
+ expect(provider.current_resource.version).to eql(["6.1.3.0"])
+ expect(provider).not_to receive(:install_package)
+ provider.run_action(:install)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "#action_remove" do
+ it "does nothing when the package is not present" do
+ provider.load_current_resource
+ expect(provider).not_to receive(:remove_package)
+ provider.run_action(:remove)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "removes packages if package is installed" do
+ get_package_info_obj = double(stdout: get_package_info_stdout)
+ allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-PackageInfo /PackagePath:\"#{new_resource.source}\"").and_return(get_package_info_obj)
+ allow_get_packages
+ allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-PackageInfo /PackageName:\"Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0\"").and_return(get_package_info_obj)
+ provider.load_current_resource
+ expect(provider.current_resource.version).to eql(["6.1.3.0"])
+ expect(provider).to receive(:remove_package)
+ provider.run_action(:remove)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+
+ context "Invalid package source" do
+ def package_version_stdout
+ package_version_stdout = <<-EOF
+
+ Deployment Image Servicing and Management tool
+ Version: 6.1.7600.16385
+
+ Image Version: 6.1.7600.16385
+
+ An error occurred trying to open - c:\\temp\\test6.1-KB2664825-v3-x64.cab Error: 0x80070003
+ Error: 3
+ The system cannot find the path specified.
+ The DISM log file can be found at C:\\Windows\\Logs\\DISM\\dism.log.
+ EOF
+ end
+
+ it "raises error for invalid source path or file" do
+ expect { provider.load_current_resource }.to raise_error(Chef::Exceptions::Package, "DISM: The system cannot find the path or file specified.")
+ end
+ end
+ end
+
+ describe "#extract_msu_contents" do
+ it "extracts the msu contents by using mixlib shellout" do
+ expect(provider).to receive(:shell_out_with_timeout!).with("#{ENV['SYSTEMROOT']}\\system32\\expand.exe -f:* msu_file destination")
+ provider.extract_msu_contents("msu_file", "destination")
+ end
+ end
+
+ describe "#read_cab_files_from_xml" do
+ it "raises error if the xml file is not present" do
+ allow(Dir).to receive(:glob).and_return([])
+ expect { provider.read_cab_files_from_xml("msu_dir") }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ it "parses xml file with single cab file" do
+ xml_file = File.join(CHEF_SPEC_DATA, "sample_msu1.xml")
+ allow(Dir).to receive(:glob).and_return([xml_file])
+ cab_files = provider.read_cab_files_from_xml("msu_dir")
+ expect(cab_files).to eql(["msu_dir/IE10-Windows6.1-KB2859903-x86.CAB"])
+ end
+
+# We couldn't find any msu file with multiple cab files in it.
+# So we are not 100% sure about the structure of XML file in this case
+# The specs below cover 2 possible XML formats
+ context "handles different xml formats for multiple cab files in the msu package" do
+ it "parses xml file with multiple <package> tags" do
+ xml_file = File.join(CHEF_SPEC_DATA, "sample_msu2.xml")
+ allow(Dir).to receive(:glob).and_return([xml_file])
+ cab_files = provider.read_cab_files_from_xml("msu_dir")
+ expect(cab_files).to eql(["msu_dir/IE10-Windows6.1-KB2859903-x86.CAB", "msu_dir/abc.CAB"])
+ end
+
+ it "parses xml file with multiple <servicing> tags" do
+ xml_file = File.join(CHEF_SPEC_DATA, "sample_msu3.xml")
+ allow(Dir).to receive(:glob).and_return([xml_file])
+ cab_files = provider.read_cab_files_from_xml("msu_dir")
+ expect(cab_files).to eql(["msu_dir/IE10-Windows6.1-KB2859903-x86.CAB", "msu_dir/abc.CAB"])
+ end
+ end
+ end
+end
diff --git a/spec/unit/provider/package/openbsd_spec.rb b/spec/unit/provider/package/openbsd_spec.rb
index 3e1c1c90b6..20eb85dfcf 100644
--- a/spec/unit/provider/package/openbsd_spec.rb
+++ b/spec/unit/provider/package/openbsd_spec.rb
@@ -45,17 +45,17 @@ describe Chef::Provider::Package::Openbsd do
context "when not already installed" do
before do
- allow(provider).to receive(:shell_out!).with("pkg_info -e \"#{name}->0\"", anything()).and_return(instance_double("shellout", :stdout => ""))
+ allow(provider).to receive(:shell_out!).with("pkg_info", "-e", "#{name}->0", anything()).and_return(instance_double("shellout", :stdout => ""))
end
context "when there is a single candidate" do
context "when source is not provided" do
it "should run the installation command" do
- expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return(
+ expect(provider).to receive(:shell_out!).with("pkg_info", "-I", name, anything()).and_return(
instance_double("shellout", :stdout => "#{name}-#{version}\n"))
expect(provider).to receive(:shell_out!).with(
- "pkg_add -r #{name}-#{version}",
+ "pkg_add", "-r", "#{name}-#{version}",
{ :env => { "PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/" }, timeout: 900 }
) { OpenStruct.new :status => true }
provider.run_action(:install)
@@ -69,7 +69,7 @@ describe Chef::Provider::Package::Openbsd do
context "if no version is specified" do
it "should raise an exception" do
- expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return(
+ expect(provider).to receive(:shell_out!).with("pkg_info", "-I", name, anything()).and_return(
instance_double("shellout", :stdout => "#{name}-#{version}-#{flavor_a}\n#{name}-#{version}-#{flavor_b}\n"))
expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /multiple matching candidates/)
end
@@ -83,11 +83,11 @@ describe Chef::Provider::Package::Openbsd do
context "if no version is specified" do
it "should run the installation command" do
- expect(provider).to receive(:shell_out!).with("pkg_info -e \"#{package_name}->0\"", anything()).and_return(instance_double("shellout", :stdout => ""))
- expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return(
+ expect(provider).to receive(:shell_out!).with("pkg_info", "-e", "#{package_name}->0", anything()).and_return(instance_double("shellout", :stdout => ""))
+ expect(provider).to receive(:shell_out!).with("pkg_info", "-I", name, anything()).and_return(
instance_double("shellout", :stdout => "#{name}-#{version}-#{flavor}\n"))
expect(provider).to receive(:shell_out!).with(
- "pkg_add -r #{name}-#{version}-#{flavor}",
+ "pkg_add", "-r", "#{name}-#{version}-#{flavor}",
{ env: { "PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/" }, timeout: 900 }
) { OpenStruct.new :status => true }
provider.run_action(:install)
@@ -98,12 +98,12 @@ describe Chef::Provider::Package::Openbsd do
context "if a version is specified" do
it "should use the flavor from the version" do
- expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}-#{version}-#{flavor_b}\"", anything()).and_return(
+ expect(provider).to receive(:shell_out!).with("pkg_info", "-I", "#{name}-#{version}-#{flavor_b}", anything()).and_return(
instance_double("shellout", :stdout => "#{name}-#{version}-#{flavor_a}\n"))
new_resource.version("#{version}-#{flavor_b}")
expect(provider).to receive(:shell_out!).with(
- "pkg_add -r #{name}-#{version}-#{flavor_b}",
+ "pkg_add", "-r", "#{name}-#{version}-#{flavor_b}",
{ env: { "PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/" }, timeout: 900 }
) { OpenStruct.new :status => true }
provider.run_action(:install)
@@ -123,7 +123,7 @@ describe Chef::Provider::Package::Openbsd do
end
it "should run the command to delete the installed package" do
expect(@provider).to receive(:shell_out!).with(
- "pkg_delete #{@name}", env: nil, timeout: 900
+ "pkg_delete", @name, env: nil, timeout: 900
) { OpenStruct.new :status => true }
@provider.remove_package(@name, nil)
end
diff --git a/spec/unit/provider/package/pacman_spec.rb b/spec/unit/provider/package/pacman_spec.rb
index ce9107f31b..ceae73dbd1 100644
--- a/spec/unit/provider/package/pacman_spec.rb
+++ b/spec/unit/provider/package/pacman_spec.rb
@@ -51,7 +51,7 @@ ERR
end
it "should run pacman query with the package name" do
- expect(@provider).to receive(:shell_out).with("pacman -Qi #{@new_resource.package_name}", { timeout: 900 }).and_return(@status)
+ expect(@provider).to receive(:shell_out).with("pacman", "-Qi", @new_resource.package_name, { timeout: 900 }).and_return(@status)
@provider.load_current_resource
end
@@ -121,7 +121,7 @@ Include = /etc/pacman.d/mirrorlist
PACMAN_CONF
status = double(:stdout => "customrepo nano 1.2.3-4", :exitstatus => 0)
- allow(::File).to receive(:exists?).with("/etc/pacman.conf").and_return(true)
+ allow(::File).to receive(:exist?).with("/etc/pacman.conf").and_return(true)
allow(::File).to receive(:read).with("/etc/pacman.conf").and_return(@pacman_conf)
allow(@provider).to receive(:shell_out).and_return(status)
@@ -151,13 +151,13 @@ PACMAN_CONF
describe Chef::Provider::Package::Pacman, "install_package" do
it "should run pacman install with the package name and version" do
- expect(@provider).to receive(:shell_out!).with("pacman --sync --noconfirm --noprogressbar nano", { timeout: 900 })
+ expect(@provider).to receive(:shell_out!).with("pacman", "--sync", "--noconfirm", "--noprogressbar", "nano", { timeout: 900 })
@provider.install_package("nano", "1.0")
end
it "should run pacman install with the package name and version and options if specified" do
- expect(@provider).to receive(:shell_out!).with("pacman --sync --noconfirm --noprogressbar --debug nano", { timeout: 900 })
- allow(@new_resource).to receive(:options).and_return("--debug")
+ expect(@provider).to receive(:shell_out!).with("pacman", "--sync", "--noconfirm", "--noprogressbar", "--debug", "nano", { timeout: 900 })
+ @new_resource.options("--debug")
@provider.install_package("nano", "1.0")
end
@@ -172,13 +172,13 @@ PACMAN_CONF
describe Chef::Provider::Package::Pacman, "remove_package" do
it "should run pacman remove with the package name" do
- expect(@provider).to receive(:shell_out!).with("pacman --remove --noconfirm --noprogressbar nano", { timeout: 900 })
+ expect(@provider).to receive(:shell_out!).with("pacman", "--remove", "--noconfirm", "--noprogressbar", "nano", { timeout: 900 })
@provider.remove_package("nano", "1.0")
end
it "should run pacman remove with the package name and options if specified" do
- expect(@provider).to receive(:shell_out!).with("pacman --remove --noconfirm --noprogressbar --debug nano", { timeout: 900 })
- allow(@new_resource).to receive(:options).and_return("--debug")
+ expect(@provider).to receive(:shell_out!).with("pacman", "--remove", "--noconfirm", "--noprogressbar", "--debug", "nano", { timeout: 900 })
+ @new_resource.options("--debug")
@provider.remove_package("nano", "1.0")
end
diff --git a/spec/unit/provider/package/paludis_spec.rb b/spec/unit/provider/package/paludis_spec.rb
index b984aeb83f..df0150c8c0 100644
--- a/spec/unit/provider/package/paludis_spec.rb
+++ b/spec/unit/provider/package/paludis_spec.rb
@@ -59,7 +59,7 @@ PKG_STATUS
end
it "should run pkg info with the package name" do
- expect(@provider).to receive(:shell_out!).with("cave -L warning print-ids -M none -m \"#{@new_resource.package_name}\" -f \"%c/%p %v %r\n\"").and_return(@shell_out)
+ expect(@provider).to receive(:shell_out!).with("cave", "-L", "warning", "print-ids", "-M", "none", "-m", @new_resource.package_name, "-f", "%c/%p %v %r\n").and_return(@shell_out)
@provider.load_current_resource
end
@@ -86,13 +86,13 @@ INSTALLED
context "when installing a package" do
it "should run pkg install with the package name and version" do
- expect(@provider).to receive(:shell_out!).with("cave -L warning resolve -x \"=net/ntp-4.2.6_p5-r2\"", { :timeout => @new_resource.timeout })
+ expect(@provider).to receive(:shell_out!).with("cave", "-L", "warning", "resolve", "-x", "=net/ntp-4.2.6_p5-r2", { :timeout => @new_resource.timeout || 900 })
@provider.install_package("net/ntp", "4.2.6_p5-r2")
end
it "should run pkg install with the package name and version and options if specified" do
- expect(@provider).to receive(:shell_out!).with("cave -L warning resolve -x --preserve-world \"=net/ntp-4.2.6_p5-r2\"", { :timeout => @new_resource.timeout })
- allow(@new_resource).to receive(:options).and_return("--preserve-world")
+ expect(@provider).to receive(:shell_out!).with("cave", "-L", "warning", "resolve", "-x", "--preserve-world", "=net/ntp-4.2.6_p5-r2", { :timeout => @new_resource.timeout || 900 })
+ @new_resource.options "--preserve-world"
@provider.install_package("net/ntp", "4.2.6_p5-r2")
end
@@ -101,7 +101,7 @@ INSTALLED
sys-process/lsof 4.87 arbor
sys-process/lsof 4.87 x86_64
PKG_STATUS
- expect(@provider).to receive(:shell_out!).with("cave -L warning resolve -x \"=sys-process/lsof-4.87\"", { :timeout => @new_resource.timeout })
+ expect(@provider).to receive(:shell_out!).with("cave", "-L", "warning", "resolve", "-x", "=sys-process/lsof-4.87", { :timeout => @new_resource.timeout || 900 })
@provider.install_package("sys-process/lsof", "4.87")
end
@@ -119,14 +119,14 @@ PKG_STATUS
context "when upgrading a package" do
it "should run pkg install with the package name and version" do
- expect(@provider).to receive(:shell_out!).with("cave -L warning resolve -x \"=net/ntp-4.2.6_p5-r2\"", { :timeout => @new_resource.timeout })
+ expect(@provider).to receive(:shell_out!).with("cave", "-L", "warning", "resolve", "-x", "=net/ntp-4.2.6_p5-r2", { :timeout => @new_resource.timeout || 900 })
@provider.upgrade_package("net/ntp", "4.2.6_p5-r2")
end
end
context "when uninstalling a package" do
it "should run pkg uninstall with the package name and version" do
- expect(@provider).to receive(:shell_out!).with("cave -L warning uninstall -x \"=net/ntp-4.2.6_p5-r2\"")
+ expect(@provider).to receive(:shell_out!).with("cave", "-L", "warning", "uninstall", "-x", "=net/ntp-4.2.6_p5-r2")
@provider.remove_package("net/ntp", "4.2.6_p5-r2")
end
diff --git a/spec/unit/provider/package/portage_spec.rb b/spec/unit/provider/package/portage_spec.rb
index c2ff1cc952..40dc44b113 100644
--- a/spec/unit/provider/package/portage_spec.rb
+++ b/spec/unit/provider/package/portage_spec.rb
@@ -56,6 +56,12 @@ describe Chef::Provider::Package::Portage, "load_current_resource" do
expect(@provider.current_resource.version).to eq("1.0.0-r1")
end
+ it "should return a current resource with the correct version if the package is found with version with character" do
+ allow(::Dir).to receive(:[]).with("/var/db/pkg/dev-util/git-*").and_return(["/var/db/pkg/dev-util/git-1.0.0d"])
+ @provider.load_current_resource
+ expect(@provider.current_resource.version).to eq("1.0.0d")
+ end
+
it "should return a current resource with a nil version if the package is not found" do
allow(::Dir).to receive(:[]).with("/var/db/pkg/dev-util/git-*").and_return(["/var/db/pkg/dev-util/notgit-1.0.0"])
@provider.load_current_resource
@@ -107,202 +113,65 @@ describe Chef::Provider::Package::Portage, "load_current_resource" do
end
it "should throw an exception if the exitstatus is not 0" do
- status = double(:stdout => "", :exitstatus => 1)
+ status = double(:stdout => "", :stderr => "", :exitstatus => 1)
allow(@provider).to receive(:shell_out).and_return(status)
expect { @provider.candidate_version }.to raise_error(Chef::Exceptions::Package)
end
it "should find the candidate_version if a category is specifed and there are no duplicates" do
- output = <<EOF
-Searching...
-[ Results for search key : git ]
-[ Applications found : 14 ]
-
-* app-misc/digitemp [ Masked ]
- Latest version available: 3.5.0
- Latest version installed: [ Not Installed ]
- Size of files: 261 kB
- Homepage: http://www.digitemp.com/ http://www.ibutton.com/
- Description: Temperature logging and reporting using Dallas Semiconductor's iButtons and 1-Wire protocol
- License: GPL-2
-
-* dev-util/git
- Latest version available: 1.6.0.6
- Latest version installed: ignore
- Size of files: 2,725 kB
- Homepage: http://git.or.cz/
- Description: GIT - the stupid content tracker, the revision control system heavily used by the Linux kernel team
- License: GPL-2
-
-* dev-util/gitosis [ Masked ]
- Latest version available: 0.2_p20080825
- Latest version installed: [ Not Installed ]
- Size of files: 31 kB
- Homepage: http://eagain.net/gitweb/?p=gitosis.git;a=summary
- Description: gitosis -- software for hosting git repositories
- License: GPL-2
-EOF
-
- status = double(:stdout => output, :exitstatus => 0)
+ status = double(:stdout => "dev-vcs/git-2.16.2", :exitstatus => 0)
expect(@provider).to receive(:shell_out).and_return(status)
- expect(@provider.candidate_version).to eq("1.6.0.6")
+ expect(@provider.candidate_version).to eq("2.16.2")
end
it "should find the candidate_version if a category is not specifed and there are no duplicates" do
- output = <<EOF
-Searching...
-[ Results for search key : git ]
-[ Applications found : 14 ]
-
-* app-misc/digitemp [ Masked ]
- Latest version available: 3.5.0
- Latest version installed: [ Not Installed ]
- Size of files: 261 kB
- Homepage: http://www.digitemp.com/ http://www.ibutton.com/
- Description: Temperature logging and reporting using Dallas Semiconductor's iButtons and 1-Wire protocol
- License: GPL-2
-
-* dev-util/git
- Latest version available: 1.6.0.6
- Latest version installed: ignore
- Size of files: 2,725 kB
- Homepage: http://git.or.cz/
- Description: GIT - the stupid content tracker, the revision control system heavily used by the Linux kernel team
- License: GPL-2
-
-* dev-util/gitosis [ Masked ]
- Latest version available: 0.2_p20080825
- Latest version installed: [ Not Installed ]
- Size of files: 31 kB
- Homepage: http://eagain.net/gitweb/?p=gitosis.git;a=summary
- Description: gitosis -- software for hosting git repositories
- License: GPL-2
-EOF
-
- status = double(:stdout => output, :exitstatus => 0)
+ status = double(:stdout => "dev-vcs/git-2.16.2", :exitstatus => 0)
@provider = Chef::Provider::Package::Portage.new(@new_resource_without_category, @run_context)
expect(@provider).to receive(:shell_out).and_return(status)
- expect(@provider.candidate_version).to eq("1.6.0.6")
+ expect(@provider.candidate_version).to eq("2.16.2")
end
it "should throw an exception if a category is not specified and there are duplicates" do
- output = <<EOF
-Searching...
-[ Results for search key : git ]
-[ Applications found : 14 ]
-
-* app-misc/digitemp [ Masked ]
- Latest version available: 3.5.0
- Latest version installed: [ Not Installed ]
- Size of files: 261 kB
- Homepage: http://www.digitemp.com/ http://www.ibutton.com/
- Description: Temperature logging and reporting using Dallas Semiconductor's iButtons and 1-Wire protocol
- License: GPL-2
+ stderr_output = <<EOF
+You specified an unqualified atom that matched multiple packages:
+* app-misc/sphinx
+* dev-python/sphinx
-* app-misc/git
- Latest version available: 4.3.20
- Latest version installed: [ Not Installed ]
- Size of files: 416 kB
- Homepage: http://www.gnu.org/software/git/
- Description: GNU Interactive Tools - increase speed and efficiency of most daily task
- License: GPL-2
-
-* dev-util/git
- Latest version available: 1.6.0.6
- Latest version installed: ignore
- Size of files: 2,725 kB
- Homepage: http://git.or.cz/
- Description: GIT - the stupid content tracker, the revision control system heavily used by the Linux kernel team
- License: GPL-2
-
-* dev-util/gitosis [ Masked ]
- Latest version available: 0.2_p20080825
- Latest version installed: [ Not Installed ]
- Size of files: 31 kB
- Homepage: http://eagain.net/gitweb/?p=gitosis.git;a=summary
- Description: gitosis -- software for hosting git repositories
- License: GPL-2
+Please use a more specific atom.
EOF
-
- status = double(:stdout => output, :exitstatus => 0)
+ status = double(:stdout => "", :stderr => stderr_output, :exitstatus => 1)
@provider = Chef::Provider::Package::Portage.new(@new_resource_without_category, @run_context)
expect(@provider).to receive(:shell_out).and_return(status)
expect { @provider.candidate_version }.to raise_error(Chef::Exceptions::Package)
end
-
- it "should find the candidate_version if a category is specifed and there are category duplicates" do
- output = <<EOF
-Searching...
-[ Results for search key : git ]
-[ Applications found : 14 ]
-
-* app-misc/digitemp [ Masked ]
- Latest version available: 3.5.0
- Latest version installed: [ Not Installed ]
- Size of files: 261 kB
- Homepage: http://www.digitemp.com/ http://www.ibutton.com/
- Description: Temperature logging and reporting using Dallas Semiconductor's iButtons and 1-Wire protocol
- License: GPL-2
-
-* app-misc/git
- Latest version available: 4.3.20
- Latest version installed: [ Not Installed ]
- Size of files: 416 kB
- Homepage: http://www.gnu.org/software/git/
- Description: GNU Interactive Tools - increase speed and efficiency of most daily task
- License: GPL-2
-
-* dev-util/git
- Latest version available: 1.6.0.6
- Latest version installed: ignore
- Size of files: 2,725 kB
- Homepage: http://git.or.cz/
- Description: GIT - the stupid content tracker, the revision control system heavily used by the Linux kernel team
- License: GPL-2
-
-* dev-util/gitosis [ Masked ]
- Latest version available: 0.2_p20080825
- Latest version installed: [ Not Installed ]
- Size of files: 31 kB
- Homepage: http://eagain.net/gitweb/?p=gitosis.git;a=summary
- Description: gitosis -- software for hosting git repositories
- License: GPL-2
-EOF
-
- status = double(:stdout => output, :exitstatus => 0)
- @provider = Chef::Provider::Package::Portage.new(@new_resource, @run_context)
- expect(@provider).to receive(:shell_out).and_return(status)
- expect(@provider.candidate_version).to eq("1.6.0.6")
- end
end
describe Chef::Provider::Package::Portage, "install_package" do
it "should install a normally versioned package using portage" do
- expect(@provider).to receive(:shell_out!).with("emerge -g --color n --nospinner --quiet =dev-util/git-1.0.0")
+ expect(@provider).to receive(:shell_out!).with("emerge", "-g", "--color", "n", "--nospinner", "--quiet", "=dev-util/git-1.0.0")
@provider.install_package("dev-util/git", "1.0.0")
end
it "should install a tilde versioned package using portage" do
- expect(@provider).to receive(:shell_out!).with("emerge -g --color n --nospinner --quiet ~dev-util/git-1.0.0")
+ expect(@provider).to receive(:shell_out!).with("emerge", "-g", "--color", "n", "--nospinner", "--quiet", "~dev-util/git-1.0.0")
@provider.install_package("dev-util/git", "~1.0.0")
end
it "should add options to the emerge command when specified" do
- expect(@provider).to receive(:shell_out!).with("emerge -g --color n --nospinner --quiet --oneshot =dev-util/git-1.0.0")
- allow(@new_resource).to receive(:options).and_return("--oneshot")
-
+ expect(@provider).to receive(:shell_out!).with("emerge", "-g", "--color", "n", "--nospinner", "--quiet", "--oneshot", "=dev-util/git-1.0.0")
+ @new_resource.options "--oneshot"
@provider.install_package("dev-util/git", "1.0.0")
end
end
describe Chef::Provider::Package::Portage, "remove_package" do
it "should un-emerge the package with no version specified" do
- expect(@provider).to receive(:shell_out!).with("emerge --unmerge --color n --nospinner --quiet dev-util/git")
+ expect(@provider).to receive(:shell_out!).with("emerge", "--unmerge", "--color", "n", "--nospinner", "--quiet", "dev-util/git")
@provider.remove_package("dev-util/git", nil)
end
it "should un-emerge the package with a version specified" do
- expect(@provider).to receive(:shell_out!).with("emerge --unmerge --color n --nospinner --quiet =dev-util/git-1.0.0")
+ expect(@provider).to receive(:shell_out!).with("emerge", "--unmerge", "--color", "n", "--nospinner", "--quiet", "=dev-util/git-1.0.0")
@provider.remove_package("dev-util/git", "1.0.0")
end
end
diff --git a/spec/unit/provider/package/powershell_spec.rb b/spec/unit/provider/package/powershell_spec.rb
new file mode 100644
index 0000000000..534b7b6b13
--- /dev/null
+++ b/spec/unit/provider/package/powershell_spec.rb
@@ -0,0 +1,488 @@
+#
+# Author:: Dheeraj Dubey(<dheeraj.dubey@msystechnologies.com>)
+# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/mixin/powershell_out"
+
+describe Chef::Provider::Package::Powershell do
+ include Chef::Mixin::PowershellOut
+ let(:timeout) { 900 }
+ let(:source) { nil }
+
+ let(:new_resource) { Chef::Resource::PowershellPackage.new("windows_test_pkg") }
+
+ let(:provider) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ Chef::Provider::Package::Powershell.new(new_resource, run_context)
+ end
+
+ let(:package_xcertificate_installed) do
+ double("powershell_out", :stdout => "2.1.0.0\r\n")
+ end
+
+ let(:package_xcertificate_installed_2_0_0_0) do
+ double("powershell_out", :stdout => "2.0.0.0\r\n")
+ end
+
+ let(:package_xcertificate_available) do
+ double("powershell_out", :stdout => "2.1.0.0\r\n")
+ end
+
+ let(:package_xcertificate_available_2_0_0_0) do
+ double("powershell_out", :stdout => "2.0.0.0\r\n")
+ end
+
+ let(:package_xcertificate_not_installed) do
+ double("powershell_out", :stdout => "")
+ end
+
+ let(:package_xcertificate_not_available) do
+ double("powershell_out", :stdout => "")
+ end
+
+ let(:package_xnetworking_installed) do
+ double("powershell_out", :stdout => "2.12.0.0\r\n")
+ end
+
+ let(:package_xnetworking_installed_2_11_0_0) do
+ double("powershell_out", :stdout => "2.11.0.0\r\n")
+ end
+
+ let(:package_xnetworking_available) do
+ double("powershell_out", :stdout => "2.12.0.0\r\n")
+ end
+
+ let(:package_xnetworking_available_2_11_0_0) do
+ double("powershell_out", :stdout => "2.11.0.0\r\n")
+ end
+
+ let(:package_xnetworking_not_installed) do
+ double("powershell_out", :stdout => "")
+ end
+
+ let(:package_xnetworking_not_available) do
+ double("powershell_out", :stdout => "")
+ end
+
+ let(:package_7zip_available) do
+ double("powershell_out", :stdout => "16.02\r\n")
+ end
+
+ let(:package_7zip_not_installed) do
+ double("powershell_out", :stdout => "")
+ end
+
+ let(:powershell_installed_version) do
+ double("powershell_out", :stdout => "5")
+ end
+
+ let(:generated_command) { "( Get-Package posh-git -Force -ForceBootstrap ).Version" }
+ let(:generated_get_cmdlet) { "( Get-Package xNetworking -Force -ForceBootstrap ).Version" }
+ let(:generated_get_cmdlet_with_version) { "( Get-Package xNetworking -Force -ForceBootstrap -RequiredVersion 1.0.0.0 ).Version" }
+ let(:generated_find_cmdlet) { "( Find-Package xNetworking -Force -ForceBootstrap ).Version" }
+ let(:generated_find_cmdlet_with_version) { "( Find-Package xNetworking -Force -ForceBootstrap -RequiredVersion 1.0.0.0 ).Version" }
+ let(:generated_find_cmdlet_with_source) { "( Find-Package xNetworking -Force -ForceBootstrap -Source MyGallery ).Version" }
+ let(:generated_find_cmdlet_with_source_and_version) { "( Find-Package xNetworking -Force -ForceBootstrap -RequiredVersion 1.0.0.0 -Source MyGallery ).Version" }
+ let(:generated_install_cmdlet) { "( Install-Package xNetworking -Force -ForceBootstrap ).Version" }
+ let(:generated_install_cmdlet_with_version) { "( Install-Package xNetworking -Force -ForceBootstrap -RequiredVersion 1.0.0.0 ).Version" }
+ let(:generated_install_cmdlet_with_source) { "( Install-Package xNetworking -Force -ForceBootstrap -Source MyGallery ).Version" }
+ let(:generated_install_cmdlet_with_source_and_version) { "( Install-Package xNetworking -Force -ForceBootstrap -RequiredVersion 1.0.0.0 -Source MyGallery ).Version" }
+ let(:generated_uninstall_cmdlet) { "( Uninstall-Package xNetworking -Force -ForceBootstrap ).Version" }
+ let(:generated_uninstall_cmdlet_with_version) { "( Uninstall-Package xNetworking -Force -ForceBootstrap -RequiredVersion 1.0.0.0 ).Version" }
+
+ describe "#initialize" do
+ it "should return the correct class" do
+ expect(provider).to be_kind_of(Chef::Provider::Package::Powershell)
+ end
+ end
+
+ describe "#candidate_version" do
+
+ it "should set the candidate_version to the latest version when not pinning" do
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xNetworking' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_available)
+ new_resource.package_name(["xNetworking"])
+ new_resource.version(nil)
+ expect(provider.candidate_version).to eql(["2.12.0.0"])
+ end
+
+ it "should use the candidate_version from the correct source" do
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xNetworking' -Force -ForceBootstrap -Source MyGallery ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_available)
+ new_resource.package_name(["xNetworking"])
+ new_resource.version(nil)
+ new_resource.source("MyGallery")
+ expect(provider.candidate_version).to eql(["2.12.0.0"])
+ end
+
+ it "should set the candidate_version to the latest version when not pinning and package name is space seperated" do
+ allow(provider).to receive(:powershell_out).with("( Find-Package '7-Zip 16.02 (x64)' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_7zip_available)
+ new_resource.package_name(["7-Zip 16.02 (x64)"])
+ new_resource.version(nil)
+ expect(provider.candidate_version).to eql(["16.02"])
+ end
+
+ it "should set the candidate_version to pinned version if available" do
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.0.0.0 ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available_2_0_0_0)
+ new_resource.package_name(["xCertificate"])
+ new_resource.version(["2.0.0.0"])
+ expect(provider.candidate_version).to eql(["2.0.0.0"])
+ end
+
+ it "should set the candidate_version to nil if there is no candidate" do
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_not_available)
+ new_resource.package_name(["xCertificate"])
+ expect(provider.candidate_version).to eql([nil])
+ end
+
+ it "should set the candidate_version correctly when there are two packages to install" do
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xNetworking' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_available)
+ new_resource.package_name(%w{xCertificate xNetworking})
+ new_resource.version(nil)
+ expect(provider.candidate_version).to eql(["2.1.0.0", "2.12.0.0"])
+ end
+
+ it "should set the candidate_version correctly when only the first is installable" do
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xNetworking' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_not_available)
+ new_resource.package_name(%w{xCertificate xNetworking})
+ new_resource.version(nil)
+ expect(provider.candidate_version).to eql(["2.1.0.0", nil])
+ end
+
+ it "should set the candidate_version correctly when only the last is installable" do
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xNetworking' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_available)
+ new_resource.package_name(%w{xCertificate xNetworking})
+ new_resource.version(nil)
+ expect(provider.candidate_version).to eql([nil, "2.12.0.0"])
+ end
+
+ it "should set the candidate_version correctly when neither are is installable and version is passed as nil array" do
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xNetworking' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_not_available)
+ new_resource.package_name(%w{xNetworking xCertificate})
+ new_resource.version([nil, nil])
+ expect(provider.candidate_version).to eql([nil, nil])
+ end
+
+ it "should set the candidate_version correctly when neither are is installable and version is not passed" do
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xNetworking' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_not_available)
+ new_resource.package_name(%w{xNetworking xCertificate})
+ new_resource.version(nil)
+ expect(provider.candidate_version).to eql([nil, nil])
+ end
+
+ end
+
+ describe "#build_powershell_package_command" do
+ it "can build a valid command from a single string" do
+ expect(provider.build_powershell_package_command("Get-Package posh-git")).to eql(generated_command)
+ end
+
+ it "can build a valid command from an array" do
+ expect(provider.build_powershell_package_command(["Get-Package", "posh-git"])).to eql(generated_command)
+ end
+
+ context "when source is nil" do
+ it "build get commands correctly" do
+ expect(provider.build_powershell_package_command("Get-Package xNetworking")).to eql(generated_get_cmdlet)
+ end
+
+ it "build get commands correctly when a version is passed" do
+ expect(provider.build_powershell_package_command("Get-Package xNetworking", "1.0.0.0")).to eql(generated_get_cmdlet_with_version)
+ end
+
+ it "builds find commands correctly" do
+ expect(provider.build_powershell_package_command("Find-Package xNetworking")).to eql(generated_find_cmdlet)
+ end
+
+ it "builds find commands correctly when a version is passed" do
+ expect(provider.build_powershell_package_command("Find-Package xNetworking", "1.0.0.0")).to eql(generated_find_cmdlet_with_version)
+ end
+
+ it "build install commands correctly" do
+ expect(provider.build_powershell_package_command("Install-Package xNetworking")).to eql(generated_install_cmdlet)
+ end
+
+ it "build install commands correctly when a version is passed" do
+ expect(provider.build_powershell_package_command("Install-Package xNetworking", "1.0.0.0")).to eql(generated_install_cmdlet_with_version)
+ end
+
+ it "build install commands correctly" do
+ expect(provider.build_powershell_package_command("Uninstall-Package xNetworking")).to eql(generated_uninstall_cmdlet)
+ end
+
+ it "build install commands correctly when a version is passed" do
+ expect(provider.build_powershell_package_command("Uninstall-Package xNetworking", "1.0.0.0")).to eql(generated_uninstall_cmdlet_with_version)
+ end
+ end
+
+ context "when source is set" do
+ it "build get commands correctly" do
+ new_resource.source("MyGallery")
+ expect(provider.build_powershell_package_command("Get-Package xNetworking")).to eql(generated_get_cmdlet)
+ end
+
+ it "build get commands correctly when a version is passed" do
+ new_resource.source("MyGallery")
+ expect(provider.build_powershell_package_command("Get-Package xNetworking", "1.0.0.0")).to eql(generated_get_cmdlet_with_version)
+ end
+
+ it "builds find commands correctly" do
+ new_resource.source("MyGallery")
+ expect(provider.build_powershell_package_command("Find-Package xNetworking")).to eql(generated_find_cmdlet_with_source)
+ end
+
+ it "builds find commands correctly when a version is passed" do
+ new_resource.source("MyGallery")
+ expect(provider.build_powershell_package_command("Find-Package xNetworking", "1.0.0.0")).to eql(generated_find_cmdlet_with_source_and_version)
+ end
+
+ it "build install commands correctly" do
+ new_resource.source("MyGallery")
+ expect(provider.build_powershell_package_command("Install-Package xNetworking")).to eql(generated_install_cmdlet_with_source)
+ end
+
+ it "build install commands correctly when a version is passed" do
+ new_resource.source("MyGallery")
+ expect(provider.build_powershell_package_command("Install-Package xNetworking", "1.0.0.0")).to eql(generated_install_cmdlet_with_source_and_version)
+ end
+
+ it "build install commands correctly" do
+ new_resource.source("MyGallery")
+ expect(provider.build_powershell_package_command("Uninstall-Package xNetworking")).to eql(generated_uninstall_cmdlet)
+ end
+
+ it "build install commands correctly when a version is passed" do
+ new_resource.source("MyGallery")
+ expect(provider.build_powershell_package_command("Uninstall-Package xNetworking", "1.0.0.0")).to eql(generated_uninstall_cmdlet_with_version)
+ end
+ end
+ end
+
+ describe "#action_install" do
+ it "should install a single package" do
+ provider.load_current_resource
+ new_resource.package_name(["xCertificate"])
+ new_resource.version(nil)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("$PSVersionTable.PSVersion.Major").and_return(powershell_installed_version)
+ expect(provider).to receive(:powershell_out).with("( Install-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 ).Version", { :timeout => new_resource.timeout })
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should install a single package from a custom source" do
+ provider.load_current_resource
+ new_resource.package_name(["xCertificate"])
+ new_resource.version(nil)
+ new_resource.source("MyGallery")
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap -Source MyGallery ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("$PSVersionTable.PSVersion.Major").and_return(powershell_installed_version)
+ expect(provider).to receive(:powershell_out).with("( Install-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 -Source MyGallery ).Version", { :timeout => new_resource.timeout })
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should install a single package when package name has space in between" do
+ provider.load_current_resource
+ new_resource.package_name(["7-Zip 16.02 (x64)"])
+ new_resource.version(nil)
+ allow(provider).to receive(:powershell_out).with("( Find-Package '7-Zip 16.02 (x64)' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_7zip_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package '7-Zip 16.02 (x64)' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_7zip_not_installed)
+ allow(provider).to receive(:powershell_out).with("$PSVersionTable.PSVersion.Major").and_return(powershell_installed_version)
+ expect(provider).to receive(:powershell_out).with("( Install-Package '7-Zip 16.02 (x64)' -Force -ForceBootstrap -RequiredVersion 16.02 ).Version", { :timeout => new_resource.timeout })
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ context "when changing the timeout to 3600" do
+ let(:timeout) { 3600 }
+ it "sets the timeout on shell_out commands" do
+ new_resource.timeout(timeout)
+ provider.load_current_resource
+ new_resource.package_name(["xCertificate"])
+ new_resource.version(nil)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("$PSVersionTable.PSVersion.Major").and_return(powershell_installed_version)
+ expect(provider).to receive(:powershell_out).with("( Install-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 ).Version", { :timeout => new_resource.timeout })
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+
+ it "should not install packages that are up-to-date" do
+ new_resource.package_name(["xCertificate"])
+ new_resource.version(nil)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("$PSVersionTable.PSVersion.Major").and_return(powershell_installed_version)
+ provider.load_current_resource
+ expect(provider).not_to receive(:install_package)
+ provider.run_action(:install)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "should not install packages that are up-to-date" do
+ new_resource.package_name(["xNetworking"])
+ new_resource.version(["2.11.0.0"])
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xNetworking' -Force -ForceBootstrap -RequiredVersion 2.11.0.0 ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xNetworking' -Force -ForceBootstrap -RequiredVersion 2.11.0.0 ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_available_2_11_0_0)
+ allow(provider).to receive(:powershell_out).with("$PSVersionTable.PSVersion.Major").and_return(powershell_installed_version)
+ provider.load_current_resource
+ expect(provider).not_to receive(:install_package)
+ provider.run_action(:install)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "should handle complicated cases when the name/version array is pruned" do
+ # implicitly test that we correctly pick up new_resource.version[1] instead of
+ # new_version.resource[0]
+ new_resource.package_name(%w{xCertificate xNetworking})
+ new_resource.version([nil, "2.11.0.0"])
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xNetworking' -Force -ForceBootstrap -RequiredVersion 2.11.0.0 ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_available_2_11_0_0)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xNetworking' -Force -ForceBootstrap -RequiredVersion 2.11.0.0 ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_not_available)
+ allow(provider).to receive(:powershell_out).with("$PSVersionTable.PSVersion.Major").and_return(powershell_installed_version)
+ expect(provider).to receive(:powershell_out).with("( Install-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 ).Version", { :timeout => new_resource.timeout })
+ expect(provider).to receive(:powershell_out).with("( Install-Package 'xNetworking' -Force -ForceBootstrap -RequiredVersion 2.11.0.0 ).Version", { :timeout => new_resource.timeout })
+ provider.load_current_resource
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should split up commands when given two packages, one with a version pin" do
+ new_resource.package_name(%w{xCertificate xNetworking})
+ new_resource.version(["2.1.0.0", nil])
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xNetworking' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xNetworking' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_not_available)
+ allow(provider).to receive(:powershell_out).with("$PSVersionTable.PSVersion.Major").and_return(powershell_installed_version)
+ expect(provider).to receive(:powershell_out).with("( Install-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 ).Version", { :timeout => new_resource.timeout })
+ expect(provider).to receive(:powershell_out).with("( Install-Package 'xNetworking' -Force -ForceBootstrap -RequiredVersion 2.12.0.0 ).Version", { :timeout => new_resource.timeout })
+
+ provider.load_current_resource
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should do multipackage installs when given two packages without constraints" do
+ new_resource.package_name(%w{xCertificate xNetworking})
+ new_resource.version(nil)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xNetworking' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xNetworking' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_not_available)
+ allow(provider).to receive(:powershell_out).with("$PSVersionTable.PSVersion.Major").and_return(powershell_installed_version)
+ expect(provider).to receive(:powershell_out).with("( Install-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 ).Version", { :timeout => new_resource.timeout })
+ expect(provider).to receive(:powershell_out).with("( Install-Package 'xNetworking' -Force -ForceBootstrap -RequiredVersion 2.12.0.0 ).Version", { :timeout => new_resource.timeout })
+ provider.load_current_resource
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should do multipackage installs from a custom source when given two packages without constraints" do
+ new_resource.package_name(%w{xCertificate xNetworking})
+ new_resource.version(nil)
+ new_resource.source("MyGallery")
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap -Source MyGallery ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xNetworking' -Force -ForceBootstrap -Source MyGallery ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xNetworking' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_not_available)
+ allow(provider).to receive(:powershell_out).with("$PSVersionTable.PSVersion.Major").and_return(powershell_installed_version)
+ expect(provider).to receive(:powershell_out).with("( Install-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 -Source MyGallery ).Version", { :timeout => new_resource.timeout })
+ expect(provider).to receive(:powershell_out).with("( Install-Package 'xNetworking' -Force -ForceBootstrap -RequiredVersion 2.12.0.0 -Source MyGallery ).Version", { :timeout => new_resource.timeout })
+ provider.load_current_resource
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+
+ describe "#action_remove" do
+ it "does nothing when the package is already removed" do
+ provider.load_current_resource
+ new_resource.package_name(["xCertificate"])
+ new_resource.version(["2.1.0.0"])
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("$PSVersionTable.PSVersion.Major").and_return(powershell_installed_version)
+ expect(provider).not_to receive(:remove_package)
+ provider.run_action(:remove)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "does not pass the source parameter to get or uninstall cmdlets" do
+ new_resource.package_name(["xCertificate"])
+ new_resource.version(["2.1.0.0"])
+ new_resource.source("MyGallery")
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 -Source MyGallery).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("$PSVersionTable.PSVersion.Major").and_return(powershell_installed_version)
+ provider.load_current_resource
+ expect(provider).to receive(:powershell_out).with("( Uninstall-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 ).Version", { :timeout => new_resource.timeout })
+ provider.run_action(:remove)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "does nothing when all the packages are already removed" do
+ new_resource.package_name(%w{xCertificate xNetworking})
+ new_resource.version(nil)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xNetworking' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xNetworking' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xnetworking_not_available)
+ allow(provider).to receive(:powershell_out).with("$PSVersionTable.PSVersion.Major").and_return(powershell_installed_version)
+ provider.load_current_resource
+ expect(provider).not_to receive(:remove_package)
+ provider.run_action(:remove)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "removes a package when version is specified" do
+ new_resource.package_name(["xCertificate"])
+ new_resource.version(["2.1.0.0"])
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("$PSVersionTable.PSVersion.Major").and_return(powershell_installed_version)
+ provider.load_current_resource
+ expect(provider).to receive(:powershell_out).with("( Uninstall-Package 'xCertificate' -Force -ForceBootstrap -RequiredVersion 2.1.0.0 ).Version", { :timeout => new_resource.timeout })
+ provider.run_action(:remove)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "removes a package when version is not specified" do
+ new_resource.package_name(["xCertificate"])
+ new_resource.version(nil)
+ allow(provider).to receive(:powershell_out).with("( Find-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("( Get-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("$PSVersionTable.PSVersion.Major").and_return(powershell_installed_version)
+ provider.load_current_resource
+ expect(provider).to receive(:powershell_out).with("( Uninstall-Package 'xCertificate' -Force -ForceBootstrap ).Version", { :timeout => new_resource.timeout }).and_return(package_xcertificate_not_available)
+ provider.run_action(:remove)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+end
diff --git a/spec/unit/provider/package/rpm_spec.rb b/spec/unit/provider/package/rpm_spec.rb
index 1d179edf76..3730878026 100644
--- a/spec/unit/provider/package/rpm_spec.rb
+++ b/spec/unit/provider/package/rpm_spec.rb
@@ -41,7 +41,7 @@ describe Chef::Provider::Package::Rpm do
let(:rpm_q_status) { instance_double("Mixlib::ShellOut", exitstatus: rpm_q_exitstatus, stdout: rpm_q_stdout) }
before(:each) do
- allow(::File).to receive(:exists?).with("PLEASE STUB File.exists? EXACTLY").and_return(true)
+ allow(::File).to receive(:exist?).with("PLEASE STUB File.exists? EXACTLY").and_return(true)
# Ensure all shell out usage is stubbed with exact arguments
allow(provider).to receive(:shell_out!).with("PLEASE STUB YOUR SHELLOUT CALLS").and_return(nil)
@@ -61,7 +61,7 @@ describe Chef::Provider::Package::Rpm do
context "when the source is a file that doesn't exist" do
it "should raise an exception when attempting any action" do
- allow(::File).to receive(:exists?).with(package_source).and_return(false)
+ allow(::File).to receive(:exist?).with(package_source).and_return(false)
expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package)
end
end
@@ -71,7 +71,7 @@ describe Chef::Provider::Package::Rpm do
let(:package_source) { "foobar://example.com/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm" }
it "should raise an exception if an uri formed source is non-supported scheme" do
- allow(::File).to receive(:exists?).with(package_source).and_return(false)
+ allow(::File).to receive(:exist?).with(package_source).and_return(false)
# verify let bindings are as we expect
expect(new_resource.source).to eq("foobar://example.com/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
@@ -86,18 +86,18 @@ describe Chef::Provider::Package::Rpm do
before do
expect(provider).to receive(:shell_out!).
- with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{package_source}", timeout: 900).
+ with("rpm", "-qp", "--queryformat", "%{NAME} %{VERSION}-%{RELEASE}\n", package_source, timeout: 900).
and_return(rpm_qp_status)
expect(provider).to receive(:shell_out).
- with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{package_name}", timeout: 900).
+ with("rpm", "-q", "--queryformat", "%{NAME} %{VERSION}-%{RELEASE}\n", package_name, timeout: 900).
and_return(rpm_q_status)
end
context "when rpm fails when querying package installed state" do
before do
- allow(::File).to receive(:exists?).with(package_source).and_return(true)
+ allow(::File).to receive(:exist?).with(package_source).and_return(true)
end
let(:rpm_qp_stdout) { "ImageMagick-c++ 6.5.4.7-7.el6_5" }
@@ -129,7 +129,7 @@ describe Chef::Provider::Package::Rpm do
context "when the source is a file system path" do
before do
- allow(::File).to receive(:exists?).with(package_source).and_return(true)
+ allow(::File).to receive(:exist?).with(package_source).and_return(true)
provider.action = action
@@ -151,7 +151,7 @@ describe Chef::Provider::Package::Rpm do
context "when at the desired version already" do
it "does nothing when the correct version is installed" do
- expect(provider).to_not receive(:shell_out!).with("rpm -i /tmp/imagemagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ expect(provider).to_not receive(:shell_out!).with("rpm", "-i", "/tmp/imagemagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
provider.action_install
end
@@ -162,7 +162,7 @@ describe Chef::Provider::Package::Rpm do
let(:rpm_q_stdout) { "imagemagick-c++ 0.5.4.7-7.el6_5" }
it "runs rpm -u with the package source to upgrade" do
- expect(provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ expect(provider).to receive(:shell_out!).with("rpm", "-U", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
provider.action_install
end
end
@@ -178,7 +178,7 @@ describe Chef::Provider::Package::Rpm do
let(:rpm_q_stdout) { "imagemagick-c++ 21.4-19.el6_5" }
it "should run rpm -u --oldpackage with the package source to downgrade" do
- expect(provider).to receive(:shell_out!).with("rpm -U --oldpackage /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ expect(provider).to receive(:shell_out!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
provider.action_install
end
@@ -192,7 +192,7 @@ describe Chef::Provider::Package::Rpm do
context "when at the desired version already" do
it "does nothing when the correct version is installed" do
- expect(provider).to_not receive(:shell_out!).with("rpm -i /tmp/imagemagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ expect(provider).to_not receive(:shell_out!).with("rpm", "-i", "/tmp/imagemagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
provider.action_upgrade
end
@@ -203,7 +203,7 @@ describe Chef::Provider::Package::Rpm do
let(:rpm_q_stdout) { "imagemagick-c++ 0.5.4.7-7.el6_5" }
it "runs rpm -u with the package source to upgrade" do
- expect(provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ expect(provider).to receive(:shell_out!).with("rpm", "-U", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
provider.action_upgrade
end
end
@@ -219,7 +219,7 @@ describe Chef::Provider::Package::Rpm do
let(:rpm_q_stdout) { "imagemagick-c++ 21.4-19.el6_5" }
it "should run rpm -u --oldpackage with the package source to downgrade" do
- expect(provider).to receive(:shell_out!).with("rpm -U --oldpackage /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ expect(provider).to receive(:shell_out!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
provider.action_upgrade
end
@@ -231,7 +231,7 @@ describe Chef::Provider::Package::Rpm do
let(:action) { :remove }
it "should remove the package" do
- expect(provider).to receive(:shell_out!).with("rpm -e ImageMagick-c++-6.5.4.7-7.el6_5", timeout: 900)
+ expect(provider).to receive(:shell_out!).with("rpm", "-e", "ImageMagick-c++-6.5.4.7-7.el6_5", timeout: 900)
provider.action_remove
end
end
@@ -276,7 +276,7 @@ describe Chef::Provider::Package::Rpm do
context "when the source is given as an URI" do
before(:each) do
- allow(::File).to receive(:exists?).with(package_source).and_return(false)
+ allow(::File).to receive(:exist?).with(package_source).and_return(false)
provider.action = action
@@ -322,7 +322,7 @@ describe Chef::Provider::Package::Rpm do
let(:action) { :install }
before do
- allow(File).to receive(:exists?).with(package_source).and_return(true)
+ allow(File).to receive(:exist?).with(package_source).and_return(true)
provider.action = action
@@ -357,7 +357,7 @@ describe Chef::Provider::Package::Rpm do
describe "action install" do
it "installs the package" do
- expect(provider).to receive(:shell_out!).with("rpm -i #{package_source}", timeout: 900)
+ expect(provider).to receive(:shell_out!).with("rpm", "-i", package_source, timeout: 900)
provider.action_install
end
@@ -365,7 +365,7 @@ describe Chef::Provider::Package::Rpm do
context "when custom resource options are given" do
it "installs with custom options specified in the resource" do
new_resource.options("--dbpath /var/lib/rpm")
- expect(provider).to receive(:shell_out!).with("rpm --dbpath /var/lib/rpm -i #{package_source}", timeout: 900)
+ expect(provider).to receive(:shell_out!).with("rpm", "--dbpath", "/var/lib/rpm", "-i", package_source, timeout: 900)
provider.action_install
end
end
@@ -376,7 +376,7 @@ describe Chef::Provider::Package::Rpm do
let(:action) { :upgrade }
it "installs the package" do
- expect(provider).to receive(:shell_out!).with("rpm -i #{package_source}", timeout: 900)
+ expect(provider).to receive(:shell_out!).with("rpm", "-i", package_source, timeout: 900)
provider.action_upgrade
end
@@ -387,7 +387,7 @@ describe Chef::Provider::Package::Rpm do
let(:action) { :remove }
it "should do nothing" do
- expect(provider).to_not receive(:shell_out!).with("rpm -e ImageMagick-c++-6.5.4.7-7.el6_5", timeout: 900)
+ expect(provider).to_not receive(:shell_out!).with("rpm", "-e", "ImageMagick-c++-6.5.4.7-7.el6_5", timeout: 900)
provider.action_remove
end
end
@@ -404,7 +404,7 @@ describe Chef::Provider::Package::Rpm do
# provider will call File.exists?. Because of the ordering in our
# let() bindings and such, we have to set the stub here and not in a
# before block.
- allow(::File).to receive(:exists?).with(package_source).and_return(true)
+ allow(::File).to receive(:exist?).with(package_source).and_return(true)
Chef::Resource::Package.new("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
end
@@ -413,7 +413,7 @@ describe Chef::Provider::Package::Rpm do
it "should install from a path when the package is a path and the source is nil" do
expect(new_resource.source).to eq("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
provider.current_resource = current_resource
- expect(provider).to receive(:shell_out!).with("rpm -i /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ expect(provider).to receive(:shell_out!).with("rpm", "-i", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
provider.install_package("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", "6.5.4.7-7.el6_5")
end
@@ -421,7 +421,7 @@ describe Chef::Provider::Package::Rpm do
expect(new_resource.source).to eq("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
current_resource.version("21.4-19.el5")
provider.current_resource = current_resource
- expect(provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ expect(provider).to receive(:shell_out!).with("rpm", "-U", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
provider.upgrade_package("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", "6.5.4.7-7.el6_5")
end
end
diff --git a/spec/unit/provider/package/rubygems_spec.rb b/spec/unit/provider/package/rubygems_spec.rb
index 6e77e7c112..28bd371539 100644
--- a/spec/unit/provider/package/rubygems_spec.rb
+++ b/spec/unit/provider/package/rubygems_spec.rb
@@ -16,7 +16,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require "pp"
module GemspecBackcompatCreator
def gemspec(name, version)
@@ -34,8 +33,10 @@ require "ostruct"
describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
include GemspecBackcompatCreator
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
before do
@gem_env = Chef::Provider::Package::Rubygems::CurrentGemEnvironment.new
+ allow(@gem_env).to receive(:logger).and_return(logger)
end
it "determines the gem paths from the in memory rubygems" do
@@ -44,7 +45,10 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
it "determines the installed versions of gems from Gem.source_index" do
gems = [gemspec("rspec-core", Gem::Version.new("1.2.9")), gemspec("rspec-core", Gem::Version.new("1.3.0"))]
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("1.8.0")
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("2.7")
+ expect(Gem::Specification).to receive(:dirs).and_return(["/path/to/gems/specifications", "/another/path/to/gems/specifications"])
+ expect(Gem::Specification).to receive(:installed_stubs).with(["/path/to/gems/specifications", "/another/path/to/gems/specifications"], "rspec-core-*.gemspec").and_return(gems)
+ elsif Gem::Version.new(Gem::VERSION) >= Gem::Version.new("1.8.0")
expect(Gem::Specification).to receive(:find_all_by_name).with("rspec-core", Gem::Dependency.new("rspec-core").requirement).and_return(gems)
else
expect(Gem.source_index).to receive(:search).with(Gem::Dependency.new("rspec-core", nil)).and_return(gems)
@@ -64,7 +68,7 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
begin
@gem_env.with_gem_sources("http://gems.example.org") do
sources_in_block = Gem.sources
- raise RuntimeError, "sources should be reset even in case of an error"
+ raise "sources should be reset even in case of an error"
end
rescue RuntimeError
end
@@ -78,7 +82,7 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
begin
@gem_env.with_gem_sources(nil) do
sources_in_block = Gem.sources
- raise RuntimeError, "sources should be reset even in case of an error"
+ raise "sources should be reset even in case of an error"
end
rescue RuntimeError
end
@@ -86,34 +90,55 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
expect(Gem.sources).to eq(normal_sources)
end
- it "finds a matching gem candidate version" do
- dep = Gem::Dependency.new("rspec", ">= 0")
- dep_installer = Gem::DependencyInstaller.new
- allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer)
- latest = [[gemspec("rspec", Gem::Version.new("1.3.0")), "https://rubygems.org/"]]
- expect(dep_installer).to receive(:find_gems_with_sources).with(dep).and_return(latest)
- expect(@gem_env.candidate_version_from_remote(Gem::Dependency.new("rspec", ">= 0"))).to eq(Gem::Version.new("1.3.0"))
- end
+ context "new default rubygems behavior" do
+ before do
+ Chef::Config[:rubygems_cache_enabled] = false
+ end
- it "finds a matching gem candidate version on rubygems 2.0.0+" do
- dep = Gem::Dependency.new("rspec", ">= 0")
- dep_installer = Gem::DependencyInstaller.new
- allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer)
- best_gem = double("best gem match", :spec => gemspec("rspec", Gem::Version.new("1.3.0")), :source => "https://rubygems.org")
- available_set = double("Gem::AvailableSet test double")
- expect(available_set).to receive(:pick_best!)
- expect(available_set).to receive(:set).and_return([best_gem])
- expect(dep_installer).to receive(:find_gems_with_sources).with(dep).and_return(available_set)
- expect(@gem_env.candidate_version_from_remote(Gem::Dependency.new("rspec", ">= 0"))).to eq(Gem::Version.new("1.3.0"))
+ it "finds a matching gem candidate version on rubygems 2.0.0+" do
+ dep = Gem::Dependency.new("rspec", ">= 0")
+ dep_installer = Gem::DependencyInstaller.new
+ allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer)
+ expect(dep_installer).not_to receive(:find_gems_with_sources).with(dep).and_call_original
+ expect(@gem_env.candidate_version_from_remote(dep)).to be_kind_of(Gem::Version)
+ end
+
+ it "gives the candidate version as nil if none is found" do
+ dep = Gem::Dependency.new("lksdjflksdjflsdkfj", ">= 0")
+ dep_installer = Gem::DependencyInstaller.new
+ allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer)
+ expect(dep_installer).not_to receive(:find_gems_with_sources).with(dep).and_call_original
+ expect(@gem_env.candidate_version_from_remote(dep)).to be_nil
+ end
+
+ it "finds a matching gem from a specific gemserver when explicit sources are given (to a server that doesn't respond to api requests)" do
+ dep = Gem::Dependency.new("rspec", ">= 0")
+ dep_installer = Gem::DependencyInstaller.new
+ allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer)
+ expect(dep_installer).not_to receive(:find_gems_with_sources).with(dep).and_call_original
+ expect(@gem_env.candidate_version_from_remote(dep, "http://production.cf.rubygems.org")).to be_kind_of(Gem::Version)
+ end
end
- it "gives the candidate version as nil if none is found" do
- dep = Gem::Dependency.new("rspec", ">= 0")
- latest = []
- dep_installer = Gem::DependencyInstaller.new
- allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer)
- expect(dep_installer).to receive(:find_gems_with_sources).with(dep).and_return(latest)
- expect(@gem_env.candidate_version_from_remote(Gem::Dependency.new("rspec", ">= 0"))).to be_nil
+ context "old rubygems caching behavior" do
+ before do
+ Chef::Config[:rubygems_cache_enabled] = true
+ end
+
+ it "finds a matching gem candidate version on rubygems 2.0.0+" do
+ dep = Gem::Dependency.new("rspec", ">= 0")
+ expect(@gem_env.candidate_version_from_remote(dep)).to be_kind_of(Gem::Version)
+ end
+
+ it "gives the candidate version as nil if none is found" do
+ dep = Gem::Dependency.new("lksdjflksdjflsdkfj", ">= 0")
+ expect(@gem_env.candidate_version_from_remote(dep)).to be_nil
+ end
+
+ it "finds a matching gem from a specific gemserver when explicit sources are given" do
+ dep = Gem::Dependency.new("rspec", ">= 0")
+ expect(@gem_env.candidate_version_from_remote(dep, "http://production.cf.rubygems.org")).to be_kind_of(Gem::Version)
+ end
end
it "finds a matching candidate version from a .gem file when the path to the gem is supplied" do
@@ -122,43 +147,32 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
expect(@gem_env.candidate_version_from_file(Gem::Dependency.new("chef-integration-test", ">= 0.2.0"), location)).to be_nil
end
- it "finds a matching gem from a specific gemserver when explicit sources are given" do
- dep = Gem::Dependency.new("rspec", ">= 0")
- latest = [[gemspec("rspec", Gem::Version.new("1.3.0")), "https://rubygems.org/"]]
-
- expect(@gem_env).to receive(:with_gem_sources).with("http://gems.example.com").and_yield
- dep_installer = Gem::DependencyInstaller.new
- allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer)
- expect(dep_installer).to receive(:find_gems_with_sources).with(dep).and_return(latest)
- expect(@gem_env.candidate_version_from_remote(Gem::Dependency.new("rspec", ">=0"), "http://gems.example.com")).to eq(Gem::Version.new("1.3.0"))
- end
-
it "installs a gem with a hash of options for the dependency installer" do
dep_installer = Gem::DependencyInstaller.new
- expect(@gem_env).to receive(:dependency_installer).with(:install_dir => "/foo/bar").and_return(dep_installer)
+ expect(@gem_env).to receive(:dependency_installer).with(install_dir: "/foo/bar").and_return(dep_installer)
expect(@gem_env).to receive(:with_gem_sources).with("http://gems.example.com").and_yield
expect(dep_installer).to receive(:install).with(Gem::Dependency.new("rspec", ">= 0"))
- @gem_env.install(Gem::Dependency.new("rspec", ">= 0"), :install_dir => "/foo/bar", :sources => ["http://gems.example.com"])
+ @gem_env.install(Gem::Dependency.new("rspec", ">= 0"), install_dir: "/foo/bar", sources: ["http://gems.example.com"])
end
it "builds an uninstaller for a gem with options set to avoid requiring user input" do
# default options for uninstaller should be:
# :ignore => true, :executables => true
- expect(Gem::Uninstaller).to receive(:new).with("rspec", :ignore => true, :executables => true)
+ expect(Gem::Uninstaller).to receive(:new).with("rspec", ignore: true, executables: true)
@gem_env.uninstaller("rspec")
end
it "uninstalls all versions of a gem" do
uninstaller = double("gem uninstaller")
expect(uninstaller).to receive(:uninstall)
- expect(@gem_env).to receive(:uninstaller).with("rspec", :all => true).and_return(uninstaller)
+ expect(@gem_env).to receive(:uninstaller).with("rspec", all: true).and_return(uninstaller)
@gem_env.uninstall("rspec")
end
it "uninstalls a specific version of a gem" do
uninstaller = double("gem uninstaller")
expect(uninstaller).to receive(:uninstall)
- expect(@gem_env).to receive(:uninstaller).with("rspec", :version => "1.2.3").and_return(uninstaller)
+ expect(@gem_env).to receive(:uninstaller).with("rspec", version: "1.2.3").and_return(uninstaller)
@gem_env.uninstall("rspec", "1.2.3")
end
@@ -175,14 +189,14 @@ describe Chef::Provider::Package::Rubygems::AlternateGemEnvironment do
it "determines the gem paths from shelling out to gem env" do
gem_env_output = ["/path/to/gems", "/another/path/to/gems"].join(File::PATH_SEPARATOR)
- shell_out_result = OpenStruct.new(:stdout => gem_env_output)
+ shell_out_result = OpenStruct.new(stdout: gem_env_output)
expect(@gem_env).to receive(:shell_out!).with("/usr/weird/bin/gem env gempath").and_return(shell_out_result)
expect(@gem_env.gem_paths).to eq(["/path/to/gems", "/another/path/to/gems"])
end
it "caches the gempaths by gem_binary" do
gem_env_output = ["/path/to/gems", "/another/path/to/gems"].join(File::PATH_SEPARATOR)
- shell_out_result = OpenStruct.new(:stdout => gem_env_output)
+ shell_out_result = OpenStruct.new(stdout: gem_env_output)
expect(@gem_env).to receive(:shell_out!).with("/usr/weird/bin/gem env gempath").and_return(shell_out_result)
expected = ["/path/to/gems", "/another/path/to/gems"]
expect(@gem_env.gem_paths).to eq(["/path/to/gems", "/another/path/to/gems"])
@@ -210,7 +224,11 @@ describe Chef::Provider::Package::Rubygems::AlternateGemEnvironment do
it "determines the installed versions of gems from the source index" do
gems = [gemspec("rspec", Gem::Version.new("1.2.9")), gemspec("rspec", Gem::Version.new("1.3.0"))]
rspec_dep = Gem::Dependency.new("rspec", nil)
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("1.8.0")
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("2.7")
+ allow(@gem_env).to receive(:gem_specification).and_return(Gem::Specification)
+ expect(Gem::Specification).to receive(:dirs).and_return(["/path/to/gems/specifications", "/another/path/to/gems/specifications"])
+ expect(Gem::Specification).to receive(:installed_stubs).with(["/path/to/gems/specifications", "/another/path/to/gems/specifications"], "rspec-*.gemspec").and_return(gems)
+ elsif Gem::Version.new(Gem::VERSION) >= Gem::Version.new("1.8.0")
allow(@gem_env).to receive(:gem_specification).and_return(Gem::Specification)
expect(@gem_env.gem_specification).to receive(:find_all_by_name).with(rspec_dep.name, rspec_dep.requirement).and_return(gems)
else
@@ -262,7 +280,7 @@ RubyGems Environment:
- https://rubygems.org/
- http://gems.github.com/
JRUBY_GEM_ENV
- expect(@gem_env).to receive(:shell_out!).with("/usr/weird/bin/gem env").and_return(double("jruby_gem_env", :stdout => gem_env_out))
+ expect(@gem_env).to receive(:shell_out!).with("/usr/weird/bin/gem env").and_return(double("jruby_gem_env", stdout: gem_env_out))
expected = ["ruby", Gem::Platform.new("universal-java-1.6")]
expect(@gem_env.gem_platforms).to eq(expected)
# it should also cache the result
@@ -304,7 +322,7 @@ RubyGems Environment:
- https://rubygems.org/
- http://gems.github.com/
RBX_GEM_ENV
- expect(@gem_env).to receive(:shell_out!).with("/usr/weird/bin/gem env").and_return(double("rbx_gem_env", :stdout => gem_env_out))
+ expect(@gem_env).to receive(:shell_out!).with("/usr/weird/bin/gem env").and_return(double("rbx_gem_env", stdout: gem_env_out))
expect(@gem_env.gem_platforms).to eq(Gem.platforms)
expect(Chef::Provider::Package::Rubygems::AlternateGemEnvironment.platform_cache["/usr/weird/bin/gem"]).to eq(Gem.platforms)
end
@@ -329,9 +347,10 @@ describe Chef::Provider::Package::Rubygems do
let(:target_version) { nil }
let(:gem_name) { "rspec-core" }
let(:gem_binary) { nil }
- let(:bindir) { "/usr/bin/ruby" }
+ let(:bindir) { "/usr/bin" }
let(:options) { nil }
let(:source) { nil }
+ let(:include_default_source) { true }
let(:new_resource) do
new_resource = Chef::Resource::GemPackage.new(gem_name)
@@ -339,10 +358,11 @@ describe Chef::Provider::Package::Rubygems do
new_resource.gem_binary(gem_binary) if gem_binary
new_resource.options(options) if options
new_resource.source(source) if source
+ new_resource.include_default_source(include_default_source)
new_resource
end
- let (:current_resource) { nil }
+ let(:current_resource) { nil }
let(:provider) do
run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
@@ -361,6 +381,8 @@ describe Chef::Provider::Package::Rubygems do
allow(RbConfig::CONFIG).to receive(:[]).with("bindir").and_return(bindir)
# Rubygems uses this interally
allow(RbConfig::CONFIG).to receive(:[]).with("arch").and_call_original
+ allow(File).to receive(:executable?).and_return false
+ allow(File).to receive(:executable?).with("#{bindir}/gem").and_return true
end
describe "when new_resource version is nil" do
@@ -370,6 +392,11 @@ describe Chef::Provider::Package::Rubygems do
provider.load_current_resource
expect(provider.target_version_already_installed?(provider.current_resource.version, new_resource.version)).to be_falsey
end
+
+ it "version_equals? should return false so that we can search for candidates" do
+ provider.load_current_resource
+ expect(provider.version_equals?(provider.current_resource.version, new_resource.version)).to be_falsey
+ end
end
describe "when new_resource version is an rspec version" do
@@ -394,7 +421,7 @@ describe Chef::Provider::Package::Rubygems do
end
context "when you try to use a hash of install options" do
- let(:options) { { :fail => :burger } }
+ let(:options) { { fail: :burger } }
it "smites you" do
expect { provider }.to raise_error(ArgumentError)
@@ -428,9 +455,9 @@ describe Chef::Provider::Package::Rubygems do
it "searches for a gem binary when running on Omnibus on Unix" do
platform_mock :unix do
allow(ENV).to receive(:[]).with("PATH").and_return("/usr/bin:/usr/sbin:/opt/chef/embedded/bin")
- allow(File).to receive(:exists?).with("/usr/bin/gem").and_return(false)
- allow(File).to receive(:exists?).with("/usr/sbin/gem").and_return(true)
- allow(File).to receive(:exists?).with("/opt/chef/embedded/bin/gem").and_return(true) # should not get here
+ allow(File).to receive(:executable?).with("/usr/bin/gem").and_return(false)
+ allow(File).to receive(:executable?).with("/usr/sbin/gem").and_return(true)
+ allow(File).to receive(:executable?).with("/opt/chef/embedded/bin/gem").and_return(true) # should not get here
expect(provider.gem_env.gem_binary_location).to eq("/usr/sbin/gem")
end
end
@@ -440,13 +467,14 @@ describe Chef::Provider::Package::Rubygems do
it "searches for a gem binary when running on Omnibus on Windows" do
platform_mock :windows do
- allow(ENV).to receive(:[]).with("PATH").and_return('C:\windows\system32;C:\windows;C:\Ruby186\bin;d:\opscode\chef\embedded\bin')
- allow(File).to receive(:exists?).with('C:\\windows\\system32\\gem').and_return(false)
- allow(File).to receive(:exists?).with('C:\\windows\\gem').and_return(false)
- allow(File).to receive(:exists?).with('C:\\Ruby186\\bin\\gem').and_return(true)
- allow(File).to receive(:exists?).with('d:\\opscode\\chef\\bin\\gem').and_return(false) # should not get here
- allow(File).to receive(:exists?).with('d:\\opscode\\chef\\embedded\\bin\\gem').and_return(false) # should not get here
- expect(provider.gem_env.gem_binary_location).to eq('C:\Ruby186\bin\gem')
+ allow(ENV).to receive(:[]).with("PATH").and_return('C:\windows\system32;C:\windows;C:\Ruby186\bin')
+ allow(File).to receive(:executable?).with('C:\\windows\\system32/gem').and_return(false)
+ allow(File).to receive(:executable?).with('C:\\windows/gem').and_return(false)
+ allow(File).to receive(:executable?).with('C:\\Ruby186\\bin/gem').and_return(true)
+ allow(File).to receive(:executable?).with('d:\\opscode\\chef\\bin/gem').and_return(false) # should not get here
+ allow(File).to receive(:executable?).with('d:\\opscode\\chef\\bin/gem').and_return(false) # should not get here
+ allow(File).to receive(:executable?).with("d:/opscode/chef/embedded/bin/gem").and_return(false) # should not get here
+ expect(provider.gem_env.gem_binary_location).to eq('C:\Ruby186\bin/gem')
end
end
end
@@ -496,32 +524,94 @@ describe Chef::Provider::Package::Rubygems do
provider.load_current_resource
end
- it "does not query for available versions when the current version is the target version" do
- expect(provider.candidate_version).to be_nil
+ context "when the current version is the target version" do
+ it "does not query for available versions" do
+ # NOTE: odd use case -- we've equality pinned a version, but are calling :upgrade
+ expect(provider.gem_env).not_to receive(:candidate_version_from_remote)
+ expect(provider.gem_env).not_to receive(:install)
+ provider.run_action(:upgrade)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
end
- context "when the current version is not the target version" do
- let(:target_version) { "9000.0.2" }
+ context "when the current version satisfies the target version requirement" do
+ let(:target_version) { ">= 0" }
+
+ it "does not query for available versions on install" do
+ expect(provider.gem_env).not_to receive(:candidate_version_from_remote)
+ expect(provider.gem_env).not_to receive(:install)
+ provider.run_action(:install)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "queries for available versions on upgrade" do
+ expect(provider.gem_env).to receive(:candidate_version_from_remote)
+ .and_return(Gem::Version.new("9000.0.2"))
+ expect(provider.gem_env).to receive(:install)
+ provider.run_action(:upgrade)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+
+ context "when the source is from the rubygems_url" do
+ it "determines the candidate version by querying the remote gem servers" do
+ Chef::Config[:rubygems_url] = "https://mirror1/"
+ expect(provider.gem_env).to receive(:candidate_version_from_remote)
+ .with(gem_dep, "https://mirror1/")
+ .and_return(Gem::Version.new(target_version))
+ expect(provider.candidate_version).to eq(target_version)
+ end
+ end
+
+ context "when the requested source is a remote server" do
let(:source) { "http://mygems.example.com" }
it "determines the candidate version by querying the remote gem servers" do
- expect(provider.gem_env).to receive(:candidate_version_from_remote).
- with(gem_dep, source).
- and_return(Gem::Version.new(target_version))
+ expect(provider.gem_env).to receive(:candidate_version_from_remote)
+ .with(gem_dep, source, Chef::Config[:rubygems_url])
+ .and_return(Gem::Version.new(target_version))
+ expect(provider.candidate_version).to eq(target_version)
+ end
+
+ it "overwrites the config variable" do
+ new_resource.include_default_source false
+ Chef::Config[:rubygems_url] = "https://overridden"
+ expect(provider.gem_env).to receive(:candidate_version_from_remote)
+ .with(gem_dep, source)
+ .and_return(Gem::Version.new(target_version))
+ expect(provider.candidate_version).to eq(target_version)
+ end
+ end
+
+ context "when the requested source is an array" do
+ let(:source) { [ "https://mirror1", "https://mirror2" ] }
+
+ it "determines the candidate version by querying the remote gem servers" do
+ expect(provider.gem_env).to receive(:candidate_version_from_remote)
+ .with(gem_dep, *[source, Chef::Config[:rubygems_url] ].flatten)
+ .and_return(Gem::Version.new(target_version))
+ expect(provider.candidate_version).to eq(target_version)
+ end
+
+ it "overwrites the config variable" do
+ new_resource.include_default_source false
+ Chef::Config[:rubygems_url] = "https://overridden"
+ expect(provider.gem_env).to receive(:candidate_version_from_remote)
+ .with(gem_dep, *source)
+ .and_return(Gem::Version.new(target_version))
expect(provider.candidate_version).to eq(target_version)
end
end
context "when the requested source is a file" do
- let (:gem_name) { "chef-integration-test" }
- let (:source) { CHEF_SPEC_DATA + "/gems/chef-integration-test-0.1.0.gem" }
- let (:target_version) { ">= 0" }
+ let(:gem_name) { "chef-integration-test" }
+ let(:source) { CHEF_SPEC_DATA + "/gems/chef-integration-test-0.1.0.gem" }
+ let(:target_version) { ">= 0" }
it "parses the gem's specification" do
expect(provider.candidate_version).to eq("0.1.0")
end
end
-
end
describe "when installing a gem" do
@@ -534,18 +624,17 @@ describe Chef::Provider::Package::Rubygems do
current_resource
end
+ let(:version) { Gem::Version.new(candidate_version) }
+
before do
- version = Gem::Version.new(candidate_version)
- args = [gem_dep]
- args << source if source
- allow(provider.gem_env).to receive(:candidate_version_from_remote).
- with(*args).
- and_return(version)
+ expected_source = [ source ]
+ expected_source << Chef::Config[:rubygems_url] if include_default_source
+ allow(provider.gem_env).to receive(:candidate_version_from_remote).with(gem_dep, *expected_source.flatten.compact).and_return(version)
end
describe "in the current gem environment" do
it "installs the gem via the gems api when no explicit options are used" do
- expect(provider.gem_env).to receive(:install).with(gem_dep, :sources => nil)
+ expect(provider.gem_env).to receive(:install).with(gem_dep, sources: [ Chef::Config[:rubygems_url] ])
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -554,7 +643,7 @@ describe Chef::Provider::Package::Rubygems do
let(:source) { "http://gems.example.org" }
it "installs the gem via the gems api" do
- expect(provider.gem_env).to receive(:install).with(gem_dep, :sources => [source])
+ expect(provider.gem_env).to receive(:install).with(gem_dep, sources: [source, Chef::Config[:rubygems_url]])
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -562,10 +651,9 @@ describe Chef::Provider::Package::Rubygems do
context "when source is a path" do
let(:source) { CHEF_SPEC_DATA + "/gems/chef-integration-test-0.1.0.gem" }
- let(:domain) { { domain: :local } }
it "installs the gem from file via the gems api" do
- expect(provider.gem_env).to receive(:install).with(source, domain)
+ expect(provider.gem_env).to receive(:install).with(source)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -573,11 +661,10 @@ describe Chef::Provider::Package::Rubygems do
context "when the gem name is a file path and source is nil" do
let(:gem_name) { CHEF_SPEC_DATA + "/gems/chef-integration-test-0.1.0.gem" }
- let(:domain) { { domain: :local } }
it "installs the gem from file via the gems api" do
expect(new_resource.source).to eq(gem_name)
- expect(provider.gem_env).to receive(:install).with(gem_name, domain)
+ expect(provider.gem_env).to receive(:install).with(gem_name)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -585,9 +672,9 @@ describe Chef::Provider::Package::Rubygems do
# this catches 'gem_package "foo"' when "./foo" is a file in the cwd, and instead of installing './foo' it fetches the remote gem
it "installs the gem via the gems api, when the package has no file separator characters in it, but a matching file exists in cwd" do
- allow(::File).to receive(:exists?).and_return(true)
+ allow(::File).to receive(:exist?).and_return(true)
new_resource.package_name("rspec-core")
- expect(provider.gem_env).to receive(:install).with(gem_dep, :sources => nil)
+ expect(provider.gem_env).to receive(:install).with(gem_dep, sources: [ Chef::Config[:rubygems_url] ])
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -596,7 +683,20 @@ describe Chef::Provider::Package::Rubygems do
let(:options) { "-i /alt/install/location" }
it "installs the gem by shelling out when options are provided as a String" do
- expected = "gem install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" #{options}"
+ expected = "gem install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=https://www.rubygems.org #{options}"
+ expect(provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+
+ context "when the Chef::Config[:rubygems_url] option is provided" do
+ let(:gem_binary) { "/foo/bar" }
+
+ it "installs the gem with rubygems.org as an added source" do
+ Chef::Config[:rubygems_url] = "https://mirror1"
+ expect(provider.gem_env).to receive(:candidate_version_from_remote).with(gem_dep, Chef::Config[:rubygems_url]).and_return(version)
+ expected = "#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=https://mirror1"
expect(provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
@@ -608,11 +708,47 @@ describe Chef::Provider::Package::Rubygems do
let(:gem_binary) { "/foo/bar" }
it "installs the gem with rubygems.org as an added source" do
- expected = "#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=#{source} --source=https://rubygems.org"
+ expected = "#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=#{source} --source=https://www.rubygems.org"
expect(provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
+
+ context "with include_default_source false" do
+ let(:include_default_source) { false }
+
+ it "ignores the Chef::Config setting" do
+ Chef::Config[:rubygems_url] = "https://ignored"
+ expected = "#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=#{source}"
+ expect(provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+ end
+
+ context "when the source is an array" do
+ let(:source) { [ "https://mirror1" , "https://mirror2" ] }
+ let(:gem_binary) { "/foo/bar" }
+
+ it "installs the gem with an array as an added source" do
+ expected = "#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=https://mirror1 --source=https://mirror2 --source=https://www.rubygems.org"
+ expect(provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ context "with include_default_source false" do
+ let(:include_default_source) { false }
+
+ it "ignores the Chef::Config setting" do
+ Chef::Config[:rubygems_url] = "https://ignored"
+ expected = "#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=https://mirror1 --source=https://mirror2"
+ expect(provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
end
context "when we have cleared sources and an explict source is specified" do
@@ -621,7 +757,7 @@ describe Chef::Provider::Package::Rubygems do
it "installs the gem" do
new_resource.clear_sources(true)
- expected = "#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --clear-sources --source=#{source}"
+ expected = "#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --clear-sources --source=#{source} --source=https://www.rubygems.org"
expect(provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
@@ -633,7 +769,7 @@ describe Chef::Provider::Package::Rubygems do
let(:options) { "-i /alt/install/location" }
it "installs the gem by shelling out when options are provided but no version is given" do
- expected = "gem install rspec-core -q --no-rdoc --no-ri -v \"#{candidate_version}\" #{options}"
+ expected = "gem install rspec-core -q --no-rdoc --no-ri -v \"#{candidate_version}\" --source=https://www.rubygems.org #{options}"
expect(provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
@@ -641,10 +777,10 @@ describe Chef::Provider::Package::Rubygems do
end
context "when options are given as a Hash" do
- let(:options) { { :install_dir => "/alt/install/location" } }
+ let(:options) { { install_dir: "/alt/install/location" } }
it "installs the gem via the gems api when options are given as a Hash" do
- expect(provider.gem_env).to receive(:install).with(gem_dep, { :sources => nil }.merge(options))
+ expect(provider.gem_env).to receive(:install).with(gem_dep, { sources: [ Chef::Config[:rubygems_url] ] }.merge(options))
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -654,7 +790,7 @@ describe Chef::Provider::Package::Rubygems do
let(:target_version) { "9000.0.2" }
it "installs the gem via the gems api" do
- expect(provider.gem_env).to receive(:install).with(gem_dep, :sources => nil)
+ expect(provider.gem_env).to receive(:install).with(gem_dep, sources: [ Chef::Config[:rubygems_url] ] )
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -669,6 +805,11 @@ describe Chef::Provider::Package::Rubygems do
expect(provider.gem_env).not_to receive(:install)
provider.run_action(:install)
end
+
+ it "performs the upgrade" do
+ expect(provider.gem_env).to receive(:install)
+ provider.run_action(:upgrade)
+ end
end
context "if the fuzzy operator is used" do
@@ -679,6 +820,11 @@ describe Chef::Provider::Package::Rubygems do
expect(provider.gem_env).not_to receive(:install)
provider.run_action(:install)
end
+
+ it "it upgrades an existing gem" do
+ expect(provider.gem_env).to receive(:install)
+ provider.run_action(:upgrade)
+ end
end
end
end
@@ -687,7 +833,7 @@ describe Chef::Provider::Package::Rubygems do
let(:gem_binary) { "/usr/weird/bin/gem" }
it "installs the gem by shelling out to gem install" do
- expect(provider).to receive(:shell_out!).with("#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\"", env: nil, timeout: 900)
+ expect(provider).to receive(:shell_out!).with("#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=https://www.rubygems.org", env: nil, timeout: 900)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -695,10 +841,9 @@ describe Chef::Provider::Package::Rubygems do
context "when source is a path" do
let(:source) { CHEF_SPEC_DATA + "/gems/chef-integration-test-0.1.0.gem" }
let(:target_version) { ">= 0" }
- let(:domain) { " --local" }
it "installs the gem by shelling out to gem install" do
- expect(provider).to receive(:shell_out!).with("#{gem_binary} install #{source} -q --no-rdoc --no-ri -v \"#{target_version}\"#{domain}", env: nil, timeout: 900)
+ expect(provider).to receive(:shell_out!).with("#{gem_binary} install #{source} -q --no-rdoc --no-ri -v \"#{target_version}\"", env: nil, timeout: 900)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -707,11 +852,10 @@ describe Chef::Provider::Package::Rubygems do
context "when the package is a path and source is nil" do
let(:gem_name) { CHEF_SPEC_DATA + "/gems/chef-integration-test-0.1.0.gem" }
let(:target_version) { ">= 0" }
- let(:domain) { " --local" }
it "installs the gem from file by shelling out to gem install when the package is a path and the source is nil" do
expect(new_resource.source).to eq(gem_name)
- expect(provider).to receive(:shell_out!).with("#{gem_binary} install #{gem_name} -q --no-rdoc --no-ri -v \"#{target_version}\"#{domain}", env: nil, timeout: 900)
+ expect(provider).to receive(:shell_out!).with("#{gem_binary} install #{gem_name} -q --no-rdoc --no-ri -v \"#{target_version}\"", env: nil, timeout: 900)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -742,7 +886,7 @@ describe Chef::Provider::Package::Rubygems do
end
context "when options are given as a Hash" do
- let(:options) { { :install_dir => "/alt/install/location" } }
+ let(:options) { { install_dir: "/alt/install/location" } }
it "uninstalls via the api" do
# pre-reqs for action_remove to actually remove the package:
diff --git a/spec/unit/provider/package/solaris_spec.rb b/spec/unit/provider/package/solaris_spec.rb
index 9cc8deeb2a..2fba2e3a08 100644
--- a/spec/unit/provider/package/solaris_spec.rb
+++ b/spec/unit/provider/package/solaris_spec.rb
@@ -27,7 +27,7 @@ describe Chef::Provider::Package::Solaris do
@new_resource.source("/tmp/bash.pkg")
@provider = Chef::Provider::Package::Solaris.new(@new_resource, @run_context)
- allow(::File).to receive(:exists?).and_return(true)
+ allow(::File).to receive(:exist?).and_return(true)
end
describe "assessing the current package status" do
@@ -63,7 +63,7 @@ PKGINFO
it "should raise an exception if a source is supplied but not found" do
allow(@provider).to receive(:shell_out).and_return(@status)
- allow(::File).to receive(:exists?).and_return(false)
+ allow(::File).to receive(:exist?).and_return(false)
@provider.load_current_resource
@provider.define_resource_requirements
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Package)
@@ -71,8 +71,8 @@ PKGINFO
it "should get the source package version from pkginfo if provided" do
status = double(:stdout => @pkginfo, :exitstatus => 0)
- expect(@provider).to receive(:shell_out).with("pkginfo -l -d /tmp/bash.pkg SUNWbash", { timeout: 900 }).and_return(status)
- expect(@provider).to receive(:shell_out).with("pkginfo -l SUNWbash", { timeout: 900 }).and_return(@status)
+ expect(@provider).to receive(:shell_out).with("pkginfo", "-l", "-d", "/tmp/bash.pkg", "SUNWbash", { timeout: 900 }).and_return(status)
+ expect(@provider).to receive(:shell_out).with("pkginfo", "-l", "SUNWbash", { timeout: 900 }).and_return(@status)
@provider.load_current_resource
expect(@provider.current_resource.package_name).to eq("SUNWbash")
@@ -81,8 +81,8 @@ PKGINFO
it "should return the current version installed if found by pkginfo" do
status = double(:stdout => @pkginfo, :exitstatus => 0)
- expect(@provider).to receive(:shell_out).with("pkginfo -l -d /tmp/bash.pkg SUNWbash", { timeout: 900 }).and_return(@status)
- expect(@provider).to receive(:shell_out).with("pkginfo -l SUNWbash", { timeout: 900 }).and_return(status)
+ expect(@provider).to receive(:shell_out).with("pkginfo", "-l", "-d", "/tmp/bash.pkg", "SUNWbash", { timeout: 900 }).and_return(@status)
+ expect(@provider).to receive(:shell_out).with("pkginfo", "-l", "SUNWbash", { timeout: 900 }).and_return(status)
@provider.load_current_resource
expect(@provider.current_resource.version).to eq("11.10.0,REV=2005.01.08.05.16")
end
@@ -101,8 +101,8 @@ PKGINFO
end
it "should return a current resource with a nil version if the package is not found" do
- expect(@provider).to receive(:shell_out).with("pkginfo -l -d /tmp/bash.pkg SUNWbash", { timeout: 900 }).and_return(@status)
- expect(@provider).to receive(:shell_out).with("pkginfo -l SUNWbash", { timeout: 900 }).and_return(@status)
+ expect(@provider).to receive(:shell_out).with("pkginfo", "-l", "-d", "/tmp/bash.pkg", "SUNWbash", { timeout: 900 }).and_return(@status)
+ expect(@provider).to receive(:shell_out).with("pkginfo", "-l", "SUNWbash", { timeout: 900 }).and_return(@status)
@provider.load_current_resource
expect(@provider.current_resource.version).to be_nil
end
@@ -132,7 +132,7 @@ PKGINFO
describe "install and upgrade" do
it "should run pkgadd -n -d with the package source to install" do
- expect(@provider).to receive(:shell_out!).with("pkgadd -n -d /tmp/bash.pkg all", { timeout: 900 })
+ expect(@provider).to receive(:shell_out!).with("pkgadd", "-n", "-d", "/tmp/bash.pkg", "all", { timeout: 900 })
@provider.install_package("SUNWbash", "11.10.0,REV=2005.01.08.05.16")
end
@@ -140,26 +140,26 @@ PKGINFO
@new_resource = Chef::Resource::Package.new("/tmp/bash.pkg")
@provider = Chef::Provider::Package::Solaris.new(@new_resource, @run_context)
expect(@new_resource.source).to eq("/tmp/bash.pkg")
- expect(@provider).to receive(:shell_out!).with("pkgadd -n -d /tmp/bash.pkg all", { timeout: 900 })
+ expect(@provider).to receive(:shell_out!).with("pkgadd", "-n", "-d", "/tmp/bash.pkg", "all", { timeout: 900 })
@provider.install_package("/tmp/bash.pkg", "11.10.0,REV=2005.01.08.05.16")
end
it "should run pkgadd -n -a /tmp/myadmin -d with the package options -a /tmp/myadmin" do
- allow(@new_resource).to receive(:options).and_return("-a /tmp/myadmin")
- expect(@provider).to receive(:shell_out!).with("pkgadd -n -a /tmp/myadmin -d /tmp/bash.pkg all", { timeout: 900 })
+ @new_resource.options "-a /tmp/myadmin"
+ expect(@provider).to receive(:shell_out!).with("pkgadd", "-n", "-a", "/tmp/myadmin", "-d", "/tmp/bash.pkg", "all", { timeout: 900 })
@provider.install_package("SUNWbash", "11.10.0,REV=2005.01.08.05.16")
end
end
describe "remove" do
it "should run pkgrm -n to remove the package" do
- expect(@provider).to receive(:shell_out!).with("pkgrm -n SUNWbash", { timeout: 900 })
+ expect(@provider).to receive(:shell_out!).with("pkgrm", "-n", "SUNWbash", { timeout: 900 })
@provider.remove_package("SUNWbash", "11.10.0,REV=2005.01.08.05.16")
end
it "should run pkgrm -n -a /tmp/myadmin with options -a /tmp/myadmin" do
- allow(@new_resource).to receive(:options).and_return("-a /tmp/myadmin")
- expect(@provider).to receive(:shell_out!).with("pkgrm -n -a /tmp/myadmin SUNWbash", { timeout: 900 })
+ @new_resource.options "-a /tmp/myadmin"
+ expect(@provider).to receive(:shell_out!).with("pkgrm", "-n", "-a", "/tmp/myadmin", "SUNWbash", { timeout: 900 })
@provider.remove_package("SUNWbash", "11.10.0,REV=2005.01.08.05.16")
end
diff --git a/spec/unit/provider/package/windows/exe_spec.rb b/spec/unit/provider/package/windows/exe_spec.rb
index 23a54601c2..f18cbf3dca 100644
--- a/spec/unit/provider/package/windows/exe_spec.rb
+++ b/spec/unit/provider/package/windows/exe_spec.rb
@@ -110,23 +110,37 @@ describe Chef::Provider::Package::Windows::Exe do
end
describe "remove_package" do
- context "no version given and one package installed" do
- it "removes installed package" do
- expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir\" uninst_file \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ before do
+ allow(::File).to receive(:exist?).and_return(false)
+ end
+
+ context "no version given and one package installed with unquoted uninstall string" do
+ it "removes installed package and quotes uninstall string" do
+ allow(::File).to receive(:exist?).with("uninst_dir/uninst_file").and_return(true)
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"uninst_dir\/uninst_file\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ provider.remove_package
+ end
+ end
+
+ context "When timeout value is passed" do
+ it "removes installed package and quotes uninstall string" do
+ new_resource.timeout = 300
+ allow(::File).to receive(:exist?).with("uninst_dir/uninst_file").and_return(true)
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"uninst_dir\/uninst_file\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, :timeout => 300, :returns => [0])
provider.remove_package
end
end
- context "several packages installed" do
+ context "several packages installed with quoted uninstall strings" do
let(:uninstall_hash) do
[
{
"DisplayVersion" => "v1",
- "UninstallString" => File.join("uninst_dir1", "uninst_file1"),
+ "UninstallString" => "\"#{File.join("uninst_dir1", "uninst_file1")}\"",
},
{
"DisplayVersion" => "v2",
- "UninstallString" => File.join("uninst_dir2", "uninst_file2"),
+ "UninstallString" => "\"#{File.join("uninst_dir2", "uninst_file2")}\"",
},
]
end
@@ -134,15 +148,15 @@ describe Chef::Provider::Package::Windows::Exe do
context "version given and installed" do
it "removes given version" do
new_resource.version("v2")
- expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir2\" uninst_file2 \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"uninst_dir2\/uninst_file2\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
provider.remove_package
end
end
context "no version given" do
it "removes both versions" do
- expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir1\" uninst_file1 \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
- expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir2\" uninst_file2 \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"uninst_dir1\/uninst_file1\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"uninst_dir2\/uninst_file2\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
provider.remove_package
end
end
diff --git a/spec/unit/provider/package/windows/msi_spec.rb b/spec/unit/provider/package/windows/msi_spec.rb
index e29508ca7b..aa528ab90e 100644
--- a/spec/unit/provider/package/windows/msi_spec.rb
+++ b/spec/unit/provider/package/windows/msi_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -121,7 +121,7 @@ describe Chef::Provider::Package::Windows::MSI do
end
it "removes installed package" do
- expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid} \/Q/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(/msiexec \/x {guid} \/q/, kind_of(Hash))
provider.remove_package
end
@@ -140,8 +140,8 @@ describe Chef::Provider::Package::Windows::MSI do
end
it "removes both installed package" do
- expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid} \/Q/, kind_of(Hash))
- expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid2} \/Q/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(/msiexec \/x {guid} \/q/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(/msiexec \/x {guid2} \/q/, kind_of(Hash))
provider.remove_package
end
end
@@ -150,7 +150,16 @@ describe Chef::Provider::Package::Windows::MSI do
before { new_resource.options("/Q") }
it "does not duplicate quiet switch" do
- expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid} \/Q/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(/msiexec \/x {guid} \/Q/, kind_of(Hash))
+ provider.remove_package
+ end
+ end
+
+ context "custom options includes /qn" do
+ before { new_resource.options("/qn") }
+
+ it "does not duplicate quiet switch" do
+ expect(provider).to receive(:shell_out!).with(/msiexec \/x {guid} \/qn/, kind_of(Hash))
provider.remove_package
end
end
diff --git a/spec/unit/provider/package/windows/registry_uninstall_entry_spec.rb b/spec/unit/provider/package/windows/registry_uninstall_entry_spec.rb
new file mode 100644
index 0000000000..803f3271df
--- /dev/null
+++ b/spec/unit/provider/package/windows/registry_uninstall_entry_spec.rb
@@ -0,0 +1,78 @@
+require "spec_helper"
+require "chef/provider/package/windows/registry_uninstall_entry"
+
+describe Chef::Provider::Package::Windows::RegistryUninstallEntry do
+ let(:hkey) { :hkey } # mock all the methods
+ let(:key) { :key }
+ let(:entry) { { "UninstallString" => "UninstallStringPath", "QuietUninstallString" => "QuietUninstallStringPath" } }
+
+ describe "when QuietUninstallString key not present" do
+ let(:quiet_uninstall_string) { nil }
+ let (:quiet_uninstall_string_key) { Chef::Provider::Package::Windows::RegistryUninstallEntry.quiet_uninstall_string_key?(quiet_uninstall_string, hkey, key, entry).uninstall_string }
+ it "returns UninstallString key value" do
+ expect(quiet_uninstall_string_key).to eql "UninstallStringPath"
+ end
+ end
+
+ describe "when QuietUninstallString key present" do
+ let(:quiet_uninstall_string) { "QuietUninstallString" }
+ let (:quiet_uninstall_string_key) { Chef::Provider::Package::Windows::RegistryUninstallEntry.quiet_uninstall_string_key?(quiet_uninstall_string, hkey, key, entry).uninstall_string }
+ it "returns QuietUninstallString key value" do
+ expect(quiet_uninstall_string_key).to eql "QuietUninstallStringPath"
+ end
+ end
+
+ describe ".find_entries", :windows_only do
+ let (:registry_uninstall_entry) { Chef::Provider::Package::Windows::RegistryUninstallEntry }
+ before(:each) do
+ allow_any_instance_of(::Win32::Registry).to receive(:open).and_return("::Win32::Registry::HKEY_CURRENT_USER")
+ end
+
+ context "when passing nil" do
+ let(:package_name) { nil }
+ it "returns empty entries array" do
+ allow(Chef::Provider::Package::Windows::RegistryUninstallEntry).to receive(:read_registry_property).and_return(nil)
+ entries = Chef::Provider::Package::Windows::RegistryUninstallEntry.find_entries(package_name)
+ expect(entries.size).to eql 0
+ end
+ end
+
+ context "when passing empty string" do
+ let(:package_name) { " " }
+ it "returns no entries" do
+ allow(Chef::Provider::Package::Windows::RegistryUninstallEntry).to receive(:read_registry_property).and_return(nil)
+ entries = Chef::Provider::Package::Windows::RegistryUninstallEntry.find_entries(package_name)
+ expect(entries.size).to eql 0
+ end
+ end
+
+ context "when package is not found" do
+ let(:package_name) { "hive" }
+ it "returns no entries" do
+ allow(Chef::Provider::Package::Windows::RegistryUninstallEntry).to receive(:read_registry_property).and_return("Chef Client")
+ entries = Chef::Provider::Package::Windows::RegistryUninstallEntry.find_entries(package_name)
+ expect(entries).to eql []
+ end
+ end
+
+ context "when trailing spaces are given in display name" do
+ let(:package_name) { "Chef" }
+ let(:display_name_with_space) { "Chef " }
+ it "removes the trailing spaces" do
+ allow(Chef::Provider::Package::Windows::RegistryUninstallEntry).to receive(:read_registry_property).and_return(display_name_with_space)
+ entries = registry_uninstall_entry.find_entries(package_name).first
+ expect(entries.display_name.rstrip).to eql package_name
+ end
+ end
+
+ context "When package found successfully" do
+ let(:package_name) { "Chef Client" }
+ let(:display_name) { "Chef Client" }
+ it "returns 'Chef Client' entries" do
+ allow(Chef::Provider::Package::Windows::RegistryUninstallEntry).to receive(:read_registry_property).and_return(display_name)
+ entries = registry_uninstall_entry.find_entries(package_name).first
+ expect(entries.display_name.rstrip).to eql package_name
+ end
+ end
+ end
+end
diff --git a/spec/unit/provider/package/windows_spec.rb b/spec/unit/provider/package/windows_spec.rb
index e26662ac75..d07e68cb5b 100644
--- a/spec/unit/provider/package/windows_spec.rb
+++ b/spec/unit/provider/package/windows_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,9 +26,9 @@ describe Chef::Provider::Package::Windows, :windows_only do
allow(Chef::FileCache).to receive(:create_cache_path).with("package/").and_return(cache_path)
end
- let(:node) { double("Chef::Node") }
- let(:events) { double("Chef::Events").as_null_object } # mock all the methods
- let(:run_context) { double("Chef::RunContext", :node => node, :events => events) }
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
let(:resource_source) { "calculator.msi" }
let(:resource_name) { "calculator" }
let(:installer_type) { nil }
@@ -224,7 +224,7 @@ describe Chef::Provider::Package::Windows, :windows_only do
end
end
- context "eninstall entries is empty" do
+ context "uninstall entries is empty" do
before { allow(Chef::Provider::Package::Windows::RegistryUninstallEntry).to receive(:find_entries).and_return([]) }
it "returns nil" do
@@ -312,6 +312,7 @@ describe Chef::Provider::Package::Windows, :windows_only do
let(:resource_source) { "https://foo.bar/calculator.exe" }
it "downloads the http resource" do
+ allow(File).to receive(:exist?).with('c:\cache\calculator.exe').and_return(false)
expect(provider).to receive(:download_source_file)
provider.run_action(:install)
end
@@ -394,4 +395,49 @@ describe Chef::Provider::Package::Windows, :windows_only do
end
end
end
+
+ shared_context "valid checksum" do
+ context "checksum is valid" do
+ before do
+ allow(provider).to receive(:checksum).and_return("jiie00u3bbs92vsbhvgvklb2lasgh20ah")
+ end
+
+ it "does not raise the checksum mismatch exception" do
+ expect { provider.send(:validate_content!) }.to_not raise_error
+ end
+ end
+ end
+
+ shared_context "invalid checksum" do
+ context "checksum is invalid" do
+ before do
+ allow(provider).to receive(:checksum).and_return("kiie30u3bbs92vsbhvgvklb2lasgh20ah")
+ end
+
+ it "raises the checksum mismatch exception" do
+ expect { provider.send(:validate_content!) }.to raise_error(
+ Chef::Exceptions::ChecksumMismatch)
+ end
+ end
+ end
+
+ describe "validate_content!" do
+ before(:each) do
+ new_resource.checksum("jiie00u3bbs92vsbhvgvklb2lasgh20ah")
+ end
+
+ context "checksum is in lowercase" do
+ include_context "valid checksum"
+ include_context "invalid checksum"
+ end
+
+ context "checksum is in uppercase" do
+ before do
+ new_resource.checksum = new_resource.checksum.upcase
+ end
+
+ include_context "valid checksum"
+ include_context "invalid checksum"
+ end
+ end
end
diff --git a/spec/unit/provider/package/yum/python_helper_spec.rb b/spec/unit/provider/package/yum/python_helper_spec.rb
new file mode 100644
index 0000000000..ba91e2f24a
--- /dev/null
+++ b/spec/unit/provider/package/yum/python_helper_spec.rb
@@ -0,0 +1,29 @@
+#
+# Copyright:: Copyright 2017-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+# NOTE: most of the tests of this functionality are baked into the func tests for the yum package provider
+
+describe Chef::Provider::Package::Yum::PythonHelper do
+ let(:helper) { Chef::Provider::Package::Yum::PythonHelper.instance }
+
+ it "propagates stacktraces on stderr from the forked subprocess", :rhel do
+ allow(helper).to receive(:yum_command).and_return("ruby -e 'raise \"your hands in the air\"'")
+ expect { helper.package_query(:whatprovides, "tcpdump") }.to raise_error(/your hands in the air/)
+ end
+end
diff --git a/spec/unit/provider/package/yum/yum_cache_spec.rb b/spec/unit/provider/package/yum/yum_cache_spec.rb
new file mode 100644
index 0000000000..6b2a617ac8
--- /dev/null
+++ b/spec/unit/provider/package/yum/yum_cache_spec.rb
@@ -0,0 +1,109 @@
+#
+# Copyright:: Copyright 2018-2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Provider::Package::Yum::YumCache do
+ let(:yum_cache) { Chef::Provider::Package::Yum::YumCache.instance }
+
+ let(:python_helper) { instance_double(Chef::Provider::Package::Yum::PythonHelper) }
+
+ def yum_version(name, version, arch)
+ Chef::Provider::Package::Yum::Version.new(name, version, arch)
+ end
+
+ before(:each) do
+ allow( yum_cache ).to receive(:python_helper).and_return(python_helper)
+ end
+
+ it "package_available? returns false if the helper reports the available version is nil" do
+ expect( python_helper ).to receive(:package_query).with(:whatavailable, "foo", arch: nil).and_return( yum_version("foo", nil, nil) )
+ expect( yum_cache.package_available?("foo") ).to be false
+ end
+
+ it "package_available? returns true if the helper returns an available version" do
+ expect( python_helper ).to receive(:package_query).with(:whatavailable, "foo", arch: nil).and_return( yum_version("foo", "1.2.3-1", "x86_64") )
+ expect( yum_cache.package_available?("foo") ).to be true
+ end
+
+ it "version_available? returns false if the helper reports the available version is nil" do
+ expect( python_helper ).to receive(:package_query).with(:whatavailable, "foo", version: "1.2.3", arch: nil).and_return( yum_version("foo", nil, nil) )
+ expect( yum_cache.version_available?("foo", "1.2.3") ).to be false
+ end
+
+ it "version_available? returns true if the helper returns an available version" do
+ expect( python_helper ).to receive(:package_query).with(:whatavailable, "foo", version: "1.2.3", arch: nil).and_return( yum_version("foo", "1.2.3-1", "x86_64") )
+ expect( yum_cache.version_available?("foo", "1.2.3") ).to be true
+ end
+
+ it "version_available? with an arch returns false if the helper reports the available version is nil" do
+ expect( python_helper ).to receive(:package_query).with(:whatavailable, "foo", version: "1.2.3", arch: "x86_64").and_return( yum_version("foo", nil, nil) )
+ expect( yum_cache.version_available?("foo", "1.2.3", "x86_64") ).to be false
+ end
+
+ it "version_available? with an arch returns true if the helper returns an available version" do
+ expect( python_helper ).to receive(:package_query).with(:whatavailable, "foo", version: "1.2.3", arch: "x86_64").and_return( yum_version("foo", "1.2.3-1", "x86_64") )
+ expect( yum_cache.version_available?("foo", "1.2.3", "x86_64") ).to be true
+ end
+
+ [ :refresh, :reload, :reload_installed, :reload_provides, :reset, :reset_installed ].each do |method|
+ it "restarts the python helper when #{method} is called" do
+ expect( python_helper ).to receive(:restart)
+ yum_cache.send(method)
+ end
+ end
+
+ it "installed_version? returns nil if the helper reports the installed version is nil" do
+ expect( python_helper ).to receive(:package_query).with(:whatinstalled, "foo", arch: nil).and_return( yum_version("foo", nil, nil) )
+ expect( yum_cache.installed_version("foo") ).to be nil
+ end
+
+ it "installed_version? returns version string if the helper returns an installed version" do
+ expect( python_helper ).to receive(:package_query).with(:whatinstalled, "foo", arch: nil).and_return( yum_version("foo", "1.2.3-1", "x86_64") )
+ expect( yum_cache.installed_version("foo") ).to eql("1.2.3-1.x86_64")
+ end
+
+ it "installed_version? returns nil if the helper reports the installed version is nil" do
+ expect( python_helper ).to receive(:package_query).with(:whatinstalled, "foo", arch: "x86_64").and_return( yum_version("foo", nil, nil) )
+ expect( yum_cache.installed_version("foo", "x86_64") ).to be nil
+ end
+
+ it "installed_version? returns version string if the helper returns an installed version" do
+ expect( python_helper ).to receive(:package_query).with(:whatinstalled, "foo", arch: "x86_64").and_return( yum_version("foo", "1.2.3-1", "x86_64") )
+ expect( yum_cache.installed_version("foo", "x86_64") ).to eql("1.2.3-1.x86_64")
+ end
+
+ it "available_version? returns nil if the helper reports the available version is nil" do
+ expect( python_helper ).to receive(:package_query).with(:whatavailable, "foo", arch: nil).and_return( yum_version("foo", nil, nil) )
+ expect( yum_cache.available_version("foo") ).to be nil
+ end
+
+ it "available_version? returns version string if the helper returns an available version" do
+ expect( python_helper ).to receive(:package_query).with(:whatavailable, "foo", arch: nil).and_return( yum_version("foo", "1.2.3-1", "x86_64") )
+ expect( yum_cache.available_version("foo") ).to eql("1.2.3-1.x86_64")
+ end
+
+ it "available_version? returns nil if the helper reports the available version is nil" do
+ expect( python_helper ).to receive(:package_query).with(:whatavailable, "foo", arch: "x86_64").and_return( yum_version("foo", nil, nil) )
+ expect( yum_cache.available_version("foo", "x86_64") ).to be nil
+ end
+
+ it "available_version? returns version string if the helper returns an available version" do
+ expect( python_helper ).to receive(:package_query).with(:whatavailable, "foo", arch: "x86_64").and_return( yum_version("foo", "1.2.3-1", "x86_64") )
+ expect( yum_cache.available_version("foo", "x86_64") ).to eql("1.2.3-1.x86_64")
+ end
+end
diff --git a/spec/unit/provider/package/yum_spec.rb b/spec/unit/provider/package/yum_spec.rb
deleted file mode 100644
index ab726134e9..0000000000
--- a/spec/unit/provider/package/yum_spec.rb
+++ /dev/null
@@ -1,2267 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-require "securerandom"
-
-describe Chef::Provider::Package::Yum do
- before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::YumPackage.new("cups")
- @status = double("Status", :exitstatus => 0)
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache",
- :reload_installed => true,
- :reset => true,
- :installed_version => "1.2.4-11.18.el5",
- :candidate_version => "1.2.4-11.18.el5_2.3",
- :package_available? => true,
- :version_available? => true,
- :allow_multi_install => [ "kernel" ],
- :package_repository => "base",
- :disable_extra_repo_control => true
- )
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @pid = double("PID")
- end
-
- describe "when loading the current system state" do
- it "should create a current resource with the name of the new_resource" do
- @provider.load_current_resource
- expect(@provider.current_resource.name).to eq("cups")
- end
-
- it "should set the current resources package name to the new resources package name" do
- @provider.load_current_resource
- expect(@provider.current_resource.package_name).to eq("cups")
- end
-
- it "should set the installed version to nil on the current resource if no installed package" do
- allow(@yum_cache).to receive(:installed_version).and_return(nil)
- @provider.load_current_resource
- expect(@provider.current_resource.version).to be_nil
- end
-
- it "should set the installed version if yum has one" do
- @provider.load_current_resource
- expect(@provider.current_resource.version).to eq("1.2.4-11.18.el5")
- end
-
- it "should set the candidate version if yum info has one" do
- @provider.load_current_resource
- expect(@provider.candidate_version).to eql("1.2.4-11.18.el5_2.3")
- end
-
- it "should return the current resouce" do
- expect(@provider.load_current_resource).to eql(@provider.current_resource)
- end
-
- describe "when source is provided" do
- it "should set the candidate version" do
- @new_resource = Chef::Resource::YumPackage.new("testing.source")
- @new_resource.source "chef-server-core-12.0.5-1.rpm"
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- allow(File).to receive(:exists?).with(@new_resource.source).and_return(true)
- allow(@yum_cache).to receive(:installed_version).and_return(nil)
- shellout_double = double(:stdout => "chef-server-core 12.0.5-1")
- allow(@provider).to receive(:shell_out!).and_return(shellout_double)
- @provider.load_current_resource
- expect(@provider.candidate_version).to eql("12.0.5-1")
- end
- end
-
- describe "yum_binary accessor" do
- it "when yum-deprecated exists" do
- expect(File).to receive(:exist?).with("/usr/bin/yum-deprecated").and_return(true)
- expect(@yum_cache).to receive(:yum_binary=).with("yum-deprecated")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- expect(@provider.yum_binary).to eql("yum-deprecated")
- end
-
- it "when yum-deprecated does not exist" do
- expect(File).to receive(:exist?).with("/usr/bin/yum-deprecated").and_return(false)
- expect(@yum_cache).to receive(:yum_binary=).with("yum")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- expect(@provider.yum_binary).to eql("yum")
- end
-
- it "when the yum_binary is set on the resource" do
- @new_resource.yum_binary "/usr/bin/yum-something"
- expect(File).not_to receive(:exist?)
- expect(@yum_cache).to receive(:yum_binary=).with("/usr/bin/yum-something")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- expect(@provider.yum_binary).to eql("/usr/bin/yum-something")
- end
-
- it "when the new_resource is a vanilla package class and yum-deprecated exists" do
- @new_resource = Chef::Resource::Package.new("cups")
- expect(File).to receive(:exist?).with("/usr/bin/yum-deprecated").and_return(true)
- expect(@yum_cache).to receive(:yum_binary=).with("yum-deprecated")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- expect(@provider.yum_binary).to eql("yum-deprecated")
- end
-
- it "when the new_resource is a vanilla package class and yum-deprecated does not exist" do
- @new_resource = Chef::Resource::Package.new("cups")
- expect(File).to receive(:exist?).with("/usr/bin/yum-deprecated").and_return(false)
- expect(@yum_cache).to receive(:yum_binary=).with("yum")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- expect(@provider.yum_binary).to eql("yum")
- end
- end
-
- describe "when arch in package_name" do
- it "should set the arch if no existing package_name is found and new_package_name+new_arch is available" do
- @new_resource = Chef::Resource::YumPackage.new("testing.noarch")
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache"
- )
- allow(@yum_cache).to receive(:installed_version) do |package_name, arch|
- # nothing installed for package_name/new_package_name
- nil
- end
- allow(@yum_cache).to receive(:candidate_version) do |package_name, arch|
- if package_name == "testing.noarch" || package_name == "testing.more.noarch"
- nil
- # candidate for new_package_name
- elsif package_name == "testing" || package_name == "testing.more"
- "1.1"
- end
- end
- allow(@yum_cache).to receive(:package_available?).and_return(true)
- allow(@yum_cache).to receive(:disable_extra_repo_control).and_return(true)
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@provider.new_resource.package_name).to eq("testing")
- expect(@provider.new_resource.arch).to eq("noarch")
- expect(@provider.arch).to eq("noarch")
-
- @new_resource = Chef::Resource::YumPackage.new("testing.more.noarch")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@provider.new_resource.package_name).to eq("testing.more")
- expect(@provider.new_resource.arch).to eq("noarch")
- expect(@provider.arch).to eq("noarch")
- end
-
- describe "when version constraint in package_name" do
- it "should set package_version if no existing package_name is found and new_package_name is available" do
- @new_resource = Chef::Resource::Package.new("cups = 1.2.4-11.18.el5_2.3")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- allow(@yum_cache).to receive(:package_available?) { |pkg| pkg == "cups" ? true : false }
- allow(@yum_cache).to receive(:packages_from_require) do |pkg|
- [Chef::Provider::Package::Yum::RPMDbPackage.new("cups", "1.2.4-11.18.el5_2.3", "noarch", [], false, true, "base"),
- Chef::Provider::Package::Yum::RPMDbPackage.new("cups", "1.2.4-11.18.el5_2.2", "noarch", [], false, true, "base")]
- end
- expect(Chef::Log).to receive(:debug).exactly(1).times.with(%r{checking yum info})
- expect(Chef::Log).to receive(:debug).exactly(1).times.with(%r{installed version})
- expect(Chef::Log).to receive(:debug).exactly(1).times.with(%r{matched 2 packages,})
- @provider.load_current_resource
- expect(@provider.new_resource.package_name).to eq("cups")
- expect(@provider.new_resource.version).to eq("1.2.4-11.18.el5_2.3")
- expect(@provider.send(:new_version_array)).to eq(["1.2.4-11.18.el5_2.3"])
- expect(@provider.send(:package_name_array)).to eq(["cups"])
- end
- end
-
- it "should not set the arch when an existing package_name is found" do
- @new_resource = Chef::Resource::YumPackage.new("testing.beta3")
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache"
- )
- allow(@yum_cache).to receive(:installed_version) do |package_name, arch|
- # installed for package_name
- if package_name == "testing.beta3" || package_name == "testing.beta3.more"
- "1.1"
- elsif package_name == "testing" || package_name == "testing.beta3"
- nil
- end
- end
- allow(@yum_cache).to receive(:candidate_version) do |package_name, arch|
- # no candidate for package_name/new_package_name
- nil
- end
- allow(@yum_cache).to receive(:package_available?).and_return(true)
- allow(@yum_cache).to receive(:disable_extra_repo_control).and_return(true)
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- # annoying side effect of the fun stub'ing above
- @provider.load_current_resource
- expect(@provider.new_resource.package_name).to eq("testing.beta3")
- expect(@provider.new_resource.arch).to eq(nil)
- expect(@provider.arch).to eq(nil)
-
- @new_resource = Chef::Resource::YumPackage.new("testing.beta3.more")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@provider.new_resource.package_name).to eq("testing.beta3.more")
- expect(@provider.new_resource.arch).to eq(nil)
- expect(@provider.arch).to eq(nil)
- end
-
- it "should not set the arch when no existing package_name or new_package_name+new_arch is found" do
- @new_resource = Chef::Resource::YumPackage.new("testing.beta3")
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache"
- )
- allow(@yum_cache).to receive(:installed_version) do |package_name, arch|
- # nothing installed for package_name/new_package_name
- nil
- end
- allow(@yum_cache).to receive(:candidate_version) do |package_name, arch|
- # no candidate for package_name/new_package_name
- nil
- end
- allow(@yum_cache).to receive(:package_available?).and_return(true)
- allow(@yum_cache).to receive(:disable_extra_repo_control).and_return(true)
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@provider.new_resource.package_name).to eq("testing.beta3")
- expect(@provider.new_resource.arch).to eq(nil)
- expect(@provider.arch).to eq(nil)
-
- @new_resource = Chef::Resource::YumPackage.new("testing.beta3.more")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@provider.new_resource.package_name).to eq("testing.beta3.more")
- expect(@provider.new_resource.arch).to eq(nil)
- expect(@provider.arch).to eq(nil)
- end
-
- it "should ensure it doesn't clobber an existing arch if passed" do
- @new_resource = Chef::Resource::YumPackage.new("testing.i386")
- @new_resource.arch("x86_64")
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache"
- )
- allow(@yum_cache).to receive(:installed_version) do |package_name, arch|
- # nothing installed for package_name/new_package_name
- nil
- end
- allow(@yum_cache).to receive(:candidate_version) do |package_name, arch|
- if package_name == "testing.noarch"
- nil
- # candidate for new_package_name
- elsif package_name == "testing"
- "1.1"
- end
- end.and_return("something")
- allow(@yum_cache).to receive(:package_available?).and_return(true)
- allow(@yum_cache).to receive(:disable_extra_repo_control).and_return(true)
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@provider.new_resource.package_name).to eq("testing.i386")
- expect(@provider.new_resource.arch).to eq("x86_64")
- end
- end
-
- it "should flush the cache if :before is true" do
- allow(@new_resource).to receive(:flush_cache).and_return({ :after => false, :before => true })
- expect(@yum_cache).to receive(:reload).once
- @provider.load_current_resource
- end
-
- it "should flush the cache if :before is false" do
- allow(@new_resource).to receive(:flush_cache).and_return({ :after => false, :before => false })
- expect(@yum_cache).not_to receive(:reload)
- @provider.load_current_resource
- end
-
- it "should detect --enablerepo or --disablerepo when passed among options, collect them preserving order and notify the yum cache" do
- allow(@new_resource).to receive(:options).and_return("--stuff --enablerepo=foo --otherthings --disablerepo=a,b,c --enablerepo=bar")
- expect(@yum_cache).to receive(:enable_extra_repo_control).with("--enablerepo=foo --disablerepo=a,b,c --enablerepo=bar")
- @provider.load_current_resource
- end
-
- it "should let the yum cache know extra repos are disabled if --enablerepo or --disablerepo aren't among options" do
- allow(@new_resource).to receive(:options).and_return("--stuff --otherthings")
- expect(@yum_cache).to receive(:disable_extra_repo_control)
- @provider.load_current_resource
- end
-
- it "should let the yum cache know extra repos are disabled if options aren't set" do
- allow(@new_resource).to receive(:options).and_return(nil)
- expect(@yum_cache).to receive(:disable_extra_repo_control)
- @provider.load_current_resource
- end
-
- context "when the package name isn't found" do
- let(:yum_cache) { double(
- "Chef::Provider::Yum::YumCache",
- :reload_installed => true,
- :reset => true,
- :installed_version => "1.0.1.el5",
- :candidate_version => "2.0.1.el5",
- :package_available? => false,
- :version_available? => true,
- :disable_extra_repo_control => true
- )
- }
-
- before do
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(yum_cache)
- allow(yum_cache).to receive(:yum_binary=).with("yum")
- @pkg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "2.0.1.el5", "x86_64", [])
- expect(yum_cache).to receive(:packages_from_require).and_return([@pkg])
- end
-
- it "should search provides then set package_name to match" do
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@new_resource.package_name).to eq("test-package")
- expect(@new_resource.version).to eq(nil)
- end
-
- it "should search provides then set version to match if a requirement was passed in the package name" do
- @new_resource = Chef::Resource::YumPackage.new("test-package = 2.0.1.el5")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@new_resource.package_name).to eq("test-package")
- expect(@new_resource.version).to eq("2.0.1.el5")
- end
-
- it "should search provides then set version to match if a requirement was passed in the version" do
- @new_resource = Chef::Resource::YumPackage.new("test-package")
- @new_resource.version("= 2.0.1.el5")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@new_resource.package_name).to eq("test-package")
- expect(@new_resource.version).to eq("2.0.1.el5")
- end
-
- it "should search provides and not set the version to match if a specific version was requested" do
- @new_resource = Chef::Resource::YumPackage.new("test-package")
- @new_resource.version("3.0.1.el5")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@new_resource.package_name).to eq("test-package")
- expect(@new_resource.version).to eq("3.0.1.el5")
- end
-
- it "should search provides then set versions to match if requirements were passed in the package name as an array" do
- @new_resource = Chef::Resource::YumPackage.new(["test-package = 2.0.1.el5"])
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@new_resource.package_name).to eq(["test-package"])
- expect(@new_resource.version).to eq(["2.0.1.el5"])
- end
-
- it "should search provides and not set the versions to match if specific versions were requested in an array" do
- @new_resource = Chef::Resource::YumPackage.new(["test-package"])
- @new_resource.version(["3.0.1.el5"])
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@new_resource.package_name).to eq(["test-package"])
- expect(@new_resource.version).to eq(["3.0.1.el5"])
- end
-
- end
-
- it "should not return an error if no version number is specified in the resource" do
- @new_resource = Chef::Resource::YumPackage.new("test-package")
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache",
- :reload_installed => true,
- :reset => true,
- :installed_version => "1.0.1.el5",
- :candidate_version => "2.0.1.el5",
- :package_available? => false,
- :version_available? => true,
- :disable_extra_repo_control => true
- )
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- pkg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "2.0.1.el5", "x86_64", [])
- expect(@yum_cache).to receive(:packages_from_require).and_return([pkg])
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@new_resource.package_name).to eq("test-package")
- expect(@new_resource.version).to eq(nil)
- end
-
- it "should give precedence to the version attribute when both a requirement in the resource name and a version attribute are specified" do
- @new_resource = Chef::Resource::YumPackage.new("test-package")
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache",
- :reload_installed => true,
- :reset => true,
- :installed_version => "1.2.4-11.18.el5",
- :candidate_version => "1.2.4-11.18.el5",
- :package_available? => false,
- :version_available? => true,
- :disable_extra_repo_control => true
- )
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- pkg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "2.0.1.el5", "x86_64", [])
- expect(@yum_cache).to receive(:packages_from_require).and_return([pkg])
- @new_resource = Chef::Resource::YumPackage.new("test-package = 2.0.1.el5")
- @new_resource.version("3.0.1.el5")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@new_resource.package_name).to eq("test-package")
- expect(@new_resource.version).to eq("3.0.1.el5")
- end
-
- it "should correctly detect the installed states of an array of package names and version numbers" do
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache",
- :reload_installed => true,
- :reset => true,
- :installed_version => "1.0.1.el5",
- :candidate_version => "2.0.1.el5",
- :package_available? => false,
- :version_available? => true,
- :disable_extra_repo_control => true
- )
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
-
- expect(@yum_cache).to receive(:packages_from_require).exactly(4).times.and_return([])
- expect(@yum_cache).to receive(:reload_provides).twice
-
- @new_resource = Chef::Resource::YumPackage.new(["test-package", "test-package2"])
- @new_resource.version(["2.0.1.el5", "3.0.1.el5"])
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@new_resource.package_name).to eq(["test-package", "test-package2"])
- expect(@new_resource.version).to eq(["2.0.1.el5", "3.0.1.el5"])
- end
-
- it "should search provides if no package is available - if no match in installed provides then load the complete set" do
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache",
- :reload_installed => true,
- :reset => true,
- :installed_version => "1.2.4-11.18.el5",
- :candidate_version => "1.2.4-11.18.el5",
- :package_available? => false,
- :version_available? => true,
- :disable_extra_repo_control => true
- )
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- expect(@yum_cache).to receive(:packages_from_require).twice.and_return([])
- expect(@yum_cache).to receive(:reload_provides)
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@new_resource.version).to eq(nil)
- end
-
- it "should search provides if no package is available and not load the complete set if action is :remove or :purge" do
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache",
- :reload_installed => true,
- :reset => true,
- :installed_version => "1.2.4-11.18.el5",
- :candidate_version => "1.2.4-11.18.el5",
- :package_available? => false,
- :version_available? => true,
- :disable_extra_repo_control => true
- )
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- expect(@yum_cache).to receive(:packages_from_require).once.and_return([])
- expect(@yum_cache).not_to receive(:reload_provides)
- @new_resource.action(:remove)
- @provider.load_current_resource
- expect(@yum_cache).to receive(:packages_from_require).once.and_return([])
- expect(@yum_cache).not_to receive(:reload_provides)
- @new_resource.action(:purge)
- @provider.load_current_resource
- end
-
- it "should search provides if no package is available - if no match in provides leave the name intact" do
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache",
- :reload_provides => true,
- :reload_installed => true,
- :reset => true,
- :installed_version => "1.2.4-11.18.el5",
- :candidate_version => "1.2.4-11.18.el5",
- :package_available? => false,
- :version_available? => true,
- :disable_extra_repo_control => true
- )
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- expect(@yum_cache).to receive(:packages_from_require).twice.and_return([])
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@new_resource.package_name).to eq("cups")
- end
- end
-
- describe "when installing a package" do
- it "should run yum install with the package name and version" do
- @provider.load_current_resource
- allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y install cups-1.2.4-11.19.el5"
- )
- @provider.install_package("cups", "1.2.4-11.19.el5")
- end
-
- it "should run yum localinstall if given a path to an rpm" do
- allow(@new_resource).to receive(:source).and_return("/tmp/emacs-21.4-20.el5.i386.rpm")
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y localinstall /tmp/emacs-21.4-20.el5.i386.rpm"
- )
- @provider.install_package("emacs", "21.4-20.el5")
- end
-
- it "should run yum localinstall if given a path to an rpm as the package" do
- @new_resource = Chef::Resource::Package.new("/tmp/emacs-21.4-20.el5.i386.rpm")
- allow(::File).to receive(:exists?).and_return(true)
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- expect(@new_resource.source).to eq("/tmp/emacs-21.4-20.el5.i386.rpm")
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y localinstall /tmp/emacs-21.4-20.el5.i386.rpm"
- )
- @provider.install_package("/tmp/emacs-21.4-20.el5.i386.rpm", "21.4-20.el5")
- end
-
- it "should run yum install with the package name, version and arch" do
- @provider.load_current_resource
- allow(@new_resource).to receive(:arch).and_return("i386")
- allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y install cups-1.2.4-11.19.el5.i386"
- )
- @provider.install_package("cups", "1.2.4-11.19.el5")
- end
-
- it "installs the package with the options given in the resource" do
- @provider.load_current_resource
- allow(@provider).to receive(:candidate_version).and_return("11")
- allow(@new_resource).to receive(:options).and_return("--disablerepo epmd")
- allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y --disablerepo epmd install cups-11"
- )
- @provider.install_package(@new_resource.name, @provider.candidate_version)
- end
-
- it "should raise an exception if the package is not available" do
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache",
- :reload_from_cache => true,
- :reset => true,
- :installed_version => "1.2.4-11.18.el5",
- :candidate_version => "1.2.4-11.18.el5_2.3",
- :package_available? => true,
- :version_available? => nil,
- :disable_extra_repo_control => true
- )
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- expect { @provider.install_package("lolcats", "0.99") }.to raise_error(Chef::Exceptions::Package, %r{Version .* not found})
- end
-
- it "should raise an exception if candidate version is older than the installed version and allow_downgrade is false" do
- allow(@new_resource).to receive(:allow_downgrade).and_return(false)
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache",
- :reload_installed => true,
- :reset => true,
- :installed_version => "1.2.4-11.18.el5",
- :candidate_version => "1.2.4-11.15.el5",
- :package_available? => true,
- :version_available? => true,
- :allow_multi_install => [ "kernel" ],
- :disable_extra_repo_control => true
- )
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect { @provider.install_package("cups", "1.2.4-11.15.el5") }.to raise_error(Chef::Exceptions::Package, %r{is newer than candidate package})
- end
-
- it "should not raise an exception if candidate version is older than the installed version and the package is list in yum's installonlypkg option" do
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache",
- :reload_installed => true,
- :reset => true,
- :installed_version => "1.2.4-11.18.el5",
- :candidate_version => "1.2.4-11.15.el5",
- :package_available? => true,
- :version_available? => true,
- :allow_multi_install => [ "cups" ],
- :package_repository => "base",
- :disable_extra_repo_control => true
- )
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y install cups-1.2.4-11.15.el5"
- )
- @provider.install_package("cups", "1.2.4-11.15.el5")
- end
-
- it "should run yum downgrade if candidate version is older than the installed version and allow_downgrade is true" do
- allow(@new_resource).to receive(:allow_downgrade).and_return(true)
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache",
- :reload_installed => true,
- :reset => true,
- :installed_version => "1.2.4-11.18.el5",
- :candidate_version => "1.2.4-11.15.el5",
- :package_available? => true,
- :version_available? => true,
- :allow_multi_install => [],
- :package_repository => "base",
- :disable_extra_repo_control => true
- )
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y downgrade cups-1.2.4-11.15.el5"
- )
- @provider.install_package("cups", "1.2.4-11.15.el5")
- end
-
- it "should run yum install then flush the cache if :after is true" do
- allow(@new_resource).to receive(:flush_cache).and_return({ :after => true, :before => false })
- @provider.load_current_resource
- allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y install cups-1.2.4-11.15.el5"
- )
- expect(@yum_cache).to receive(:reload).once
- @provider.install_package("cups", "1.2.4-11.15.el5")
- end
-
- it "should run yum install then not flush the cache if :after is false" do
- allow(@new_resource).to receive(:flush_cache).and_return({ :after => false, :before => false })
- @provider.load_current_resource
- allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y install cups-1.2.4-11.15.el5"
- )
- expect(@yum_cache).not_to receive(:reload)
- @provider.install_package("cups", "1.2.4-11.15.el5")
- end
- end
-
- describe "when upgrading a package" do
- it "should run yum install if the package is installed and a version is given" do
- @provider.load_current_resource
- allow(@provider).to receive(:candidate_version).and_return("11")
- allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y install cups-11"
- )
- @provider.upgrade_package(@new_resource.name, @provider.candidate_version)
- end
-
- it "should run yum install if the package is not installed" do
- @provider.load_current_resource
- @current_resource = Chef::Resource::Package.new("cups")
- allow(@provider).to receive(:candidate_version).and_return("11")
- allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y install cups-11"
- )
- @provider.upgrade_package(@new_resource.name, @provider.candidate_version)
- end
-
- it "should raise an exception if candidate version is older than the installed version" do
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache",
- :reload_installed => true,
- :reset => true,
- :installed_version => "1.2.4-11.18.el5",
- :candidate_version => "1.2.4-11.15.el5",
- :package_available? => true,
- :version_available? => true,
- :allow_multi_install => [ "kernel" ],
- :disable_extra_repo_control => true
- )
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @provider.load_current_resource
- expect { @provider.upgrade_package("cups", "1.2.4-11.15.el5") }.to raise_error(Chef::Exceptions::Package, %r{is newer than candidate package})
- end
-
- # Test our little workaround, some crossover into Chef::Provider::Package territory
- it "should call action_upgrade in the parent if the current resource version is nil" do
- allow(@yum_cache).to receive(:installed_version).and_return(nil)
- @current_resource = Chef::Resource::Package.new("cups")
- allow(@provider).to receive(:candidate_version).and_return("11")
- expect(@provider).to receive(:upgrade_package).with(
- "cups",
- "11"
- )
- @provider.run_action(:upgrade)
- end
-
- it "should call action_upgrade in the parent if the candidate version is nil" do
- @provider.load_current_resource
- @current_resource = Chef::Resource::Package.new("cups")
- allow(@provider).to receive(:candidate_version).and_return(nil)
- expect(@provider).not_to receive(:upgrade_package)
- @provider.run_action(:upgrade)
- end
-
- it "should call action_upgrade in the parent if the candidate is newer" do
- @provider.load_current_resource
- @current_resource = Chef::Resource::Package.new("cups")
- allow(@provider).to receive(:candidate_version).and_return("11")
- expect(@provider).to receive(:upgrade_package).with(
- "cups",
- "11"
- )
- @provider.run_action(:upgrade)
- end
-
- it "should not call action_upgrade in the parent if the candidate is older" do
- allow(@yum_cache).to receive(:installed_version).and_return("12")
- @provider.load_current_resource
- @current_resource = Chef::Resource::Package.new("cups")
- allow(@provider).to receive(:candidate_version).and_return("11")
- expect(@provider).not_to receive(:upgrade_package)
- @provider.run_action(:upgrade)
- end
- end
-
- describe "when removing a package" do
- it "should run yum remove with the package name" do
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y remove emacs-1.0"
- )
- @provider.remove_package("emacs", "1.0")
- end
-
- it "should run yum remove with the package name and arch" do
- allow(@new_resource).to receive(:arch).and_return("x86_64")
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y remove emacs-1.0.x86_64"
- )
- @provider.remove_package("emacs", "1.0")
- end
- end
-
- describe "when purging a package" do
- it "should run yum remove with the package name" do
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y remove emacs-1.0"
- )
- @provider.purge_package("emacs", "1.0")
- end
- end
-
- describe "when running yum" do
- it "should run yum once if it exits with a return code of 0" do
- @status = double("Status", :exitstatus => 0, :stdout => "", :stderr => "")
- allow(@provider).to receive(:shell_out).and_return(@status)
- expect(@provider).to receive(:shell_out).once.with(
- "yum -d0 -e0 -y install emacs-1.0",
- { :timeout => Chef::Config[:yum_timeout] }
- )
- @provider.yum_command("-d0 -e0 -y install emacs-1.0")
- end
-
- it "should run yum once if it exits with a return code > 0 and no scriptlet failures" do
- @status = double("Status", :exitstatus => 2, :stdout => "failure failure", :stderr => "problem problem")
- allow(@provider).to receive(:shell_out).and_return(@status)
- expect(@provider).to receive(:shell_out).once.with(
- "yum -d0 -e0 -y install emacs-1.0",
- { :timeout => Chef::Config[:yum_timeout] }
- )
- expect { @provider.yum_command("-d0 -e0 -y install emacs-1.0") }.to raise_error(Chef::Exceptions::Exec)
- end
-
- it "should run yum once if it exits with a return code of 1 and %pre scriptlet failures" do
- @status = double("Status", :exitstatus => 1, :stdout => "error: %pre(demo-1-1.el5.centos.x86_64) scriptlet failed, exit status 2",
- :stderr => "")
- allow(@provider).to receive(:shell_out).and_return(@status)
- expect(@provider).to receive(:shell_out).once.with(
- "yum -d0 -e0 -y install emacs-1.0",
- { :timeout => Chef::Config[:yum_timeout] }
- )
- # will still raise an exception, can't stub out the subsequent call
- expect { @provider.yum_command("-d0 -e0 -y install emacs-1.0") }.to raise_error(Chef::Exceptions::Exec)
- end
-
- it "should run yum twice if it exits with a return code of 1 and %post scriptlet failures" do
- @status = double("Status", :exitstatus => 1, :stdout => "error: %post(demo-1-1.el5.centos.x86_64) scriptlet failed, exit status 2",
- :stderr => "")
- allow(@provider).to receive(:shell_out).and_return(@status)
- expect(@provider).to receive(:shell_out).twice.with(
- "yum -d0 -e0 -y install emacs-1.0",
- { :timeout => Chef::Config[:yum_timeout] }
- )
- # will still raise an exception, can't stub out the subsequent call
- expect { @provider.yum_command("-d0 -e0 -y install emacs-1.0") }.to raise_error(Chef::Exceptions::Exec)
- end
-
- it "should pass the yum_binary to the command if its specified" do
- @new_resource.yum_binary "yum-deprecated"
- expect(@yum_cache).to receive(:yum_binary=).with("yum-deprecated")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @status = double("Status", :exitstatus => 0, :stdout => "", :stderr => "")
- allow(@provider).to receive(:shell_out).and_return(@status)
- expect(@provider).to receive(:shell_out).once.with(
- "yum-deprecated -d0 -e0 -y install emacs-1.0",
- { :timeout => Chef::Config[:yum_timeout] }
- )
- @provider.yum_command("-d0 -e0 -y install emacs-1.0")
- end
- end
-end
-
-describe Chef::Provider::Package::Yum::RPMUtils do
- describe "version_parse" do
- before do
- @rpmutils = Chef::Provider::Package::Yum::RPMUtils
- end
-
- it "parses known good epoch strings" do
- [
- [ "0:3.3", [ 0, "3.3", nil ] ],
- [ "9:1.7.3", [ 9, "1.7.3", nil ] ],
- [ "15:20020927", [ 15, "20020927", nil ] ],
- ].each do |x, y|
- expect(@rpmutils.version_parse(x)).to eq(y)
- end
- end
-
- it "parses strange epoch strings" do
- [
- [ ":3.3", [ 0, "3.3", nil ] ],
- [ "-1:1.7.3", [ nil, nil, "1:1.7.3" ] ],
- [ "-:20020927", [ nil, nil, ":20020927" ] ],
- ].each do |x, y|
- expect(@rpmutils.version_parse(x)).to eq(y)
- end
- end
-
- it "parses known good version strings" do
- [
- [ "3.3", [ nil, "3.3", nil ] ],
- [ "1.7.3", [ nil, "1.7.3", nil ] ],
- [ "20020927", [ nil, "20020927", nil ] ],
- ].each do |x, y|
- expect(@rpmutils.version_parse(x)).to eq(y)
- end
- end
-
- it "parses strange version strings" do
- [
- [ "3..3", [ nil, "3..3", nil ] ],
- [ "0001.7.3", [ nil, "0001.7.3", nil ] ],
- [ "20020927,3", [ nil, "20020927,3", nil ] ],
- ].each do |x, y|
- expect(@rpmutils.version_parse(x)).to eq(y)
- end
- end
-
- it "parses known good version release strings" do
- [
- [ "3.3-0.pre3.1.60.el5_5.1", [ nil, "3.3", "0.pre3.1.60.el5_5.1" ] ],
- [ "1.7.3-1jpp.2.el5", [ nil, "1.7.3", "1jpp.2.el5" ] ],
- [ "20020927-46.el5", [ nil, "20020927", "46.el5" ] ],
- ].each do |x, y|
- expect(@rpmutils.version_parse(x)).to eq(y)
- end
- end
-
- it "parses strange version release strings" do
- [
- [ "3.3-", [ nil, "3.3", nil ] ],
- [ "-1jpp.2.el5", [ nil, nil, "1jpp.2.el5" ] ],
- [ "-0020020927-46.el5", [ nil, "-0020020927", "46.el5" ] ],
- ].each do |x, y|
- expect(@rpmutils.version_parse(x)).to eq(y)
- end
- end
- end
-
- describe "rpmvercmp" do
- before do
- @rpmutils = Chef::Provider::Package::Yum::RPMUtils
- end
-
- it "should validate version compare logic for standard examples" do
- [
- # numeric
- [ "0.0.2", "0.0.1", 1 ],
- [ "0.2.0", "0.1.0", 1 ],
- [ "2.0.0", "1.0.0", 1 ],
- [ "0.0.1", "0.0.1", 0 ],
- [ "0.0.1", "0.0.2", -1 ],
- [ "0.1.0", "0.2.0", -1 ],
- [ "1.0.0", "2.0.0", -1 ],
- # alpha
- [ "bb", "aa", 1 ],
- [ "ab", "aa", 1 ],
- [ "aa", "aa", 0 ],
- [ "aa", "bb", -1 ],
- [ "aa", "ab", -1 ],
- [ "BB", "AA", 1 ],
- [ "AA", "AA", 0 ],
- [ "AA", "BB", -1 ],
- [ "aa", "AA", 1 ],
- [ "AA", "aa", -1 ],
- # alphanumeric
- [ "0.0.1b", "0.0.1a", 1 ],
- [ "0.1b.0", "0.1a.0", 1 ],
- [ "1b.0.0", "1a.0.0", 1 ],
- [ "0.0.1a", "0.0.1a", 0 ],
- [ "0.0.1a", "0.0.1b", -1 ],
- [ "0.1a.0", "0.1b.0", -1 ],
- [ "1a.0.0", "1b.0.0", -1 ],
- # alphanumeric against alphanumeric
- [ "0.0.1", "0.0.a", 1 ],
- [ "0.1.0", "0.a.0", 1 ],
- [ "1.0.0", "a.0.0", 1 ],
- [ "0.0.a", "0.0.a", 0 ],
- [ "0.0.a", "0.0.1", -1 ],
- [ "0.a.0", "0.1.0", -1 ],
- [ "a.0.0", "1.0.0", -1 ],
- # alphanumeric against numeric
- [ "0.0.2", "0.0.1a", 1 ],
- [ "0.0.2a", "0.0.1", 1 ],
- [ "0.0.1", "0.0.2a", -1 ],
- [ "0.0.1a", "0.0.2", -1 ],
- # length
- [ "0.0.1aa", "0.0.1a", 1 ],
- [ "0.0.1aa", "0.0.1aa", 0 ],
- [ "0.0.1a", "0.0.1aa", -1 ],
- ].each do |x, y, result|
- expect(@rpmutils.rpmvercmp(x, y)).to eq(result)
- end
- end
-
- it "should validate version compare logic for strange examples" do
- [
- [ "2,0,0", "1.0.0", 1 ],
- [ "0.0.1", "0,0.1", 0 ],
- [ "1.0.0", "2,0,0", -1 ],
- [ "002.0.0", "001.0.0", 1 ],
- [ "001..0.1", "001..0.0", 1 ],
- [ "-001..1", "-001..0", 1 ],
- [ "1.0.1", nil, 1 ],
- [ nil, nil, 0 ],
- [ nil, "1.0.1", -1 ],
- [ "1.0.1", "", 1 ],
- [ "", "", 0 ],
- [ "", "1.0.1", -1 ],
- ].each do |x, y, result|
- expect(@rpmutils.rpmvercmp(x, y)).to eq(result)
- end
- end
-
- it "tests isalnum good input" do
- %w{a z A Z 0 9}.each do |t|
- expect(@rpmutils.isalnum(t)).to eq(true)
- end
- end
-
- it "tests isalnum bad input" do
- [ "-", ".", "!", "^", ":", "_" ].each do |t|
- expect(@rpmutils.isalnum(t)).to eq(false)
- end
- end
-
- it "tests isalpha good input" do
- %w{a z A Z}.each do |t|
- expect(@rpmutils.isalpha(t)).to eq(true)
- end
- end
-
- it "tests isalpha bad input" do
- [ "0", "9", "-", ".", "!", "^", ":", "_" ].each do |t|
- expect(@rpmutils.isalpha(t)).to eq(false)
- end
- end
-
- it "tests isdigit good input" do
- %w{0 9}.each do |t|
- expect(@rpmutils.isdigit(t)).to eq(true)
- end
- end
-
- it "tests isdigit bad input" do
- [ "A", "z", "-", ".", "!", "^", ":", "_" ].each do |t|
- expect(@rpmutils.isdigit(t)).to eq(false)
- end
- end
- end
-
-end
-
-describe Chef::Provider::Package::Yum::RPMVersion do
- describe "new - with parsing" do
- before do
- @rpmv = Chef::Provider::Package::Yum::RPMVersion.new("1:1.6.5-9.36.el5")
- end
-
- it "should expose evr (name-version-release) available" do
- expect(@rpmv.e).to eq(1)
- expect(@rpmv.v).to eq("1.6.5")
- expect(@rpmv.r).to eq("9.36.el5")
-
- expect(@rpmv.evr).to eq("1:1.6.5-9.36.el5")
- end
-
- it "should output a version-release string" do
- expect(@rpmv.to_s).to eq("1.6.5-9.36.el5")
- end
- end
-
- describe "new - no parsing" do
- before do
- @rpmv = Chef::Provider::Package::Yum::RPMVersion.new("1", "1.6.5", "9.36.el5")
- end
-
- it "should expose evr (name-version-release) available" do
- expect(@rpmv.e).to eq(1)
- expect(@rpmv.v).to eq("1.6.5")
- expect(@rpmv.r).to eq("9.36.el5")
-
- expect(@rpmv.evr).to eq("1:1.6.5-9.36.el5")
- end
-
- it "should output a version-release string" do
- expect(@rpmv.to_s).to eq("1.6.5-9.36.el5")
- end
- end
-
- it "should raise an error unless passed 1 or 3 args" do
- expect {
- Chef::Provider::Package::Yum::RPMVersion.new()
- }.to raise_error(ArgumentError)
- expect {
- Chef::Provider::Package::Yum::RPMVersion.new("1:1.6.5-9.36.el5")
- }.not_to raise_error
- expect {
- Chef::Provider::Package::Yum::RPMVersion.new("1:1.6.5-9.36.el5", "extra")
- }.to raise_error(ArgumentError)
- expect {
- Chef::Provider::Package::Yum::RPMVersion.new("1", "1.6.5", "9.36.el5")
- }.not_to raise_error
- expect {
- Chef::Provider::Package::Yum::RPMVersion.new("1", "1.6.5", "9.36.el5", "extra")
- }.to raise_error(ArgumentError)
- end
-
- # thanks version_class_spec.rb!
- describe "compare" do
- it "should sort based on complete epoch-version-release data" do
- [
- # smaller, larger
- [ "0:1.6.5-9.36.el5",
- "1:1.6.5-9.36.el5" ],
- [ "0:2.3-15.el5",
- "0:3.3-15.el5" ],
- [ "0:alpha9.8-27.2",
- "0:beta9.8-27.2" ],
- [ "0:0.09-14jpp.3",
- "0:0.09-15jpp.3" ],
- [ "0:0.9.0-0.6.20110211.el5",
- "0:0.9.0-0.6.20120211.el5" ],
- [ "0:1.9.1-4.el5",
- "0:1.9.1-5.el5" ],
- [ "0:1.4.10-7.20090624svn.el5",
- "0:1.4.10-7.20090625svn.el5" ],
- [ "0:2.3.4-2.el5",
- "0:2.3.4-2.el6" ],
- ].each do |smaller, larger|
- sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller)
- lg = Chef::Provider::Package::Yum::RPMVersion.new(larger)
- expect(sm).to be < lg
- expect(lg).to be > sm
- expect(sm).not_to eq(lg)
- end
- end
-
- it "should sort based on partial epoch-version-release data" do
- [
- # smaller, larger
- [ ":1.6.5-9.36.el5",
- "1:1.6.5-9.36.el5" ],
- [ "2.3-15.el5",
- "3.3-15.el5" ],
- [ "alpha9.8",
- "beta9.8" ],
- %w{14jpp
-15jpp},
- [ "0.9.0-0.6",
- "0.9.0-0.7" ],
- [ "0:1.9",
- "3:1.9" ],
- [ "2.3-2.el5",
- "2.3-2.el6" ],
- ].each do |smaller, larger|
- sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller)
- lg = Chef::Provider::Package::Yum::RPMVersion.new(larger)
- expect(sm).to be < lg
- expect(lg).to be > sm
- expect(sm).not_to eq(lg)
- end
- end
-
- it "should verify equality of complete epoch-version-release data" do
- [
- [ "0:1.6.5-9.36.el5",
- "0:1.6.5-9.36.el5" ],
- [ "0:2.3-15.el5",
- "0:2.3-15.el5" ],
- [ "0:alpha9.8-27.2",
- "0:alpha9.8-27.2" ],
- ].each do |smaller, larger|
- sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller)
- lg = Chef::Provider::Package::Yum::RPMVersion.new(larger)
- expect(sm).to eq(lg)
- end
- end
-
- it "should verify equality of partial epoch-version-release data" do
- [
- [ ":1.6.5-9.36.el5",
- "0:1.6.5-9.36.el5" ],
- [ "2.3-15.el5",
- "2.3-15.el5" ],
- [ "alpha9.8-3",
- "alpha9.8-3" ],
- ].each do |smaller, larger|
- sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller)
- lg = Chef::Provider::Package::Yum::RPMVersion.new(larger)
- expect(sm).to eq(lg)
- end
- end
- end
-
- describe "partial compare" do
- it "should compare based on partial epoch-version-release data" do
- [
- # smaller, larger
- [ "0:1.1.1-1",
- "1:" ],
- [ "0:1.1.1-1",
- "0:1.1.2" ],
- [ "0:1.1.1-1",
- "0:1.1.2-1" ],
- [ "0:",
- "1:1.1.1-1" ],
- [ "0:1.1.1",
- "0:1.1.2-1" ],
- [ "0:1.1.1-1",
- "0:1.1.2-1" ],
- ].each do |smaller, larger|
- sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller)
- lg = Chef::Provider::Package::Yum::RPMVersion.new(larger)
- expect(sm.partial_compare(lg)).to eq(-1)
- expect(lg.partial_compare(sm)).to eq(1)
- expect(sm.partial_compare(lg)).not_to eq(0)
- end
- end
-
- it "should verify equality based on partial epoch-version-release data" do
- [
- [ "0:",
- "0:1.1.1-1" ],
- [ "0:1.1.1",
- "0:1.1.1-1" ],
- [ "0:1.1.1-1",
- "0:1.1.1-1" ],
- ].each do |smaller, larger|
- sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller)
- lg = Chef::Provider::Package::Yum::RPMVersion.new(larger)
- expect(sm.partial_compare(lg)).to eq(0)
- end
- end
- end
-
-end
-
-describe Chef::Provider::Package::Yum::RPMPackage do
- describe "new - with parsing" do
- before do
- @rpm = Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5", "x86_64", [])
- end
-
- it "should expose nevra (name-epoch-version-release-arch) available" do
- expect(@rpm.name).to eq("testing")
- expect(@rpm.version.e).to eq(1)
- expect(@rpm.version.v).to eq("1.6.5")
- expect(@rpm.version.r).to eq("9.36.el5")
- expect(@rpm.arch).to eq("x86_64")
-
- expect(@rpm.nevra).to eq("testing-1:1.6.5-9.36.el5.x86_64")
- expect(@rpm.to_s).to eq(@rpm.nevra)
- end
-
- it "should always have at least one provide, itself" do
- expect(@rpm.provides.size).to eq(1)
- @rpm.provides[0].name == "testing"
- @rpm.provides[0].version.evr == "1:1.6.5-9.36.el5"
- @rpm.provides[0].flag == :==
- end
- end
-
- describe "new - no parsing" do
- before do
- @rpm = Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64", [])
- end
-
- it "should expose nevra (name-epoch-version-release-arch) available" do
- expect(@rpm.name).to eq("testing")
- expect(@rpm.version.e).to eq(1)
- expect(@rpm.version.v).to eq("1.6.5")
- expect(@rpm.version.r).to eq("9.36.el5")
- expect(@rpm.arch).to eq("x86_64")
-
- expect(@rpm.nevra).to eq("testing-1:1.6.5-9.36.el5.x86_64")
- expect(@rpm.to_s).to eq(@rpm.nevra)
- end
-
- it "should always have at least one provide, itself" do
- expect(@rpm.provides.size).to eq(1)
- @rpm.provides[0].name == "testing"
- @rpm.provides[0].version.evr == "1:1.6.5-9.36.el5"
- @rpm.provides[0].flag == :==
- end
- end
-
- it "should raise an error unless passed 4 or 6 args" do
- expect {
- Chef::Provider::Package::Yum::RPMPackage.new()
- }.to raise_error(ArgumentError)
- expect {
- Chef::Provider::Package::Yum::RPMPackage.new("testing")
- }.to raise_error(ArgumentError)
- expect {
- Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5")
- }.to raise_error(ArgumentError)
- expect {
- Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5", "x86_64")
- }.to raise_error(ArgumentError)
- expect {
- Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5", "x86_64", [])
- }.not_to raise_error
- expect {
- Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64")
- }.to raise_error(ArgumentError)
- expect {
- Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64", [])
- }.not_to raise_error
- expect {
- Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64", [], "extra")
- }.to raise_error(ArgumentError)
- end
-
- describe "<=>" do
- it "should sort alphabetically based on package name" do
- [
- [ "a-test",
- "b-test" ],
- [ "B-test",
- "a-test" ],
- [ "A-test",
- "B-test" ],
- [ "Aa-test",
- "aA-test" ],
- %w{1test
-2test},
- ].each do |smaller, larger|
- sm = Chef::Provider::Package::Yum::RPMPackage.new(smaller, "0:0.0.1-1", "x86_64", [])
- lg = Chef::Provider::Package::Yum::RPMPackage.new(larger, "0:0.0.1-1", "x86_64", [])
- expect(sm).to be < lg
- expect(lg).to be > sm
- expect(sm).not_to eq(lg)
- end
- end
-
- it "should sort alphabetically based on package arch" do
- [
- %w{i386
-x86_64},
- %w{i386
-noarch},
- %w{noarch
-x86_64},
- ].each do |smaller, larger|
- sm = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "0:0.0.1-1", smaller, [])
- lg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "0:0.0.1-1", larger, [])
- expect(sm).to be < lg
- expect(lg).to be > sm
- expect(sm).not_to eq(lg)
- end
- end
- end
-
-end
-
-describe Chef::Provider::Package::Yum::RPMDbPackage do
- before(:each) do
- # name, version, arch, installed, available, repoid
- @rpm_x = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "0:1.6.5-9.36.el5", "noarch", [], false, true, "base")
- @rpm_y = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "0:1.6.5-9.36.el5", "noarch", [], true, true, "extras")
- @rpm_z = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "0:1.6.5-9.36.el5", "noarch", [], true, false, "other")
- end
-
- describe "initialize" do
- it "should return a Chef::Provider::Package::Yum::RPMDbPackage object" do
- expect(@rpm_x).to be_kind_of(Chef::Provider::Package::Yum::RPMDbPackage)
- end
- end
-
- describe "available" do
- it "should return true" do
- expect(@rpm_x.available).to eq(true)
- expect(@rpm_y.available).to eq(true)
- expect(@rpm_z.available).to eq(false)
- end
- end
-
- describe "installed" do
- it "should return true" do
- expect(@rpm_x.installed).to eq(false)
- expect(@rpm_y.installed).to eq(true)
- expect(@rpm_z.installed).to eq(true)
- end
- end
-
- describe "repoid" do
- it "should return the source repository repoid" do
- expect(@rpm_x.repoid).to eq("base")
- expect(@rpm_y.repoid).to eq("extras")
- expect(@rpm_z.repoid).to eq("other")
- end
- end
-end
-
-describe Chef::Provider::Package::Yum::RPMDependency do
- describe "new - with parsing" do
- before do
- @rpmdep = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==)
- end
-
- it "should expose name, version, flag available" do
- expect(@rpmdep.name).to eq("testing")
- expect(@rpmdep.version.e).to eq(1)
- expect(@rpmdep.version.v).to eq("1.6.5")
- expect(@rpmdep.version.r).to eq("9.36.el5")
- expect(@rpmdep.flag).to eq(:==)
- end
- end
-
- describe "new - no parsing" do
- before do
- @rpmdep = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1", "1.6.5", "9.36.el5", :==)
- end
-
- it "should expose name, version, flag available" do
- expect(@rpmdep.name).to eq("testing")
- expect(@rpmdep.version.e).to eq(1)
- expect(@rpmdep.version.v).to eq("1.6.5")
- expect(@rpmdep.version.r).to eq("9.36.el5")
- expect(@rpmdep.flag).to eq(:==)
- end
- end
-
- it "should raise an error unless passed 3 or 5 args" do
- expect {
- Chef::Provider::Package::Yum::RPMDependency.new()
- }.to raise_error(ArgumentError)
- expect {
- Chef::Provider::Package::Yum::RPMDependency.new("testing")
- }.to raise_error(ArgumentError)
- expect {
- Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5")
- }.to raise_error(ArgumentError)
- expect {
- Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==)
- }.not_to raise_error
- expect {
- Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==, "extra")
- }.to raise_error(ArgumentError)
- expect {
- Chef::Provider::Package::Yum::RPMDependency.new("testing", "1", "1.6.5", "9.36.el5", :==)
- }.not_to raise_error
- expect {
- Chef::Provider::Package::Yum::RPMDependency.new("testing", "1", "1.6.5", "9.36.el5", :==, "extra")
- }.to raise_error(ArgumentError)
- end
-
- describe "parse" do
- it "should parse a name, flag, version string into a valid RPMDependency object" do
- @rpmdep = Chef::Provider::Package::Yum::RPMDependency.parse("testing >= 1:1.6.5-9.36.el5")
-
- expect(@rpmdep.name).to eq("testing")
- expect(@rpmdep.version.e).to eq(1)
- expect(@rpmdep.version.v).to eq("1.6.5")
- expect(@rpmdep.version.r).to eq("9.36.el5")
- expect(@rpmdep.flag).to eq(:>=)
- end
-
- it "should parse a name into a valid RPMDependency object" do
- @rpmdep = Chef::Provider::Package::Yum::RPMDependency.parse("testing")
-
- expect(@rpmdep.name).to eq("testing")
- expect(@rpmdep.version.e).to eq(nil)
- expect(@rpmdep.version.v).to eq(nil)
- expect(@rpmdep.version.r).to eq(nil)
- expect(@rpmdep.flag).to eq(:==)
- end
-
- it "should parse an invalid string into the name of a RPMDependency object" do
- @rpmdep = Chef::Provider::Package::Yum::RPMDependency.parse("testing blah >")
-
- expect(@rpmdep.name).to eq("testing blah >")
- expect(@rpmdep.version.e).to eq(nil)
- expect(@rpmdep.version.v).to eq(nil)
- expect(@rpmdep.version.r).to eq(nil)
- expect(@rpmdep.flag).to eq(:==)
- end
-
- it "should parse various valid flags" do
- [
- [ ">", :> ],
- [ ">=", :>= ],
- [ "=", :== ],
- [ "==", :== ],
- [ "<=", :<= ],
- [ "<", :< ],
- ].each do |before, after|
- @rpmdep = Chef::Provider::Package::Yum::RPMDependency.parse("testing #{before} 1:1.1-1")
- expect(@rpmdep.flag).to eq(after)
- end
- end
-
- it "should parse various invalid flags and treat them as names" do
- [
- [ "<>", :== ],
- [ "!=", :== ],
- [ ">>", :== ],
- [ "<<", :== ],
- [ "!", :== ],
- [ "~", :== ],
- ].each do |before, after|
- @rpmdep = Chef::Provider::Package::Yum::RPMDependency.parse("testing #{before} 1:1.1-1")
- expect(@rpmdep.name).to eq("testing #{before} 1:1.1-1")
- expect(@rpmdep.flag).to eq(after)
- end
- end
- end
-
- describe "satisfy?" do
- it "should raise an error unless a RPMDependency is passed" do
- @rpmprovide = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==)
- @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :>=)
- expect {
- @rpmprovide.satisfy?("hi")
- }.to raise_error(ArgumentError)
- expect {
- @rpmprovide.satisfy?(@rpmrequire)
- }.not_to raise_error
- end
-
- it "should validate dependency satisfaction logic for standard examples" do
- [
- # names
- [ "test", "test", true ],
- [ "test", "foo", false ],
- # full: epoch:version-relese
- [ "testing = 1:1.1-1", "testing > 1:1.1-0", true ],
- [ "testing = 1:1.1-1", "testing >= 1:1.1-0", true ],
- [ "testing = 1:1.1-1", "testing >= 1:1.1-1", true ],
- [ "testing = 1:1.1-1", "testing = 1:1.1-1", true ],
- [ "testing = 1:1.1-1", "testing == 1:1.1-1", true ],
- [ "testing = 1:1.1-1", "testing <= 1:1.1-1", true ],
- [ "testing = 1:1.1-1", "testing <= 1:1.1-0", false ],
- [ "testing = 1:1.1-1", "testing < 1:1.1-0", false ],
- # partial: epoch:version
- [ "testing = 1:1.1", "testing > 1:1.0", true ],
- [ "testing = 1:1.1", "testing >= 1:1.0", true ],
- [ "testing = 1:1.1", "testing >= 1:1.1", true ],
- [ "testing = 1:1.1", "testing = 1:1.1", true ],
- [ "testing = 1:1.1", "testing == 1:1.1", true ],
- [ "testing = 1:1.1", "testing <= 1:1.1", true ],
- [ "testing = 1:1.1", "testing <= 1:1.0", false ],
- [ "testing = 1:1.1", "testing < 1:1.0", false ],
- # partial: epoch
- [ "testing = 1:", "testing > 0:", true ],
- [ "testing = 1:", "testing >= 0:", true ],
- [ "testing = 1:", "testing >= 1:", true ],
- [ "testing = 1:", "testing = 1:", true ],
- [ "testing = 1:", "testing == 1:", true ],
- [ "testing = 1:", "testing <= 1:", true ],
- [ "testing = 1:", "testing <= 0:", false ],
- [ "testing = 1:", "testing < 0:", false ],
- # mix and match!
- [ "testing = 1:1.1-1", "testing == 1:1.1", true ],
- [ "testing = 1:1.1-1", "testing == 1:", true ],
- ].each do |prov, req, result|
- @rpmprovide = Chef::Provider::Package::Yum::RPMDependency.parse(prov)
- @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.parse(req)
-
- expect(@rpmprovide.satisfy?(@rpmrequire)).to eq(result)
- expect(@rpmrequire.satisfy?(@rpmprovide)).to eq(result)
- end
- end
- end
-
-end
-
-# thanks resource_collection_spec.rb!
-describe Chef::Provider::Package::Yum::RPMDb do
- before(:each) do
- @rpmdb = Chef::Provider::Package::Yum::RPMDb.new
- # name, version, arch, installed, available
- deps_v = [
- Chef::Provider::Package::Yum::RPMDependency.parse("libz.so.1()(64bit)"),
- Chef::Provider::Package::Yum::RPMDependency.parse("test-package-a = 0:1.6.5-9.36.el5"),
- ]
- deps_z = [
- Chef::Provider::Package::Yum::RPMDependency.parse("libz.so.1()(64bit)"),
- Chef::Provider::Package::Yum::RPMDependency.parse("config(test) = 0:1.6.5-9.36.el5"),
- Chef::Provider::Package::Yum::RPMDependency.parse("test-package-c = 0:1.6.5-9.36.el5"),
- ]
- @rpm_v = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-a", "0:1.6.5-9.36.el5", "i386", deps_v, true, false, "base")
- @rpm_w = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "0:1.6.5-9.36.el5", "i386", [], true, true, "extras")
- @rpm_x = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "0:1.6.5-9.36.el5", "x86_64", [], false, true, "extras")
- @rpm_y = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "1:1.6.5-9.36.el5", "x86_64", [], true, true, "extras")
- @rpm_z = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-c", "0:1.6.5-9.36.el5", "noarch", deps_z, true, true, "base")
- @rpm_z_mirror = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-c", "0:1.6.5-9.36.el5", "noarch", deps_z, true, true, "base")
- end
-
- describe "initialize" do
- it "should return a Chef::Provider::Package::Yum::RPMDb object" do
- expect(@rpmdb).to be_kind_of(Chef::Provider::Package::Yum::RPMDb)
- end
- end
-
- describe "push" do
- it "should accept an RPMDbPackage object through pushing" do
- expect { @rpmdb.push(@rpm_w) }.not_to raise_error
- end
-
- it "should accept multiple RPMDbPackage object through pushing" do
- expect { @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z) }.not_to raise_error
- end
-
- it "should only accept an RPMDbPackage object" do
- expect { @rpmdb.push("string") }.to raise_error(ArgumentError)
- end
-
- it "should add the package to the package db" do
- @rpmdb.push(@rpm_w)
- expect(@rpmdb["test-package-b"]).not_to eq(nil)
- end
-
- it "should add conditionally add the package to the available list" do
- expect(@rpmdb.available_size).to eq(0)
- @rpmdb.push(@rpm_v, @rpm_w)
- expect(@rpmdb.available_size).to eq(1)
- end
-
- it "should add conditionally add the package to the installed list" do
- expect(@rpmdb.installed_size).to eq(0)
- @rpmdb.push(@rpm_w, @rpm_x)
- expect(@rpmdb.installed_size).to eq(1)
- end
-
- it "should have a total of 2 packages in the RPMDb" do
- expect(@rpmdb.size).to eq(0)
- @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z)
- expect(@rpmdb.size).to eq(2)
- end
-
- it "should keep the Array unique when a duplicate is pushed" do
- @rpmdb.push(@rpm_z, @rpm_z_mirror)
- expect(@rpmdb["test-package-c"].size).to eq(1)
- end
-
- it "should register the package provides in the provides index" do
- @rpmdb.push(@rpm_v, @rpm_w, @rpm_z)
- expect(@rpmdb.lookup_provides("test-package-a")[0]).to eq(@rpm_v)
- expect(@rpmdb.lookup_provides("config(test)")[0]).to eq(@rpm_z)
- expect(@rpmdb.lookup_provides("libz.so.1()(64bit)")[0]).to eq(@rpm_v)
- expect(@rpmdb.lookup_provides("libz.so.1()(64bit)")[1]).to eq(@rpm_z)
- end
- end
-
- describe "<<" do
- it "should accept an RPMPackage object through the << operator" do
- expect { @rpmdb << @rpm_w }.not_to raise_error
- end
- end
-
- describe "lookup" do
- it "should return an Array of RPMPackage objects by index" do
- @rpmdb << @rpm_w
- expect(@rpmdb.lookup("test-package-b")).to be_kind_of(Array)
- end
- end
-
- describe "[]" do
- it "should return an Array of RPMPackage objects though the [index] operator" do
- @rpmdb << @rpm_w
- expect(@rpmdb["test-package-b"]).to be_kind_of(Array)
- end
-
- it "should return an Array of 3 RPMPackage objects" do
- @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z)
- expect(@rpmdb["test-package-b"].size).to eq(3)
- end
-
- it "should return an Array of RPMPackage objects sorted from newest to oldest" do
- @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z)
- expect(@rpmdb["test-package-b"][0]).to eq(@rpm_y)
- expect(@rpmdb["test-package-b"][1]).to eq(@rpm_x)
- expect(@rpmdb["test-package-b"][2]).to eq(@rpm_w)
- end
- end
-
- describe "lookup_provides" do
- it "should return an Array of RPMPackage objects by index" do
- @rpmdb << @rpm_z
- x = @rpmdb.lookup_provides("config(test)")
- expect(x).to be_kind_of(Array)
- expect(x[0]).to eq(@rpm_z)
- end
- end
-
- describe "clear" do
- it "should clear the RPMDb" do
- expect(@rpmdb).to receive(:clear_available).once
- expect(@rpmdb).to receive(:clear_installed).once
- @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z)
- expect(@rpmdb.size).not_to eq(0)
- expect(@rpmdb.lookup_provides("config(test)")).to be_kind_of(Array)
- @rpmdb.clear
- expect(@rpmdb.lookup_provides("config(test)")).to eq(nil)
- expect(@rpmdb.size).to eq(0)
- end
- end
-
- describe "clear_available" do
- it "should clear the available list" do
- @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z)
- expect(@rpmdb.available_size).not_to eq(0)
- @rpmdb.clear_available
- expect(@rpmdb.available_size).to eq(0)
- end
- end
-
- describe "available?" do
- it "should return true if a package is available" do
- expect(@rpmdb.available?(@rpm_w)).to eq(false)
- @rpmdb.push(@rpm_v, @rpm_w)
- expect(@rpmdb.available?(@rpm_v)).to eq(false)
- expect(@rpmdb.available?(@rpm_w)).to eq(true)
- end
- end
-
- describe "clear_installed" do
- it "should clear the installed list" do
- @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z)
- expect(@rpmdb.installed_size).not_to eq(0)
- @rpmdb.clear_installed
- expect(@rpmdb.installed_size).to eq(0)
- end
- end
-
- describe "installed?" do
- it "should return true if a package is installed" do
- expect(@rpmdb.installed?(@rpm_w)).to eq(false)
- @rpmdb.push(@rpm_w, @rpm_x)
- expect(@rpmdb.installed?(@rpm_w)).to eq(true)
- expect(@rpmdb.installed?(@rpm_x)).to eq(false)
- end
- end
-
- describe "whatprovides" do
- it "should raise an error unless a RPMDependency is passed" do
- @rpmprovide = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==)
- @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :>=)
- expect {
- @rpmdb.whatprovides("hi")
- }.to raise_error(ArgumentError)
- expect {
- @rpmdb.whatprovides(@rpmrequire)
- }.not_to raise_error
- end
-
- it "should return an Array of packages statisfying a RPMDependency" do
- @rpmdb.push(@rpm_v, @rpm_w, @rpm_z)
-
- @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.parse("test-package-a >= 1.6.5")
- x = @rpmdb.whatprovides(@rpmrequire)
- expect(x).to be_kind_of(Array)
- expect(x[0]).to eq(@rpm_v)
-
- @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.parse("libz.so.1()(64bit)")
- x = @rpmdb.whatprovides(@rpmrequire)
- expect(x).to be_kind_of(Array)
- expect(x[0]).to eq(@rpm_v)
- expect(x[1]).to eq(@rpm_z)
- end
- end
-
-end
-
-describe Chef::Provider::Package::Yum::YumCache do
- # allow for the reset of a Singleton
- # thanks to Ian White (http://blog.ardes.com/2006/12/11/testing-singletons-with-ruby)
- class << Chef::Provider::Package::Yum::YumCache
- def reset_instance
- Singleton.send :__init__, self
- self
- end
- end
-
- let(:yum_exe) {
- StringIO.new("#!/usr/bin/python\n\naldsjfa\ldsajflkdsjf\lajsdfj")
- }
-
- let(:bin_exe) {
- StringIO.new(SecureRandom.random_bytes)
- }
-
- before(:each) do
- @stdin = double("STDIN", :nil_object => true)
- @stdout = double("STDOUT", :nil_object => true)
-
- @stdout_good = <<EOF
-[option installonlypkgs] kernel kernel-bigmem kernel-enterprise
-erlang-mochiweb 0 1.4.1 5.el5 x86_64 ['erlang-mochiweb = 1.4.1-5.el5', 'mochiweb = 1.4.1-5.el5'] i installed
-zip 0 2.31 2.el5 x86_64 ['zip = 2.31-2.el5'] r base
-zisofs-tools 0 1.0.6 3.2.2 x86_64 [] a extras
-zlib 0 1.2.3 3 x86_64 ['zlib = 1.2.3-3', 'libz.so.1()(64bit)'] r base
-zlib 0 1.2.3 3 i386 ['zlib = 1.2.3-3', 'libz.so.1'] r base
-zlib-devel 0 1.2.3 3 i386 [] a extras
-zlib-devel 0 1.2.3 3 x86_64 ['zlib-devel = 1.2.3-3'] r base
-znc 0 0.098 1.el5 x86_64 [] a base
-znc-devel 0 0.098 1.el5 i386 [] a extras
-znc-devel 0 0.098 1.el5 x86_64 [] a base
-znc-extra 0 0.098 1.el5 x86_64 [] a base
-znc-modtcl 0 0.098 1.el5 x86_64 [] a base
-znc-test.beta1 0 0.098 1.el5 x86_64 [] a extras
-znc-test.test.beta1 0 0.098 1.el5 x86_64 [] a base
-EOF
- @stdout_bad_type = <<EOF
-zip 0 2.31 2.el5 x86_64 ['zip = 2.31-2.el5'] r base
-zlib 0 1.2.3 3 x86_64 ['zlib = 1.2.3-3', 'libz.so.1()(64bit)'] c base
-zlib-devel 0 1.2.3 3 i386 [] a extras
-zlib-devel 0 1.2.3 3 x86_64 ['zlib-devel = 1.2.3-3'] bad installed
-znc-modtcl 0 0.098 1.el5 x86_64 [] a base
-EOF
-
- @stdout_bad_separators = <<EOF
-zip 0 2.31 2.el5 x86_64 ['zip = 2.31-2.el5'] r base
-zlib 0 1.2.3 3 x86_64 ['zlib = 1.2.3-3', 'libz.so.1()(64bit)'] i base bad
-zlib-devel 0 1.2.3 3 i386 [] a extras
-bad zlib-devel 0 1.2.3 3 x86_64 ['zlib-devel = 1.2.3-3'] i installed
-znc-modtcl 0 0.098 1.el5 x86_64 [] a base bad
-EOF
-
- @stdout_no_output = ""
-
- @stderr = <<EOF
-yum-dump Config Error: File contains no section headers.
-file: file://///etc/yum.repos.d/CentOS-Base.repo, line: 12
-'qeqwewe\n'
-EOF
- @status = double("Status", :exitstatus => 0, :stdin => @stdin, :stdout => @stdout_good, :stderr => @stderr)
- # new singleton each time
- Chef::Provider::Package::Yum::YumCache.reset_instance
- @yc = Chef::Provider::Package::Yum::YumCache.instance
- # load valid data
- @yc.yum_binary = "yum"
- allow(@yc).to receive(:shell_out!).and_return(@status)
- allow_any_instance_of(described_class).to receive(:which).with("yum").and_return("/usr/bin/yum")
- allow(::File).to receive(:open).with("/usr/bin/yum", "r") do |&block|
- res = block.call(yum_exe)
- # a bit of a hack. rewind this since it seem that no matter what
- # I do, we get the same StringIO objects on multiple calls to
- # ::File.open
- yum_exe.rewind; res
- end
- end
-
- describe "initialize" do
- it "should return a Chef::Provider::Package::Yum::YumCache object" do
- expect(@yc).to be_kind_of(Chef::Provider::Package::Yum::YumCache)
- end
-
- it "should register reload for start of Chef::Client runs" do
- Chef::Provider::Package::Yum::YumCache.reset_instance
- expect(Chef::Client).to receive(:when_run_starts) do |&b|
- expect(b).not_to be_nil
- end
- @yc = Chef::Provider::Package::Yum::YumCache.instance
- end
- end
-
- describe "python_bin" do
- it "should return the default python if an error occurs" do
- allow(::File).to receive(:open).with("/usr/bin/yum", "r").and_raise(StandardError)
- expect(@yc.python_bin).to eq("/usr/bin/python")
- end
-
- it "should return the default python if the yum-executable doesn't start with #!" do
- allow(::File).to receive(:open).with("/usr/bin/yum", "r") { |&b| r = b.call(bin_exe); bin_exe.rewind; r }
- expect(@yc.python_bin).to eq("/usr/bin/python")
- end
-
- it "should return /usr/bin/python if the interpreter is /bin/bash" do
- other = StringIO.new("#!/bin/bash\n# The yum executable redirecting to dnf from dnf-yum compatible package.")
- allow(::File).to receive(:open).with("/usr/bin/yum", "r") { |&b| r = b.call(other); other.rewind; r }
- expect(@yc.python_bin).to eq("/usr/bin/python")
- end
-
- it "should return the interpreter for yum" do
- other = StringIO.new("#!/usr/bin/super_python\n\nlasjdfdsaljf\nlasdjfs")
- allow(::File).to receive(:open).with("/usr/bin/yum", "r") { |&b| r = b.call(other); other.rewind; r }
- expect(@yc.python_bin).to eq("/usr/bin/super_python")
- end
- end
-
- describe "refresh" do
- it "should implicitly call yum-dump.py only once by default after being instantiated" do
- expect(@yc).to receive(:shell_out!).once
- @yc.installed_version("zlib")
- @yc.reset
- @yc.installed_version("zlib")
- end
-
- it "should run yum-dump.py using the system python when next_refresh is for :all" do
- @yc.reload
- expect(@yc).to receive(:shell_out!).with(%r{^/usr/bin/python .*/yum-dump.py --options --installed-provides --yum-lock-timeout 30$}, :timeout => Chef::Config[:yum_timeout])
- @yc.refresh
- end
-
- it "should run yum-dump.py with the installed flag when next_refresh is for :installed" do
- @yc.reload_installed
- expect(@yc).to receive(:shell_out!).with(%r{^/usr/bin/python .*/yum-dump.py --installed --yum-lock-timeout 30$}, :timeout => Chef::Config[:yum_timeout])
- @yc.refresh
- end
-
- it "should run yum-dump.py with the all-provides flag when next_refresh is for :provides" do
- @yc.reload_provides
- expect(@yc).to receive(:shell_out!).with(%r{^/usr/bin/python .*/yum-dump.py --options --all-provides --yum-lock-timeout 30$}, :timeout => Chef::Config[:yum_timeout])
- @yc.refresh
- end
-
- it "should pass extra_repo_control args to yum-dump.py" do
- @yc.enable_extra_repo_control("--enablerepo=foo --disablerepo=bar")
- expect(@yc).to receive(:shell_out!).with(%r{^/usr/bin/python .*/yum-dump.py --options --installed-provides --enablerepo=foo --disablerepo=bar --yum-lock-timeout 30$}, :timeout => Chef::Config[:yum_timeout])
- @yc.refresh
- end
-
- it "should pass extra_repo_control args and configured yum lock timeout to yum-dump.py" do
- Chef::Config[:yum_lock_timeout] = 999
- @yc.enable_extra_repo_control("--enablerepo=foo --disablerepo=bar")
- expect(@yc).to receive(:shell_out!).with(%r{^/usr/bin/python .*/yum-dump.py --options --installed-provides --enablerepo=foo --disablerepo=bar --yum-lock-timeout 999$}, :timeout => Chef::Config[:yum_timeout])
- @yc.refresh
- end
-
- it "should warn about invalid data with too many separators" do
- @status = double("Status", :exitstatus => 0, :stdin => @stdin, :stdout => @stdout_bad_separators, :stderr => @stderr)
- allow(@yc).to receive(:shell_out!).and_return(@status)
- expect(Chef::Log).to receive(:warn).exactly(3).times.with(%r{Problem parsing})
- @yc.refresh
- end
-
- it "should warn about invalid data with an incorrect type" do
- @status = double("Status", :exitstatus => 0, :stdin => @stdin, :stdout => @stdout_bad_type, :stderr => @stderr)
- allow(@yc).to receive(:shell_out!).and_return(@status)
- expect(Chef::Log).to receive(:warn).exactly(2).times.with(%r{Problem parsing})
- @yc.refresh
- end
-
- it "should warn about no output from yum-dump.py" do
- @status = double("Status", :exitstatus => 0, :stdin => @stdin, :stdout => @stdout_no_output, :stderr => @stderr)
- allow(@yc).to receive(:shell_out!).and_return(@status)
- expect(Chef::Log).to receive(:warn).exactly(1).times.with(%r{no output from yum-dump.py})
- @yc.refresh
- end
-
- it "should raise exception yum-dump.py exits with a non zero status" do
- @status = double("Status", :exitstatus => 1, :stdin => @stdin, :stdout => @stdout_no_output, :stderr => @stderr)
- allow(@yc).to receive(:shell_out!).and_return(@status)
- expect { @yc.refresh }.to raise_error(Chef::Exceptions::Package, %r{CentOS-Base.repo, line: 12})
- end
-
- it "should parse type 'i' into an installed state for a package" do
- expect(@yc.available_version("erlang-mochiweb")).to eq(nil)
- expect(@yc.installed_version("erlang-mochiweb")).not_to eq(nil)
- end
-
- it "should parse type 'a' into an available state for a package" do
- expect(@yc.available_version("znc")).not_to eq(nil)
- expect(@yc.installed_version("znc")).to eq(nil)
- end
-
- it "should parse type 'r' into an installed and available states for a package" do
- expect(@yc.available_version("zip")).not_to eq(nil)
- expect(@yc.installed_version("zip")).not_to eq(nil)
- end
-
- it "should parse installonlypkgs from yum-dump.py options output" do
- expect(@yc.allow_multi_install).to eq(%w{kernel kernel-bigmem kernel-enterprise})
- end
- end
-
- describe "installed_version" do
- it "should take one or two arguments" do
- expect { @yc.installed_version("zip") }.not_to raise_error
- expect { @yc.installed_version("zip", "i386") }.not_to raise_error
- expect { @yc.installed_version("zip", "i386", "extra") }.to raise_error(ArgumentError)
- end
-
- it "should return version-release for matching package regardless of arch" do
- expect(@yc.installed_version("zip", "x86_64")).to eq("2.31-2.el5")
- expect(@yc.installed_version("zip", nil)).to eq("2.31-2.el5")
- end
-
- it "should return version-release for matching package and arch" do
- expect(@yc.installed_version("zip", "x86_64")).to eq("2.31-2.el5")
- expect(@yc.installed_version("zisofs-tools", "i386")).to eq(nil)
- end
-
- it "should return nil for an unmatched package" do
- expect(@yc.installed_version(nil, nil)).to eq(nil)
- expect(@yc.installed_version("test1", nil)).to eq(nil)
- expect(@yc.installed_version("test2", "x86_64")).to eq(nil)
- end
- end
-
- describe "available_version" do
- it "should take one or two arguments" do
- expect { @yc.available_version("zisofs-tools") }.not_to raise_error
- expect { @yc.available_version("zisofs-tools", "i386") }.not_to raise_error
- expect { @yc.available_version("zisofs-tools", "i386", "extra") }.to raise_error(ArgumentError)
- end
-
- it "should return version-release for matching package regardless of arch" do
- expect(@yc.available_version("zip", "x86_64")).to eq("2.31-2.el5")
- expect(@yc.available_version("zip", nil)).to eq("2.31-2.el5")
- end
-
- it "should return version-release for matching package and arch" do
- expect(@yc.available_version("zip", "x86_64")).to eq("2.31-2.el5")
- expect(@yc.available_version("zisofs-tools", "i386")).to eq(nil)
- end
-
- it "should return nil for an unmatched package" do
- expect(@yc.available_version(nil, nil)).to eq(nil)
- expect(@yc.available_version("test1", nil)).to eq(nil)
- expect(@yc.available_version("test2", "x86_64")).to eq(nil)
- end
- end
-
- describe "version_available?" do
- it "should take two or three arguments" do
- expect { @yc.version_available?("zisofs-tools") }.to raise_error(ArgumentError)
- expect { @yc.version_available?("zisofs-tools", "1.0.6-3.2.2") }.not_to raise_error
- expect { @yc.version_available?("zisofs-tools", "1.0.6-3.2.2", "x86_64") }.not_to raise_error
- end
-
- it "should return true if our package-version-arch is available" do
- expect(@yc.version_available?("zisofs-tools", "1.0.6-3.2.2", "x86_64")).to eq(true)
- end
-
- it "should return true if our package-version, no arch, is available" do
- expect(@yc.version_available?("zisofs-tools", "1.0.6-3.2.2", nil)).to eq(true)
- expect(@yc.version_available?("zisofs-tools", "1.0.6-3.2.2")).to eq(true)
- end
-
- it "should return false if our package-version-arch isn't available" do
- expect(@yc.version_available?("zisofs-tools", "1.0.6-3.2.2", "pretend")).to eq(false)
- expect(@yc.version_available?("zisofs-tools", "pretend", "x86_64")).to eq(false)
- expect(@yc.version_available?("pretend", "1.0.6-3.2.2", "x86_64")).to eq(false)
- end
-
- it "should return false if our package-version, no arch, isn't available" do
- expect(@yc.version_available?("zisofs-tools", "pretend", nil)).to eq(false)
- expect(@yc.version_available?("zisofs-tools", "pretend")).to eq(false)
- expect(@yc.version_available?("pretend", "1.0.6-3.2.2")).to eq(false)
- end
- end
-
- describe "package_repository" do
- it "should take two or three arguments" do
- expect { @yc.package_repository("zisofs-tools") }.to raise_error(ArgumentError)
- expect { @yc.package_repository("zisofs-tools", "1.0.6-3.2.2") }.not_to raise_error
- expect { @yc.package_repository("zisofs-tools", "1.0.6-3.2.2", "x86_64") }.not_to raise_error
- end
-
- it "should return repoid for package-version-arch" do
- expect(@yc.package_repository("zlib-devel", "1.2.3-3", "i386")).to eq("extras")
- expect(@yc.package_repository("zlib-devel", "1.2.3-3", "x86_64")).to eq("base")
- end
-
- it "should return repoid for package-version, no arch" do
- expect(@yc.package_repository("zisofs-tools", "1.0.6-3.2.2", nil)).to eq("extras")
- expect(@yc.package_repository("zisofs-tools", "1.0.6-3.2.2")).to eq("extras")
- end
-
- it "should return nil when no match for package-version-arch" do
- expect(@yc.package_repository("zisofs-tools", "1.0.6-3.2.2", "pretend")).to eq(nil)
- expect(@yc.package_repository("zisofs-tools", "pretend", "x86_64")).to eq(nil)
- expect(@yc.package_repository("pretend", "1.0.6-3.2.2", "x86_64")).to eq(nil)
- end
-
- it "should return nil when no match for package-version, no arch" do
- expect(@yc.package_repository("zisofs-tools", "pretend", nil)).to eq(nil)
- expect(@yc.package_repository("zisofs-tools", "pretend")).to eq(nil)
- expect(@yc.package_repository("pretend", "1.0.6-3.2.2")).to eq(nil)
- end
- end
-
- describe "reset" do
- it "should empty the installed and available packages RPMDb" do
- expect(@yc.available_version("zip", "x86_64")).to eq("2.31-2.el5")
- expect(@yc.installed_version("zip", "x86_64")).to eq("2.31-2.el5")
- @yc.reset
- expect(@yc.available_version("zip", "x86_64")).to eq(nil)
- expect(@yc.installed_version("zip", "x86_64")).to eq(nil)
- end
- end
-
- describe "package_available?" do
- it "should return true a package name is available" do
- expect(@yc.package_available?("zisofs-tools")).to eq(true)
- expect(@yc.package_available?("moo")).to eq(false)
- expect(@yc.package_available?(nil)).to eq(false)
- end
-
- it "should return true a package name + arch is available" do
- expect(@yc.package_available?("zlib-devel.i386")).to eq(true)
- expect(@yc.package_available?("zisofs-tools.x86_64")).to eq(true)
- expect(@yc.package_available?("znc-test.beta1.x86_64")).to eq(true)
- expect(@yc.package_available?("znc-test.beta1")).to eq(true)
- expect(@yc.package_available?("znc-test.test.beta1")).to eq(true)
- expect(@yc.package_available?("moo.i386")).to eq(false)
- expect(@yc.package_available?("zisofs-tools.beta")).to eq(false)
- expect(@yc.package_available?("znc-test.test")).to eq(false)
- end
- end
-
- describe "enable_extra_repo_control" do
- it "should set @extra_repo_control to arg" do
- @yc.enable_extra_repo_control("--enablerepo=test")
- expect(@yc.extra_repo_control).to eq("--enablerepo=test")
- end
-
- it "should call reload once when set to flag cache for update" do
- expect(@yc).to receive(:reload).once
- @yc.enable_extra_repo_control("--enablerepo=test")
- @yc.enable_extra_repo_control("--enablerepo=test")
- end
- end
-
- describe "disable_extra_repo_control" do
- it "should set @extra_repo_control to nil" do
- @yc.enable_extra_repo_control("--enablerepo=test")
- @yc.disable_extra_repo_control
- expect(@yc.extra_repo_control).to eq(nil)
- end
-
- it "should call reload once when cleared to flag cache for update" do
- expect(@yc).to receive(:reload).once
- @yc.enable_extra_repo_control("--enablerepo=test")
- expect(@yc).to receive(:reload).once
- @yc.disable_extra_repo_control
- @yc.disable_extra_repo_control
- end
- end
-
-end
-
-describe "Chef::Provider::Package::Yum - Multi" do
- before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::Package.new(%w{cups vim})
- @status = double("Status", :exitstatus => 0)
- @yum_cache = double(
- "Chef::Provider::Yum::YumCache",
- :reload_installed => true,
- :reset => true,
- :installed_version => "XXXX",
- :candidate_version => "YYYY",
- :package_available? => true,
- :version_available? => true,
- :allow_multi_install => [ "kernel" ],
- :package_repository => "base",
- :disable_extra_repo_control => true
- )
- allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- allow(@yum_cache).to receive(:yum_binary=).with("yum")
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- @pid = double("PID")
- end
-
- describe "when loading the current system state" do
- it "should create a current resource with the name of the new_resource" do
- @provider.load_current_resource
- expect(@provider.current_resource.name).to eq("cups, vim")
- end
-
- it "should set the current resources package name to the new resources package name" do
- @provider.load_current_resource
- expect(@provider.current_resource.package_name).to eq(%w{cups vim})
- end
-
- it "should set the installed version to nil on the current resource if no installed package" do
- allow(@yum_cache).to receive(:installed_version).and_return(nil)
- @provider.load_current_resource
- expect(@provider.current_resource.version).to eq([nil, nil])
- end
-
- it "should set the installed version if yum has one" do
- allow(@yum_cache).to receive(:installed_version).with("cups", nil).and_return("1.2.4-11.18.el5")
- allow(@yum_cache).to receive(:installed_version).with("vim", nil).and_return("1.0")
- allow(@yum_cache).to receive(:candidate_version).with("cups", nil).and_return("1.2.4-11.18.el5_2.3")
- allow(@yum_cache).to receive(:candidate_version).with("vim", nil).and_return("1.5")
- @provider.load_current_resource
- expect(@provider.current_resource.version).to eq(["1.2.4-11.18.el5", "1.0"])
- end
-
- it "should set the candidate version if yum info has one" do
- allow(@yum_cache).to receive(:installed_version).with("cups", nil).and_return("1.2.4-11.18.el5")
- allow(@yum_cache).to receive(:installed_version).with("vim", nil).and_return("1.0")
- allow(@yum_cache).to receive(:candidate_version).with("cups", nil).and_return("1.2.4-11.18.el5_2.3")
- allow(@yum_cache).to receive(:candidate_version).with("vim", nil).and_return("1.5")
- @provider.load_current_resource
- expect(@provider.candidate_version).to eql(["1.2.4-11.18.el5_2.3", "1.5"])
- end
-
- it "should return the current resouce" do
- expect(@provider.load_current_resource).to eql(@provider.current_resource)
- end
-
- describe "when version constraint in package_name" do
- it "should set package_version if no existing package_name is found and new_package_name is available" do
- @new_resource = Chef::Resource::Package.new(["cups = 1.2.4-11.18.el5_2.3", "emacs = 24.4"])
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- allow(@yum_cache).to receive(:package_available?) { |pkg| %w{cups emacs}.include?(pkg) ? true : false }
- allow(@yum_cache).to receive(:candidate_version) do |pkg|
- if pkg == "cups"
- "1.2.4-11.18.el5_2.3"
- elsif pkg == "emacs"
- "24.4"
- end
- end
- allow(@yum_cache).to receive(:packages_from_require) do |pkg|
- if pkg.name == "cups"
- [Chef::Provider::Package::Yum::RPMDbPackage.new("cups", "1.2.4-11.18.el5_2.3", "noarch", [], false, true, "base")]
- elsif pkg.name == "emacs"
- [Chef::Provider::Package::Yum::RPMDbPackage.new("emacs", "24.4", "noarch", [], false, true, "base")]
- end
- end
- expect(Chef::Log).to receive(:debug).exactly(2).times.with(%r{matched 1 package,})
- expect(Chef::Log).to receive(:debug).exactly(1).times.with(%r{candidate version: \["1.2.4-11.18.el5_2.3", "24.4"\]})
- expect(Chef::Log).to receive(:debug).at_least(2).times.with(%r{checking yum info})
- @provider.load_current_resource
- expect(@provider.new_resource.package_name).to eq(%w{cups emacs})
- expect(@provider.new_resource.version).to eq(["1.2.4-11.18.el5_2.3", "24.4"])
- expect(@provider.send(:package_name_array)).to eq(%w{cups emacs})
- expect(@provider.send(:new_version_array)).to eq(["1.2.4-11.18.el5_2.3", "24.4"])
- end
- end
- end
-
- describe "when installing a package" do
- it "should run yum install with the package name and version" do
- @provider.load_current_resource
- allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
- allow(@yum_cache).to receive(:installed_version).with("cups", nil).and_return("1.2.4-11.18.el5")
- allow(@yum_cache).to receive(:installed_version).with("vim", nil).and_return("0.9")
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y install cups-1.2.4-11.19.el5 vim-1.0"
- )
- @provider.install_package(%w{cups vim}, ["1.2.4-11.19.el5", "1.0"])
- end
-
- it "should run yum install with the package name, version and arch" do
- @provider.load_current_resource
- allow(@new_resource).to receive(:arch).and_return("i386")
- allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y install cups-1.2.4-11.19.el5.i386 vim-1.0.i386"
- )
- @provider.install_package(%w{cups vim}, ["1.2.4-11.19.el5", "1.0"])
- end
-
- it "installs the package with the options given in the resource" do
- @provider.load_current_resource
- allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
- allow(@yum_cache).to receive(:installed_version).with("cups", nil).and_return("1.2.4-11.18.el5")
- allow(@yum_cache).to receive(:installed_version).with("vim", nil).and_return("0.9")
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y --disablerepo epmd install cups-1.2.4-11.19.el5 vim-1.0"
- )
- allow(@new_resource).to receive(:options).and_return("--disablerepo epmd")
- @provider.install_package(%w{cups vim}, ["1.2.4-11.19.el5", "1.0"])
- end
-
- it "should run yum install with the package name and version when name has arch" do
- @new_resource = Chef::Resource::Package.new(["cups.x86_64", "vim"])
- @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
-
- # Inside of load_current_resource() we'll call parse_arch for cups,
- # and we need to craft the right response. The default mock setup above
- # will just return valid versions all the time which won't work for this
- # test.
- allow(@yum_cache).to receive(:installed_version).with("cups", "x86_64").and_return("XXXX")
- allow(@yum_cache).to receive(:candidate_version).with("cups", "x86_64").and_return("1.2.4-11.18.el5")
- allow(@yum_cache).to receive(:installed_version).with("cups.x86_64").and_return(nil)
- allow(@yum_cache).to receive(:candidate_version).with("cups.x86_64").and_return(nil)
-
- # Normal mock's for the idempotency check
- allow(@yum_cache).to receive(:installed_version).with("cups", nil).and_return("1.2.4-11.18.el5")
- allow(@yum_cache).to receive(:installed_version).with("vim", nil).and_return("0.9")
-
- @provider.load_current_resource
- expect(@provider).to receive(:yum_command).with(
- "-d0 -e0 -y install cups-1.2.4-11.19.el5.x86_64 vim-1.0"
- )
- @provider.install_package(%w{cups vim}, ["1.2.4-11.19.el5", "1.0"])
- end
-
- end
-end
diff --git a/spec/unit/provider/package/zypper_spec.rb b/spec/unit/provider/package/zypper_spec.rb
index df0a1da9a2..819278a795 100644
--- a/spec/unit/provider/package/zypper_spec.rb
+++ b/spec/unit/provider/package/zypper_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -38,14 +38,14 @@ describe Chef::Provider::Package::Zypper do
allow(provider).to receive(:`).and_return("2.0")
end
- def shell_out_expectation(command, options = nil)
- options ||= { timeout: 900 }
- expect(provider).to receive(:shell_out).with(command, options)
+ def shell_out_expectation(*command, **options)
+ options[:timeout] ||= 900
+ expect(provider).to receive(:shell_out).with(*command, **options)
end
- def shell_out_expectation!(command, options = nil)
- options ||= { timeout: 900 }
- expect(provider).to receive(:shell_out!).with(command, options)
+ def shell_out_expectation!(*command, **options)
+ options[:timeout] ||= 900
+ expect(provider).to receive(:shell_out!).with(*command, **options)
end
describe "when loading the current package state" do
@@ -61,7 +61,7 @@ describe Chef::Provider::Package::Zypper do
it "should run zypper info with the package name" do
shell_out_expectation!(
- "zypper --non-interactive info #{new_resource.package_name}"
+ "zypper", "--non-interactive", "info", new_resource.package_name
).and_return(status)
provider.load_current_resource
end
@@ -72,7 +72,7 @@ describe Chef::Provider::Package::Zypper do
provider.load_current_resource
end
- it "should set the installed version if zypper info has one" do
+ it "should set the installed version if zypper info has one (zypper version < 1.13.0)" do
status = double(:stdout => "Version: 1.0\nInstalled: Yes\n", :exitstatus => 0)
allow(provider).to receive(:shell_out!).and_return(status)
@@ -80,7 +80,23 @@ describe Chef::Provider::Package::Zypper do
provider.load_current_resource
end
- it "should set the candidate version if zypper info has one" do
+ it "should set the installed version if zypper info has one (zypper version >= 1.13.0)" do
+ status = double(:stdout => "Version : 1.0 \nInstalled : Yes \n", :exitstatus => 0)
+
+ allow(provider).to receive(:shell_out!).and_return(status)
+ expect(current_resource).to receive(:version).with(["1.0"]).and_return(true)
+ provider.load_current_resource
+ end
+
+ it "should set the installed version if zypper info has one (zypper version >= 1.13.17)" do
+ status = double(:stdout => "Version : 1.0\nInstalled : Yes (automatically)\n", :exitstatus => 0)
+
+ allow(provider).to receive(:shell_out!).and_return(status)
+ expect(current_resource).to receive(:version).with(["1.0"]).and_return(true)
+ provider.load_current_resource
+ end
+
+ it "should set the candidate version if zypper info has one (zypper version < 1.13.0)" do
status = double(:stdout => "Version: 1.0\nInstalled: No\nStatus: out-of-date (version 0.9 installed)", :exitstatus => 0)
allow(provider).to receive(:shell_out!).and_return(status)
@@ -88,6 +104,14 @@ describe Chef::Provider::Package::Zypper do
expect(provider.candidate_version).to eql(["1.0"])
end
+ it "should set the candidate version if zypper info has one (zypper version >= 1.13.0)" do
+ status = double(:stdout => "Version : 1.0 \nInstalled : No \nStatus : out-of-date (version 0.9 installed)", :exitstatus => 0)
+
+ allow(provider).to receive(:shell_out!).and_return(status)
+ provider.load_current_resource
+ expect(provider.candidate_version).to eql(["1.0"])
+ end
+
it "should return the current resouce" do
expect(provider.load_current_resource).to eql(current_resource)
end
@@ -95,25 +119,47 @@ describe Chef::Provider::Package::Zypper do
describe "install_package" do
it "should run zypper install with the package name and version" do
- allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(true)
shell_out_expectation!(
- "zypper --non-interactive install --auto-agree-with-licenses emacs=1.0"
+ "zypper", "--non-interactive", "install", "--auto-agree-with-licenses", "emacs=1.0"
+ )
+ provider.install_package(["emacs"], ["1.0"])
+ end
+
+ it "should run zypper install with gpg checks" do
+ shell_out_expectation!(
+ "zypper", "--non-interactive", "install", "--auto-agree-with-licenses", "emacs=1.0"
+ )
+ provider.install_package(["emacs"], ["1.0"])
+ end
+
+ it "setting the property should disable gpg checks" do
+ new_resource.gpg_check false
+ shell_out_expectation!(
+ "zypper", "--non-interactive", "--no-gpg-checks", "install", "--auto-agree-with-licenses", "emacs=1.0"
)
provider.install_package(["emacs"], ["1.0"])
end
- it "should run zypper install without gpg checks" do
- allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false)
+
+ it "setting the config variable should disable gpg checks" do
+ Chef::Config[:zypper_check_gpg] = false
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks install " + "--auto-agree-with-licenses emacs=1.0"
+ "zypper", "--non-interactive", "--no-gpg-checks", "install", "--auto-agree-with-licenses", "emacs=1.0"
)
provider.install_package(["emacs"], ["1.0"])
end
- it "should warn about gpg checks on zypper install" do
- expect(Chef::Log).to receive(:warn).with(
- /All packages will be installed without gpg signature checks/
+
+ it "setting the property should allow downgrade" do
+ new_resource.allow_downgrade true
+ shell_out_expectation!(
+ "zypper", "--non-interactive", "install", "--auto-agree-with-licenses", "--oldpackage", "emacs=1.0"
)
+ provider.install_package(["emacs"], ["1.0"])
+ end
+
+ it "should add user provided options to the command" do
+ new_resource.options "--user-provided"
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks install " + "--auto-agree-with-licenses emacs=1.0"
+ "zypper", "--non-interactive", "install", "--user-provided", "--auto-agree-with-licenses", "emacs=1.0"
)
provider.install_package(["emacs"], ["1.0"])
end
@@ -121,31 +167,29 @@ describe Chef::Provider::Package::Zypper do
describe "upgrade_package" do
it "should run zypper update with the package name and version" do
- allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(true)
shell_out_expectation!(
- "zypper --non-interactive install --auto-agree-with-licenses emacs=1.0"
+ "zypper", "--non-interactive", "install", "--auto-agree-with-licenses", "emacs=1.0"
)
provider.upgrade_package(["emacs"], ["1.0"])
end
- it "should run zypper update without gpg checks" do
- allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false)
+ it "should run zypper update without gpg checks when setting the property" do
+ new_resource.gpg_check false
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks install " + "--auto-agree-with-licenses emacs=1.0"
+ "zypper", "--non-interactive", "--no-gpg-checks", "install", "--auto-agree-with-licenses", "emacs=1.0"
)
provider.upgrade_package(["emacs"], ["1.0"])
end
- it "should warn about gpg checks on zypper upgrade" do
- expect(Chef::Log).to receive(:warn).with(
- /All packages will be installed without gpg signature checks/
- )
+ it "should run zypper update without gpg checks when setting the config variable" do
+ Chef::Config[:zypper_check_gpg] = false
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks install " + "--auto-agree-with-licenses emacs=1.0"
+ "zypper", "--non-interactive", "--no-gpg-checks", "install", "--auto-agree-with-licenses", "emacs=1.0"
)
provider.upgrade_package(["emacs"], ["1.0"])
end
- it "should run zypper upgrade without gpg checks" do
+ it "should add user provided options to the command" do
+ new_resource.options "--user-provided"
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks install " + "--auto-agree-with-licenses emacs=1.0"
+ "zypper", "--non-interactive", "install", "--user-provided", "--auto-agree-with-licenses", "emacs=1.0"
)
provider.upgrade_package(["emacs"], ["1.0"])
end
@@ -155,9 +199,8 @@ describe Chef::Provider::Package::Zypper do
context "when package version is not explicitly specified" do
it "should run zypper remove with the package name" do
- allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(true)
shell_out_expectation!(
- "zypper --non-interactive remove emacs"
+ "zypper", "--non-interactive", "remove", "emacs"
)
provider.remove_package(["emacs"], [nil])
end
@@ -165,25 +208,29 @@ describe Chef::Provider::Package::Zypper do
context "when package version is explicitly specified" do
it "should run zypper remove with the package name" do
- allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(true)
shell_out_expectation!(
- "zypper --non-interactive remove emacs=1.0"
+ "zypper", "--non-interactive", "remove", "emacs=1.0"
)
provider.remove_package(["emacs"], ["1.0"])
end
it "should run zypper remove without gpg checks" do
- allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false)
+ new_resource.gpg_check false
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks remove emacs=1.0"
+ "zypper", "--non-interactive", "--no-gpg-checks", "remove", "emacs=1.0"
)
provider.remove_package(["emacs"], ["1.0"])
end
- it "should warn about gpg checks on zypper remove" do
- expect(Chef::Log).to receive(:warn).with(
- /All packages will be installed without gpg signature checks/
+ it "should run zypper remove without gpg checks when the config is false" do
+ Chef::Config[:zypper_check_gpg] = false
+ shell_out_expectation!(
+ "zypper", "--non-interactive", "--no-gpg-checks", "remove", "emacs=1.0"
)
+ provider.remove_package(["emacs"], ["1.0"])
+ end
+ it "should add user provided options to the command" do
+ new_resource.options "--user-provided"
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks remove emacs=1.0"
+ "zypper", "--non-interactive", "remove", "--user-provided", "emacs=1.0"
)
provider.remove_package(["emacs"], ["1.0"])
end
@@ -193,28 +240,146 @@ describe Chef::Provider::Package::Zypper do
describe "purge_package" do
it "should run remove with the name and version and --clean-deps" do
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks remove --clean-deps emacs=1.0"
+ "zypper", "--non-interactive", "remove", "--clean-deps", "emacs=1.0"
)
provider.purge_package(["emacs"], ["1.0"])
end
it "should run zypper purge without gpg checks" do
- allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false)
+ new_resource.gpg_check false
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks remove --clean-deps emacs=1.0"
+ "zypper", "--non-interactive", "--no-gpg-checks", "remove", "--clean-deps", "emacs=1.0"
)
provider.purge_package(["emacs"], ["1.0"])
end
- it "should warn about gpg checks on zypper purge" do
- expect(Chef::Log).to receive(:warn).with(
- /All packages will be installed without gpg signature checks/
+ it "should run zypper purge without gpg checks when the config is false" do
+ Chef::Config[:zypper_check_gpg] = false
+ shell_out_expectation!(
+ "zypper", "--non-interactive", "--no-gpg-checks", "remove", "--clean-deps", "emacs=1.0"
)
+ provider.purge_package(["emacs"], ["1.0"])
+ end
+ it "should add user provided options to the command" do
+ new_resource.options "--user-provided"
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks remove --clean-deps emacs=1.0"
+ "zypper", "--non-interactive", "remove", "--user-provided", "--clean-deps", "emacs=1.0"
)
provider.purge_package(["emacs"], ["1.0"])
end
end
+ describe "action_lock" do
+ it "should lock if the package is not already locked" do
+ prov = provider
+ allow(prov).to receive(:shell_out_compact_timeout!).with(
+ "zypper", "--non-interactive", "info", new_resource.package_name
+ ).and_return(status)
+ allow(prov).to receive(:shell_out_compact_timeout!).with(
+ "zypper", "locks"
+ ).and_return(instance_double(
+ Mixlib::ShellOut, stdout: "1 | somethingelse | package | (any)"
+ ))
+ expect(prov).to receive(:lock_package).with(["cups"], [nil])
+
+ prov.load_current_resource
+ prov.action_lock
+ end
+
+ it "should not lock if the package is already locked" do
+ prov = provider
+ allow(prov).to receive(:shell_out_compact_timeout!).with(
+ "zypper", "--non-interactive", "info", new_resource.package_name
+ ).and_return(status)
+ allow(prov).to receive(:shell_out_compact_timeout!).with(
+ "zypper", "locks"
+ ).and_return(instance_double(
+ Mixlib::ShellOut, stdout: "1 | cups | package | (any)"
+ ))
+ expect(prov).to_not receive(:lock_package)
+
+ prov.load_current_resource
+ prov.action_lock
+ end
+ end
+
+ describe "lock_package" do
+ it "should run zypper addlock with the package name" do
+ shell_out_expectation!(
+ "zypper", "--non-interactive", "addlock", "emacs"
+ )
+ provider.lock_package(["emacs"], [nil])
+ end
+ it "should run zypper addlock without gpg checks" do
+ new_resource.gpg_check false
+ shell_out_expectation!(
+ "zypper", "--non-interactive", "--no-gpg-checks", "addlock", "emacs"
+ )
+ provider.lock_package(["emacs"], [nil])
+ end
+ it "should add user provided options to the command" do
+ new_resource.options "--user-provided"
+ shell_out_expectation!(
+ "zypper", "--non-interactive", "addlock", "--user-provided", "emacs"
+ )
+ provider.lock_package(["emacs"], [nil])
+ end
+ end
+
+ describe "action_unlock" do
+ it "should unlock if the package is not already unlocked" do
+ prov = provider
+ allow(prov).to receive(:shell_out_compact_timeout!).with(
+ "zypper", "--non-interactive", "info", new_resource.package_name
+ ).and_return(status)
+ allow(prov).to receive(:shell_out_compact_timeout!).with(
+ "zypper", "locks"
+ ).and_return(instance_double(
+ Mixlib::ShellOut, stdout: "1 | cups | package | (any)"
+ ))
+ expect(prov).to receive(:unlock_package).with(["cups"], [nil])
+
+ prov.load_current_resource
+ provider.action_unlock
+ end
+ it "should not unlock if the package is already unlocked" do
+ prov = provider
+ allow(prov).to receive(:shell_out_compact_timeout!).with(
+ "zypper", "--non-interactive", "info", new_resource.package_name
+ ).and_return(status)
+ allow(prov).to receive(:shell_out_compact_timeout!).with(
+ "zypper", "locks"
+ ).and_return(instance_double(
+ Mixlib::ShellOut, stdout: "1 | somethingelse | package | (any)"
+ ))
+ expect(prov).to_not receive(:unlock_package)
+
+ prov.load_current_resource
+ provider.action_unlock
+ end
+ end
+
+ describe "unlock_package" do
+ it "should run zypper removelock with the package name" do
+ shell_out_expectation!(
+ "zypper", "--non-interactive", "removelock", "emacs"
+ )
+ provider.unlock_package(["emacs"], [nil])
+ end
+ it "should run zypper removelock without gpg checks" do
+ new_resource.gpg_check false
+ shell_out_expectation!(
+ "zypper", "--non-interactive", "--no-gpg-checks", "removelock", "emacs"
+ )
+ provider.unlock_package(["emacs"], [nil])
+ end
+ it "should add user provided options to the command" do
+ new_resource.options "--user-provided"
+ shell_out_expectation!(
+ "zypper", "--non-interactive", "removelock", "--user-provided", "emacs"
+ )
+ provider.unlock_package(["emacs"], [nil])
+ end
+ end
+
describe "on an older zypper" do
before(:each) do
allow(provider).to receive(:`).and_return("0.11.6")
@@ -223,7 +388,7 @@ describe Chef::Provider::Package::Zypper do
describe "install_package" do
it "should run zypper install with the package name and version" do
shell_out_expectation!(
- "zypper --no-gpg-checks install --auto-agree-with-licenses -y emacs"
+ "zypper", "install", "--auto-agree-with-licenses", "-y", "emacs"
)
provider.install_package(["emacs"], ["1.0"])
end
@@ -232,7 +397,7 @@ describe Chef::Provider::Package::Zypper do
describe "upgrade_package" do
it "should run zypper update with the package name and version" do
shell_out_expectation!(
- "zypper --no-gpg-checks install --auto-agree-with-licenses -y emacs"
+ "zypper", "install", "--auto-agree-with-licenses", "-y", "emacs"
)
provider.upgrade_package(["emacs"], ["1.0"])
end
@@ -241,7 +406,7 @@ describe Chef::Provider::Package::Zypper do
describe "remove_package" do
it "should run zypper remove with the package name" do
shell_out_expectation!(
- "zypper --no-gpg-checks remove -y emacs"
+ "zypper", "remove", "-y", "emacs"
)
provider.remove_package(["emacs"], ["1.0"])
end
@@ -250,17 +415,15 @@ describe Chef::Provider::Package::Zypper do
describe "when installing multiple packages" do # https://github.com/chef/chef/issues/3570
it "should install an array of package names and versions" do
- allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false)
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks install " + "--auto-agree-with-licenses emacs=1.0 vim=2.0"
+ "zypper", "--non-interactive", "install", "--auto-agree-with-licenses", "emacs=1.0", "vim=2.0"
)
provider.install_package(%w{emacs vim}, ["1.0", "2.0"])
end
it "should remove an array of package names and versions" do
- allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false)
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks remove emacs=1.0 vim=2.0"
+ "zypper", "--non-interactive", "remove", "emacs=1.0", "vim=2.0"
)
provider.remove_package(%w{emacs vim}, ["1.0", "2.0"])
end
diff --git a/spec/unit/provider/package_spec.rb b/spec/unit/provider/package_spec.rb
index abf0322868..da74c932a8 100644
--- a/spec/unit/provider/package_spec.rb
+++ b/spec/unit/provider/package_spec.rb
@@ -26,17 +26,24 @@ describe Chef::Provider::Package do
node
end
let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
let(:run_context) { Chef::RunContext.new(node, {}, events) }
- let(:new_resource) { Chef::Resource::Package.new("emacs") }
- let(:current_resource) { Chef::Resource::Package.new("emacs") }
+ let(:new_resource) { Chef::Resource::Package.new("install emacs") }
+ let(:current_resource) { Chef::Resource::Package.new("install emacs") }
let(:candidate_version) { "1.0" }
let(:provider) do
+ new_resource.package_name = "emacs"
+ current_resource.package_name = "emacs"
provider = Chef::Provider::Package.new(new_resource, run_context)
provider.current_resource = current_resource
provider.candidate_version = candidate_version
provider
end
+ before do
+ allow(run_context).to receive(:logger).and_return(logger)
+ end
+
describe "when installing a package" do
before(:each) do
provider.current_resource = current_resource
@@ -57,7 +64,7 @@ describe Chef::Provider::Package do
it "should call preseed_package if a response_file is given" do
new_resource.response_file("foo")
expect(provider).to receive(:get_preseed_file).with(
- new_resource.name,
+ new_resource.package_name,
provider.candidate_version
).and_return("/var/cache/preseed-test")
@@ -74,7 +81,7 @@ describe Chef::Provider::Package do
it "should install the package at the candidate_version if it is not already installed" do
expect(provider).to receive(:install_package).with(
- new_resource.name,
+ new_resource.package_name,
provider.candidate_version
).and_return(true)
provider.run_action(:install)
@@ -84,7 +91,7 @@ describe Chef::Provider::Package do
it "should install the package at the version specified if it is not already installed" do
new_resource.version("1.0")
expect(provider).to receive(:install_package).with(
- new_resource.name,
+ new_resource.package_name,
new_resource.version
).and_return(true)
provider.run_action(:install)
@@ -95,7 +102,7 @@ describe Chef::Provider::Package do
new_resource.version("1.0")
allow(current_resource).to receive(:version).and_return("0.99")
expect(provider).to receive(:install_package).with(
- new_resource.name,
+ new_resource.package_name,
new_resource.version
).and_return(true)
provider.run_action(:install)
@@ -143,7 +150,7 @@ describe Chef::Provider::Package do
it "should upgrade the package if the current version is not the candidate version" do
expect(provider).to receive(:upgrade_package).with(
- new_resource.name,
+ new_resource.package_name,
provider.candidate_version
).and_return(true)
provider.run_action(:upgrade)
@@ -164,7 +171,7 @@ describe Chef::Provider::Package do
it "should print the word 'uninstalled' if there was no original version" do
allow(current_resource).to receive(:version).and_return(nil)
- expect(Chef::Log).to receive(:info).with("package[emacs] upgraded emacs to 1.0")
+ expect(logger).to receive(:info).with("package[install emacs] upgraded emacs to 1.0")
provider.run_action(:upgrade)
expect(new_resource).to be_updated_by_last_action
end
@@ -285,7 +292,7 @@ describe Chef::Provider::Package do
expect(provider).to receive(:get_preseed_file).and_return("/var/cache/preseed-test")
allow(provider).to receive(:preseed_package).and_return(true)
allow(provider).to receive(:reconfig_package).and_return(true)
- expect(Chef::Log).to receive(:info).with("package[emacs] reconfigured")
+ expect(logger).to receive(:info).with("package[install emacs] reconfigured")
expect(provider).to receive(:reconfig_package)
provider.run_action(:reconfig)
expect(new_resource).to be_updated
@@ -294,7 +301,7 @@ describe Chef::Provider::Package do
it "should debug log and not reconfigure the package if the package is not installed" do
allow(current_resource).to receive(:version).and_return(nil)
- expect(Chef::Log).to receive(:debug).with("package[emacs] is NOT installed - nothing to do")
+ expect(logger).to receive(:trace).with("package[install emacs] is NOT installed - nothing to do")
expect(provider).not_to receive(:reconfig_package)
provider.run_action(:reconfig)
expect(new_resource).not_to be_updated_by_last_action
@@ -303,7 +310,7 @@ describe Chef::Provider::Package do
it "should debug log and not reconfigure the package if no response_file is given" do
allow(current_resource).to receive(:version).and_return("1.0")
allow(new_resource).to receive(:response_file).and_return(nil)
- expect(Chef::Log).to receive(:debug).with("package[emacs] no response_file provided - nothing to do")
+ expect(logger).to receive(:trace).with("package[install emacs] no response_file provided - nothing to do")
expect(provider).not_to receive(:reconfig_package)
provider.run_action(:reconfig)
expect(new_resource).not_to be_updated_by_last_action
@@ -314,13 +321,83 @@ describe Chef::Provider::Package do
allow(new_resource).to receive(:response_file).and_return(true)
expect(provider).to receive(:get_preseed_file).and_return(false)
allow(provider).to receive(:preseed_package).and_return(false)
- expect(Chef::Log).to receive(:debug).with("package[emacs] preseeding has not changed - nothing to do")
+ expect(logger).to receive(:trace).with("package[install emacs] preseeding has not changed - nothing to do")
expect(provider).not_to receive(:reconfig_package)
provider.run_action(:reconfig)
expect(new_resource).not_to be_updated_by_last_action
end
end
+ describe "When locking the package" do
+ before(:each) do
+ allow(provider).to receive(:lock_package).with(
+ new_resource.package_name,
+ nil
+ ).and_return(true)
+ end
+
+ it "should lock the package if it is unlocked" do
+ allow(provider).to receive(:package_locked).with(
+ new_resource.package_name,
+ nil
+ ).and_return(false)
+ provider.run_action(:lock)
+ end
+
+ it "should not lock the package if it is already locked" do
+ allow(provider).to receive(:package_locked).with(
+ new_resource.package_name,
+ nil
+ ).and_return(true)
+ expect(provider).not_to receive(:lock_package)
+ provider.run_action(:lock)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "should set the resource to updated if it locks the package" do
+ allow(provider).to receive(:package_locked).with(
+ new_resource.package_name,
+ nil
+ ).and_return(false)
+ provider.run_action(:lock)
+ expect(new_resource).to be_updated
+ end
+ end
+
+ describe "When unlocking the package" do
+ before(:each) do
+ allow(provider).to receive(:unlock_package).and_return(true)
+ end
+
+ it "should unlock the package if it is locked" do
+ allow(provider).to receive(:package_locked).with(
+ new_resource.package_name,
+ nil
+ ).and_return(true)
+ expect(provider).to receive(:unlock_package)
+ provider.run_action(:unlock)
+ end
+
+ it "should not unlock the package if it is already unlocked" do
+ allow(provider).to receive(:package_locked).with(
+ new_resource.package_name,
+ nil
+ ).and_return(false)
+ expect(provider).not_to receive(:unlock_package)
+ provider.run_action(:unlock)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "should set the resource to updated if it unlocks the package" do
+ allow(provider).to receive(:package_locked).with(
+ new_resource.package_name,
+ nil
+ ).and_return(true)
+ provider.run_action(:unlock)
+ expect(new_resource).to be_updated
+ end
+ end
+
describe "when running commands to be implemented by subclasses" do
it "should raises UnsupportedAction for install" do
expect { provider.install_package("emacs", "1.4.2") }.to raise_error(Chef::Exceptions::UnsupportedAction)
@@ -346,6 +423,14 @@ describe Chef::Provider::Package do
it "should raise UnsupportedAction for reconfig" do
expect { provider.reconfig_package("emacs", "1.4.2") }.to raise_error(Chef::Exceptions::UnsupportedAction)
end
+
+ it "should raise UnsupportedAction for lock" do
+ expect { provider.lock_package("emacs", nil) }.to raise_error(Chef::Exceptions::UnsupportedAction)
+ end
+
+ it "should raise UnsupportedAction for unlock" do
+ expect { provider.unlock_package("emacs", nil) }.to raise_error(Chef::Exceptions::UnsupportedAction)
+ end
end
describe "when given a response file" do
@@ -396,13 +481,13 @@ describe Chef::Provider::Package do
describe "when installing the preseed file to the cache location" do
let(:response_file_destination) { Dir.tmpdir + "/preseed--java--java-6.seed" }
- let(:response_file_resource) {
+ let(:response_file_resource) do
response_file_resource = Chef::Resource::CookbookFile.new(response_file_destination, run_context)
response_file_resource.cookbook_name = "java"
response_file_resource.backup(false)
response_file_resource.source("java.response")
response_file_resource
- }
+ end
before do
expect(provider).to receive(:preseed_resource).with("java", "6").and_return(response_file_resource)
@@ -458,8 +543,22 @@ describe "Subclass with use_multipackage_api" do
expect(provider.use_multipackage_api?).to be true
end
- it "offers a_to_s to subclasses to convert an array of strings to a single string" do
- expect(provider.send(:a_to_s, "a", nil, "b", "", "c", " ", "d e", "f-g")).to eq("a b c d e f-g")
+ context "#a_to_s utility for subclasses" do
+ before(:each) do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ end
+
+ it "converts varargs of strings to a single string" do
+ expect(provider.send(:a_to_s, "a", nil, "b", "", "c", " ", "d e", "f-g")).to eq("a b c d e f-g")
+ end
+
+ it "converts an array of strings to a single string" do
+ expect(provider.send(:a_to_s, ["a", nil, "b", "", "c", " ", "d e", "f-g"])).to eq("a b c d e f-g")
+ end
+
+ it "converts a mishmash of array args to a single string" do
+ expect(provider.send(:a_to_s, "a", [ nil, "b", "", [ "c" ] ], " ", [ "d e", "f-g" ])).to eq("a b c d e f-g")
+ end
end
it "when user passes string to package_name, passes arrays to install_package" do
@@ -566,8 +665,11 @@ describe "Chef::Provider::Package - Multi" do
let(:new_resource) { Chef::Resource::Package.new(%w{emacs vi}) }
let(:current_resource) { Chef::Resource::Package.new(%w{emacs vi}) }
let(:candidate_version) { [ "1.0", "6.2" ] }
+ class MyPackageProvider < Chef::Provider::Package
+ use_multipackage_api
+ end
let(:provider) do
- provider = Chef::Provider::Package.new(new_resource, run_context)
+ provider = MyPackageProvider.new(new_resource, run_context)
provider.current_resource = current_resource
provider.candidate_version = candidate_version
provider
@@ -731,7 +833,7 @@ describe "Chef::Provider::Package - Multi" do
it "should remove the packages if all are installed" do
expect(provider).to be_removing_package
- expect(provider).to receive(:remove_package).with(%w{emacs vi}, nil)
+ expect(provider).to receive(:remove_package).with(%w{emacs vi}, [nil])
provider.run_action(:remove)
expect(new_resource).to be_updated
expect(new_resource).to be_updated_by_last_action
@@ -740,7 +842,7 @@ describe "Chef::Provider::Package - Multi" do
it "should remove the packages if some are installed" do
current_resource.version ["1.0", nil]
expect(provider).to be_removing_package
- expect(provider).to receive(:remove_package).with(%w{emacs vi}, nil)
+ expect(provider).to receive(:remove_package).with(%w{emacs vi}, [nil])
provider.run_action(:remove)
expect(new_resource).to be_updated
expect(new_resource).to be_updated_by_last_action
@@ -787,7 +889,7 @@ describe "Chef::Provider::Package - Multi" do
it "should purge the packages if all are installed" do
expect(provider).to be_removing_package
- expect(provider).to receive(:purge_package).with(%w{emacs vi}, nil)
+ expect(provider).to receive(:purge_package).with(%w{emacs vi}, [nil])
provider.run_action(:purge)
expect(new_resource).to be_updated
expect(new_resource).to be_updated_by_last_action
@@ -796,7 +898,7 @@ describe "Chef::Provider::Package - Multi" do
it "should purge the packages if some are installed" do
current_resource.version ["1.0", nil]
expect(provider).to be_removing_package
- expect(provider).to receive(:purge_package).with(%w{emacs vi}, nil)
+ expect(provider).to receive(:purge_package).with(%w{emacs vi}, [nil])
provider.run_action(:purge)
expect(new_resource).to be_updated
expect(new_resource).to be_updated_by_last_action
@@ -835,6 +937,10 @@ describe "Chef::Provider::Package - Multi" do
end
describe "shell_out helpers" do
+ before(:each) do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ end
+
[ :shell_out_with_timeout, :shell_out_with_timeout! ].each do |method|
stubbed_method = method == :shell_out_with_timeout! ? :shell_out! : :shell_out
[ %w{command arg1 arg2}, "command arg1 arg2" ].each do |command|
diff --git a/spec/unit/provider/powershell_script_spec.rb b/spec/unit/provider/powershell_script_spec.rb
index 5b5c783986..f4663861c6 100644
--- a/spec/unit/provider/powershell_script_spec.rb
+++ b/spec/unit/provider/powershell_script_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright 2013-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,7 +20,7 @@ require "spec_helper"
describe Chef::Provider::PowershellScript, "action_run" do
let(:powershell_version) { nil }
- let(:node) {
+ let(:node) do
node = Chef::Node.new
node.default["kernel"] = Hash.new
node.default["kernel"][:machine] = :x86_64.to_s
@@ -28,14 +28,24 @@ describe Chef::Provider::PowershellScript, "action_run" do
node.default[:languages] = { :powershell => { :version => powershell_version } }
end
node
- }
+ end
+
+ # code block is mandatory for the powershell provider
+ let(:code) { "" }
+
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+
+ let(:run_context) { run_context = Chef::RunContext.new(node, {}, events) }
- let(:provider) {
- empty_events = Chef::EventDispatch::Dispatcher.new
- run_context = Chef::RunContext.new(node, {}, empty_events)
+ let(:new_resource) do
new_resource = Chef::Resource::PowershellScript.new("run some powershell code", run_context)
+ new_resource.code code
+ new_resource
+ end
+
+ let(:provider) do
Chef::Provider::PowershellScript.new(new_resource, run_context)
- }
+ end
context "when setting interpreter flags" do
context "on nano" do
@@ -43,8 +53,8 @@ describe Chef::Provider::PowershellScript, "action_run" do
allow(Chef::Platform).to receive(:windows_nano_server?).and_return(true)
allow(provider).to receive(:is_forced_32bit).and_return(false)
os_info_double = double("os_info")
- allow(provider.run_context.node.kernel).to receive(:os_info).and_return(os_info_double)
- allow(os_info_double).to receive(:system_directory).and_return("C:\\Windows\\system32")
+ allow(provider.run_context.node["kernel"]).to receive(:[]).with("os_info").and_return(os_info_double)
+ allow(os_info_double).to receive(:[]).with("system_directory").and_return("C:\\Windows\\system32")
end
it "sets the -Command flag as the last flag" do
@@ -58,8 +68,8 @@ describe Chef::Provider::PowershellScript, "action_run" do
allow(Chef::Platform).to receive(:windows_nano_server?).and_return(false)
allow(provider).to receive(:is_forced_32bit).and_return(false)
os_info_double = double("os_info")
- allow(provider.run_context.node.kernel).to receive(:os_info).and_return(os_info_double)
- allow(os_info_double).to receive(:system_directory).and_return("C:\\Windows\\system32")
+ allow(provider.run_context.node["kernel"]).to receive(:[]).with("os_info").and_return(os_info_double)
+ allow(os_info_double).to receive(:[]).with("system_directory").and_return("C:\\Windows\\system32")
end
it "sets the -File flag as the last flag" do
@@ -74,7 +84,7 @@ describe Chef::Provider::PowershellScript, "action_run" do
provider_flags.find do |value|
execution_policy_index += 1
- execution_policy_specified = value.casecmp("-ExecutionPolicy".downcase).zero?
+ execution_policy_specified = value.casecmp("-ExecutionPolicy".downcase) == 0
end
execution_policy = execution_policy_specified ? provider_flags[execution_policy_index] : nil
diff --git a/spec/unit/provider/registry_key_spec.rb b/spec/unit/provider/registry_key_spec.rb
index 41bc5b33d3..096fe9b9ed 100644
--- a/spec/unit/provider/registry_key_spec.rb
+++ b/spec/unit/provider/registry_key_spec.rb
@@ -19,23 +19,6 @@
require "spec_helper"
shared_examples_for "a registry key" do
- before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
-
- @new_resource = Chef::Resource::RegistryKey.new("windows is fun", @run_context)
- @new_resource.key keyname
- @new_resource.values( testval1 )
- @new_resource.recursive false
-
- @provider = Chef::Provider::RegistryKey.new(@new_resource, @run_context)
-
- allow(@provider).to receive(:running_on_windows!).and_return(true)
- @double_registry = double(Chef::Win32::Registry)
- allow(@provider).to receive(:registry).and_return(@double_registry)
- end
-
describe "when first created" do
end
@@ -273,6 +256,23 @@ shared_examples_for "a registry key" do
end
describe Chef::Provider::RegistryKey do
+ before(:each) do
+ @node = Chef::Node.new
+ @events = Chef::EventDispatch::Dispatcher.new
+ @run_context = Chef::RunContext.new(@node, {}, @events)
+
+ @new_resource = Chef::Resource::RegistryKey.new("windows is fun", @run_context)
+ @new_resource.key keyname
+ @new_resource.values( testval1 )
+ @new_resource.recursive false
+
+ @provider = Chef::Provider::RegistryKey.new(@new_resource, @run_context)
+
+ allow(@provider).to receive(:running_on_windows!).and_return(true)
+ @double_registry = double(Chef::Win32::Registry)
+ allow(@provider).to receive(:registry).and_return(@double_registry)
+ end
+
context "when the key data is safe" do
let(:keyname) { 'HKLM\Software\Opscode\Testing\Safe' }
let(:testval1) { { :name => "one", :type => :string, :data => "1" } }
@@ -292,4 +292,122 @@ describe Chef::Provider::RegistryKey do
it_should_behave_like "a registry key"
end
+
+ describe "action_create" do
+ context "when key exists and type matches" do
+ let(:keyname) { 'hklm\\software\\opscode\\testing\\dword' }
+ let(:dword_passed_as_integer) { { :name => "one", :type => :dword, :data => 12345 } }
+ let(:testval1) { { :name => "one", :type => :dword, :data => "12345" } }
+ before do
+ expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(true)
+ end
+
+ it "does not make a change for datatype of data value differing" do
+ expect(@double_registry).to receive(:get_values).with(keyname).and_return( dword_passed_as_integer )
+ expect(@double_registry).not_to receive(:set_value)
+ @provider.load_current_resource
+ @provider.action_create
+ end
+ end
+ end
+end
+
+describe Chef::Provider::RegistryKey, "key_missing?" do
+ let(:node) { Chef::Node.new }
+ let(:events) { double("Chef::Events").as_null_object }
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+ let(:run_context) { double("Chef::RunContext", node: node, events: events, logger: logger) }
+ let(:new_resource) { Chef::Resource::RegistryKey.new("emacs") }
+ let(:provider) { Chef::Provider::RegistryKey.new(new_resource, run_context) }
+
+ let(:all_keys_present_in_all_hash) do
+ [ { :name => "input1_value1", :type => :string, :data => "my_value1" },
+ { :name => "input1_value2", :type => :string, :data => "my_value2" },
+ ]
+ end
+ let(:type_key_not_present_in_any_hash) do
+ [ { :name => "input2_value1", :data => "my_value1" },
+ { :name => "input2_value2", :data => "my_value2" },
+ ]
+ end
+ let(:type_key_not_present_in_some_hash) do
+ [ { :name => "input3_value1", :data => "my_value1" },
+ { :name => "input3_value2", :type => :string, :data => "my_value2" },
+ ]
+ end
+ let(:data_key_not_present_in_any_hash) do
+ [ { :name => "input4_value1", :type => :string },
+ { :name => "input4_value2", :type => :string },
+ ]
+ end
+ let(:data_key_not_present_in_some_hash) do
+ [ { :name => "input5_value1", :type => :string, :data => "my_value1" },
+ { :name => "input5_value2", :type => :string },
+ ]
+ end
+ let(:only_name_key_present_in_all_hash) do
+ [ { :name => "input6_value1" },
+ { :name => "input6_value2" },
+ ]
+ end
+
+ context "type key" do
+ context "when type key is present in all the values hash of registry_key resource" do
+ it "returns false" do
+ response = provider.key_missing?(all_keys_present_in_all_hash, :type)
+ expect(response).to be == false
+ end
+ end
+
+ context "when type key is not present in any of the values hash of registry_key resource" do
+ it "returns true" do
+ response = provider.key_missing?(type_key_not_present_in_any_hash, :type)
+ expect(response).to be == true
+ end
+ end
+
+ context "when type key is not present only in some of the values hash of registry_key resource" do
+ it "returns true" do
+ response = provider.key_missing?(type_key_not_present_in_some_hash, :type)
+ expect(response).to be == true
+ end
+ end
+
+ context "when only name key is present in all the values hash of registry_key resource" do
+ it "returns true" do
+ response = provider.key_missing?(only_name_key_present_in_all_hash, :type)
+ expect(response).to be == true
+ end
+ end
+ end
+
+ context "data key" do
+ context "when data key is present in all the values hash of registry_key resource" do
+ it "returns false" do
+ response = provider.key_missing?(all_keys_present_in_all_hash, :data)
+ expect(response).to be == false
+ end
+ end
+
+ context "when data key is not present in any of the values hash of registry_key resource" do
+ it "returns true" do
+ response = provider.key_missing?(data_key_not_present_in_any_hash, :data)
+ expect(response).to be == true
+ end
+ end
+
+ context "when data key is not present only in some of the values hash of registry_key resource" do
+ it "returns true" do
+ response = provider.key_missing?(data_key_not_present_in_some_hash, :data)
+ expect(response).to be == true
+ end
+ end
+
+ context "when only name key is present in all the values hash of registry_key resource" do
+ it "returns true" do
+ response = provider.key_missing?(only_name_key_present_in_all_hash, :data)
+ expect(response).to be == true
+ end
+ end
+ end
end
diff --git a/spec/unit/provider/remote_directory_spec.rb b/spec/unit/provider/remote_directory_spec.rb
index 710d6613fc..d391da3010 100644
--- a/spec/unit/provider/remote_directory_spec.rb
+++ b/spec/unit/provider/remote_directory_spec.rb
@@ -99,6 +99,21 @@ describe Chef::Provider::RemoteDirectory do
expect(cookbook_file.owner).to eq("toor")
expect(cookbook_file.backup).to eq(23)
end
+
+ it "respects sensitive flag" do
+ @resource.cookbook "gondola_rides"
+ @resource.sensitive true
+ cookbook_file = @provider.send(:cookbook_file_resource,
+ "/target/destination/path.txt",
+ "relative/source/path.txt")
+ expect(cookbook_file.sensitive).to eq(true)
+
+ @resource.sensitive false
+ cookbook_file = @provider.send(:cookbook_file_resource,
+ "/target/destination/path.txt",
+ "relative/source/path.txt")
+ expect(cookbook_file.sensitive).to eq(false)
+ end
end
describe "when creating the remote directory" do
@@ -106,7 +121,7 @@ describe Chef::Provider::RemoteDirectory do
@node.automatic_attrs[:platform] = :just_testing
@node.automatic_attrs[:platform_version] = :just_testing
- @destination_dir = Dir.mktmpdir << "/remote_directory_test"
+ @destination_dir = make_canonical_temp_directory << "/remote_directory_test"
@resource.path(@destination_dir)
end
@@ -178,7 +193,7 @@ describe Chef::Provider::RemoteDirectory do
expect(::File.exist?(@destination_dir + "/a/multiply/nested/directory/qux.txt")).to be_falsey
end
- it "removes directory symlinks properly", :not_supported_on_win2k3 do
+ it "removes directory symlinks properly" do
symlinked_dir_path = @destination_dir + "/symlinked_dir"
@provider.action = :create
@provider.run_action
diff --git a/spec/unit/provider/remote_file/content_spec.rb b/spec/unit/provider/remote_file/content_spec.rb
index c6a560b123..307eb98187 100644
--- a/spec/unit/provider/remote_file/content_spec.rb
+++ b/spec/unit/provider/remote_file/content_spec.rb
@@ -159,7 +159,7 @@ describe Chef::Provider::RemoteFile::Content do
describe "when there is an array of sources and the first fails" do
- # https://github.com/opscode/chef/pull/1358#issuecomment-40853299
+ # https://github.com/chef/chef/pull/1358#issuecomment-40853299
def create_exception(exception_class)
if [ Net::HTTPServerException, Net::HTTPFatalError ].include? exception_class
exception_class.new("message", { "something" => 1 })
@@ -180,6 +180,7 @@ describe Chef::Provider::RemoteFile::Content do
Net::HTTPServerException,
Net::HTTPFatalError,
Net::FTPError,
+ Errno::ETIMEDOUT,
].each do |exception|
describe "with an exception of #{exception}" do
before do
diff --git a/spec/unit/provider/remote_file/fetcher_spec.rb b/spec/unit/provider/remote_file/fetcher_spec.rb
index 0fa213cdb2..f5b32800f2 100644
--- a/spec/unit/provider/remote_file/fetcher_spec.rb
+++ b/spec/unit/provider/remote_file/fetcher_spec.rb
@@ -27,6 +27,7 @@ describe Chef::Provider::RemoteFile::Fetcher do
describe "when passed a network share" do
before do
expect(Chef::Provider::RemoteFile::NetworkFile).to receive(:new).and_return(fetcher_instance)
+ allow(Chef::Platform).to receive(:windows?).and_return(true)
end
context "when host is a name" do
diff --git a/spec/unit/provider/remote_file/ftp_spec.rb b/spec/unit/provider/remote_file/ftp_spec.rb
index 9963c401d2..b2fbb7300c 100644
--- a/spec/unit/provider/remote_file/ftp_spec.rb
+++ b/spec/unit/provider/remote_file/ftp_spec.rb
@@ -19,12 +19,12 @@
require "spec_helper"
describe Chef::Provider::RemoteFile::FTP do
- let(:enclosing_directory) {
+ let(:enclosing_directory) do
canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
- }
- let(:resource_path) {
+ end
+ let(:resource_path) do
canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
- }
+ end
let(:new_resource) do
r = Chef::Resource::RemoteFile.new("remote file ftp backend test (new resource)")
diff --git a/spec/unit/provider/remote_file/http_spec.rb b/spec/unit/provider/remote_file/http_spec.rb
index f58a3d3c14..e1f74bd35a 100644
--- a/spec/unit/provider/remote_file/http_spec.rb
+++ b/spec/unit/provider/remote_file/http_spec.rb
@@ -157,9 +157,9 @@ describe Chef::Provider::RemoteFile::HTTP do
let(:expected_http_opts) { {} }
let(:expected_http_args) { [uri, expected_http_opts] }
- let(:tempfile_path) { "/tmp/chef-mock-tempfile-abc123" }
+ let(:tempfile_path) { tempfile.path }
- let(:tempfile) { double(Tempfile, :path => tempfile_path, :close => nil) }
+ let(:tempfile) { Tempfile.open("muhtempfile") }
let(:last_response) { {} }
@@ -171,7 +171,7 @@ describe Chef::Provider::RemoteFile::HTTP do
let(:rest) do
rest = double(Chef::HTTP::Simple)
- allow(rest).to receive(:streaming_request).and_return(tempfile)
+ allow_any_instance_of(Chef::FileContentManagement::Tempfile).to receive(:tempfile).and_return(tempfile)
allow(rest).to receive(:last_response).and_return(last_response)
rest
end
@@ -185,21 +185,37 @@ describe Chef::Provider::RemoteFile::HTTP do
expect(Chef::HTTP::Simple).to receive(:new).with(*expected_http_args).and_return(rest)
end
- describe "and the request does not return new content" do
+ it "should clean up the tempfile, and return a nil when streaming_request returns nil" do
+ # Streaming request returns nil for a 304 not modified (etags / last-modified)
+ expect(rest).to receive(:streaming_request).with(uri, {}, tempfile).and_return(nil)
+ expect(tempfile).to receive(:close)
+ expect(tempfile).to receive(:unlink)
+ expect(fetcher.fetch).to be_nil
+ end
+
+ context "with progress reports" do
+ let(:fetched_content_checksum) { "e2a8938cc31754f6c067b35aab1d0d4864272e9bf8504536ef3e79ebf8432305" }
- it "should return a nil tempfile for a 304 HTTPNotModifed" do
- # Streaming request returns nil for 304 errors
- allow(rest).to receive(:streaming_request).and_return(nil)
- expect(fetcher.fetch).to be_nil
+ before do
+ expect(cache_control_data).to receive(:save)
+ expect(Chef::Digester).to receive(:checksum_for_file).with(tempfile_path).and_return(fetched_content_checksum)
+ Chef::Config[:show_download_progress] = true
end
+ it "should yield its progress" do
+ expect(rest).to receive(:streaming_request_with_progress).with(uri, {}, tempfile).and_yield(50, 100).and_yield(70, 100).and_return(tempfile)
+ expect(event_dispatcher).to receive(:formatter?).and_return(true)
+ expect(event_dispatcher).to receive(:resource_update_progress).with(new_resource, 50, 100, 10).ordered
+ expect(event_dispatcher).to receive(:resource_update_progress).with(new_resource, 70, 100, 10).ordered
+ fetcher.fetch
+ end
end
describe "and the request returns new content" do
-
let(:fetched_content_checksum) { "e2a8938cc31754f6c067b35aab1d0d4864272e9bf8504536ef3e79ebf8432305" }
before do
+ expect(rest).to receive(:streaming_request).with(uri, {}, tempfile).and_return(tempfile)
expect(cache_control_data).to receive(:save)
expect(Chef::Digester).to receive(:checksum_for_file).with(tempfile_path).and_return(fetched_content_checksum)
end
@@ -212,20 +228,6 @@ describe Chef::Provider::RemoteFile::HTTP do
expect(cache_control_data.checksum).to eq(fetched_content_checksum)
end
- context "with progress reports" do
- before do
- Chef::Config[:show_download_progress] = true
- end
-
- it "should yield its progress" do
- allow(rest).to receive(:streaming_request_with_progress).and_yield(50, 100).and_yield(70, 100).and_return(tempfile)
- expect(event_dispatcher).to receive(:formatter?).and_return(true)
- expect(event_dispatcher).to receive(:resource_update_progress).with(new_resource, 50, 100, 10).ordered
- expect(event_dispatcher).to receive(:resource_update_progress).with(new_resource, 70, 100, 10).ordered
- fetcher.fetch
- end
- end
-
context "and the response does not contain an etag" do
let(:last_response) { { "etag" => nil } }
it "does not include an etag in the result" do
diff --git a/spec/unit/provider/remote_file/local_file_spec.rb b/spec/unit/provider/remote_file/local_file_spec.rb
index 31f14fbe45..6f345cadd1 100644
--- a/spec/unit/provider/remote_file/local_file_spec.rb
+++ b/spec/unit/provider/remote_file/local_file_spec.rb
@@ -17,6 +17,8 @@
#
require "spec_helper"
+require "uri"
+require "addressable/uri"
describe Chef::Provider::RemoteFile::LocalFile do
@@ -47,7 +49,7 @@ describe Chef::Provider::RemoteFile::LocalFile do
end
describe "when given local windows path with spaces" do
- let(:uri) { URI.parse(URI.escape("file:///z:/windows/path/foo & bar.txt")) }
+ let(:uri) { URI.parse(Addressable::URI.encode("file:///z:/windows/path/foo & bar.txt")) }
it "returns a valid windows local path" do
expect(fetcher.source_path).to eq("z:/windows/path/foo & bar.txt")
end
@@ -61,7 +63,7 @@ describe Chef::Provider::RemoteFile::LocalFile do
end
describe "when given unc windows path with spaces" do
- let(:uri) { URI.parse(URI.escape("file:////server/share/windows/path/foo & bar.txt")) }
+ let(:uri) { URI.parse(Addressable::URI.encode("file:////server/share/windows/path/foo & bar.txt")) }
it "returns a valid windows unc path" do
expect(fetcher.source_path).to eq("//server/share/windows/path/foo & bar.txt")
end
diff --git a/spec/unit/provider/remote_file/network_file_spec.rb b/spec/unit/provider/remote_file/network_file_spec.rb
index de065c83e2..ecb326fc64 100644
--- a/spec/unit/provider/remote_file/network_file_spec.rb
+++ b/spec/unit/provider/remote_file/network_file_spec.rb
@@ -1,3 +1,4 @@
+
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
# Copyright:: Copyright 2015-2016, Chef Software
@@ -19,7 +20,6 @@
require "spec_helper"
describe Chef::Provider::RemoteFile::NetworkFile do
-
let(:source) { "\\\\foohost\\fooshare\\Foo.tar.gz" }
let(:new_resource) { Chef::Resource::RemoteFile.new("network file (new_resource)") }
@@ -30,10 +30,15 @@ describe Chef::Provider::RemoteFile::NetworkFile do
let(:tempfile) { double("Tempfile", :path => "/tmp/foo/bar/Foo.tar.gz", :close => nil) }
let(:chef_tempfile) { double("Chef::FileContentManagement::Tempfile", :tempfile => tempfile) }
+ let(:source_file) { double("::File", :read => nil) }
+
+ before do
+ allow(Chef::Platform).to receive(:windows?).and_return(true)
+ end
it "stages the local file to a temporary file" do
expect(Chef::FileContentManagement::Tempfile).to receive(:new).with(new_resource).and_return(chef_tempfile)
- expect(::FileUtils).to receive(:cp).with(source, tempfile.path)
+ expect(::File).to receive(:open).with(source, "rb").and_return(source_file)
expect(tempfile).to receive(:close)
result = fetcher.fetch
diff --git a/spec/unit/provider/remote_file/sftp_spec.rb b/spec/unit/provider/remote_file/sftp_spec.rb
index 673ea015c0..ddab1605f0 100644
--- a/spec/unit/provider/remote_file/sftp_spec.rb
+++ b/spec/unit/provider/remote_file/sftp_spec.rb
@@ -20,12 +20,12 @@ require "spec_helper"
describe Chef::Provider::RemoteFile::SFTP do
#built out dependencies
- let(:enclosing_directory) {
+ let(:enclosing_directory) do
canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
- }
- let(:resource_path) {
+ end
+ let(:resource_path) do
canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
- }
+ end
let(:new_resource) do
r = Chef::Resource::RemoteFile.new("remote file sftp backend test (new resource)")
@@ -117,7 +117,7 @@ describe Chef::Provider::RemoteFile::SFTP do
end
context "and the URI specifies an alternate port" do
- let(:uri) { URI.parse("ftp://conan:cthu1hu@opscode.com:8021/seattle.txt") }
+ let(:uri) { URI.parse("sftp://conan:cthu1hu@opscode.com:8021/seattle.txt") }
it "should connect on an alternate port when one is provided" do
expect(Net::SFTP).to receive(:start).with("opscode.com:8021", "conan", :password => "cthu1hu")
@@ -127,10 +127,10 @@ describe Chef::Provider::RemoteFile::SFTP do
end
context "and the uri specifies a nested path" do
- let(:uri) { URI.parse("ftp://conan:cthu1hu@opscode.com/the/whole/path/seattle.txt") }
+ let(:uri) { URI.parse("sftp://conan:cthu1hu@opscode.com/the/whole/path/seattle.txt") }
it "should fetch the file from the correct path" do
- expect(sftp).to receive(:download!).with("the/whole/path/seattle.txt", "/tmp/somedir/remote-file-sftp-backend-spec-test")
+ expect(sftp).to receive(:download!).with("/the/whole/path/seattle.txt", "/tmp/somedir/remote-file-sftp-backend-spec-test")
fetcher.fetch
end
end
diff --git a/spec/unit/provider/remote_file_spec.rb b/spec/unit/provider/remote_file_spec.rb
index 6107f93c41..3f91377c60 100644
--- a/spec/unit/provider/remote_file_spec.rb
+++ b/spec/unit/provider/remote_file_spec.rb
@@ -36,13 +36,14 @@ describe Chef::Provider::RemoteFile do
let(:node) { double("Chef::Node") }
let(:events) { double("Chef::Events").as_null_object } # mock all the methods
- let(:run_context) { double("Chef::RunContext", :node => node, :events => events) }
- let(:enclosing_directory) {
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+ let(:run_context) { double("Chef::RunContext", :node => node, :events => events, :logger => logger) }
+ let(:enclosing_directory) do
canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
- }
- let(:resource_path) {
+ end
+ let(:resource_path) do
canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
- }
+ end
subject(:provider) do
provider = described_class.new(resource, run_context)
diff --git a/spec/unit/provider/route_spec.rb b/spec/unit/provider/route_spec.rb
index 2e3f6e4e9e..ea6ddffb81 100644
--- a/spec/unit/provider/route_spec.rb
+++ b/spec/unit/provider/route_spec.rb
@@ -29,9 +29,14 @@ describe Chef::Provider::Route do
@new_resource.gateway "10.0.0.9"
@current_resource = Chef::Resource::Route.new("10.0.0.10")
@current_resource.gateway "10.0.0.9"
+ @default_resource = Chef::Resource::Route.new("default")
+ @default_resource.gateway "10.0.0.9"
@provider = Chef::Provider::Route.new(@new_resource, @run_context)
@provider.current_resource = @current_resource
+
+ @default_provider = Chef::Provider::Route.new(@default_resource, @run_context)
+ @default_provider.current_resource = @default_resource
end
describe Chef::Provider::Route, "hex2ip" do
@@ -51,7 +56,7 @@ describe Chef::Provider::Route do
context "on linux" do
before do
@node.automatic_attrs[:os] = "linux"
- routing_table = "Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT\n" +
+ routing_table = "Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT\n" \
"eth0 0064A8C0 0984A8C0 0003 0 0 0 00FFFFFF 0 0 0\n"
route_file = StringIO.new(routing_table)
allow(File).to receive(:open).with("/proc/net/route", "r").and_return(route_file)
@@ -91,16 +96,16 @@ describe Chef::Provider::Route do
describe Chef::Provider::Route, "action_add" do
it "should add the route if it does not exist" do
- allow(@provider).to receive(:run_command).and_return(true)
+ allow(@provider).to receive(:shell_out!)
allow(@current_resource).to receive(:gateway).and_return(nil)
- expect(@provider).to receive(:generate_command).once.with(:add)
+ expect(@provider).to receive(:generate_command).with(:add).and_return(["command"])
expect(@provider).to receive(:generate_config)
@provider.run_action(:add)
expect(@new_resource).to be_updated
end
it "should not add the route if it exists" do
- allow(@provider).to receive(:run_command).and_return(true)
+ allow(@provider).to receive(:shell_out!)
allow(@provider).to receive(:is_running).and_return(true)
expect(@provider).not_to receive(:generate_command).with(:add)
expect(@provider).to receive(:generate_config)
@@ -115,7 +120,7 @@ describe Chef::Provider::Route do
expect(File).to receive(:new).and_return(route_file)
@resource_add = Chef::Resource::Route.new("192.168.1.0/24 via 192.168.0.1")
@run_context.resource_collection << @resource_add
- allow(@provider).to receive(:run_command).and_return(true)
+ allow(@provider).to receive(:shell_out!).and_return(true)
@resource_add.action(:add)
@provider.run_action(:add)
@@ -126,8 +131,8 @@ describe Chef::Provider::Route do
describe Chef::Provider::Route, "action_delete" do
it "should delete the route if it exists" do
- allow(@provider).to receive(:run_command).and_return(true)
- expect(@provider).to receive(:generate_command).once.with(:delete)
+ allow(@provider).to receive(:shell_out!).and_return(true)
+ expect(@provider).to receive(:generate_command).with(:delete).and_return(["command"])
allow(@provider).to receive(:is_running).and_return(true)
@provider.run_action(:delete)
expect(@new_resource).to be_updated
@@ -135,7 +140,7 @@ describe Chef::Provider::Route do
it "should not delete the route if it does not exist" do
allow(@current_resource).to receive(:gateway).and_return(nil)
- allow(@provider).to receive(:run_command).and_return(true)
+ allow(@provider).to receive(:shell_out!).and_return(true)
expect(@provider).not_to receive(:generate_command).with(:add)
@provider.run_action(:delete)
expect(@new_resource).not_to be_updated
@@ -144,62 +149,67 @@ describe Chef::Provider::Route do
describe Chef::Provider::Route, "generate_command for action_add" do
it "should include a netmask when a one is specified" do
- allow(@new_resource).to receive(:netmask).and_return("255.255.0.0")
- expect(@provider.generate_command(:add)).to match(/\/\d{1,2}\s/)
+ @new_resource.netmask("255.255.0.0")
+ expect(@provider.generate_command(:add).join(" ")).to match(/\/\d{1,2}/)
end
it "should not include a netmask when a one is specified" do
- allow(@new_resource).to receive(:netmask).and_return(nil)
- expect(@provider.generate_command(:add)).not_to match(/\/\d{1,2}\s/)
+ @new_resource.netmask(nil)
+ expect(@provider.generate_command(:add).join(" ")).not_to match(/\/\d{1,2}/)
end
it "should include ' via $gateway ' when a gateway is specified" do
- expect(@provider.generate_command(:add)).to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}\s/)
+ expect(@provider.generate_command(:add).join(" ")).to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}/)
end
it "should not include ' via $gateway ' when a gateway is not specified" do
- allow(@new_resource).to receive(:gateway).and_return(nil)
- expect(@provider.generate_command(:add)).not_to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}\s/)
+ @new_resource.gateway(nil)
+ expect(@provider.generate_command(:add).join(" ")).not_to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}/)
+ end
+
+ it "should use the gatway when target is default" do
+ @default_resource.gateway("10.0.0.10")
+ expect(@default_provider.generate_command(:add).join(" ")).to match(/10.0.0.10/)
end
end
describe Chef::Provider::Route, "generate_command for action_delete" do
it "should include a netmask when a one is specified" do
- allow(@new_resource).to receive(:netmask).and_return("255.255.0.0")
- expect(@provider.generate_command(:delete)).to match(/\/\d{1,2}\s/)
+ @new_resource.netmask("255.255.0.0")
+ expect(@provider.generate_command(:delete).join(" ")).to match(/\/\d{1,2}/)
end
it "should not include a netmask when a one is specified" do
- allow(@new_resource).to receive(:netmask).and_return(nil)
- expect(@provider.generate_command(:delete)).not_to match(/\/\d{1,2}\s/)
+ @new_resource.netmask(nil)
+ expect(@provider.generate_command(:delete).join(" ")).not_to match(/\/\d{1,2}/)
end
it "should include ' via $gateway ' when a gateway is specified" do
- expect(@provider.generate_command(:delete)).to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}\s/)
+ expect(@provider.generate_command(:delete).join(" ")).to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}/)
end
it "should not include ' via $gateway ' when a gateway is not specified" do
- allow(@new_resource).to receive(:gateway).and_return(nil)
- expect(@provider.generate_command(:delete)).not_to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}\s/)
+ @new_resource.gateway(nil)
+ expect(@provider.generate_command(:delete).join(" ")).not_to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}/)
end
end
describe Chef::Provider::Route, "config_file_contents for action_add" do
it "should include a netmask when a one is specified" do
- allow(@new_resource).to receive(:netmask).and_return("255.255.0.0")
- expect(@provider.config_file_contents(:add, { :target => @new_resource.target, :netmask => @new_resource.netmask })).to match(/\/\d{1,2}.*\n$/)
+ @new_resource.netmask("255.255.0.0")
+ expect(@provider.config_file_contents(:add, target: @new_resource.target, netmask: @new_resource.netmask)).to match(/\/\d{1,2}.*\n$/)
end
it "should not include a netmask when a one is specified" do
- expect(@provider.config_file_contents(:add, { :target => @new_resource.target })).not_to match(/\/\d{1,2}.*\n$/)
+ expect(@provider.config_file_contents(:add, target: @new_resource.target)).not_to match(/\/\d{1,2}.*\n$/)
end
it "should include ' via $gateway ' when a gateway is specified" do
- expect(@provider.config_file_contents(:add, { :target => @new_resource.target, :gateway => @new_resource.gateway })).to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}\n/)
+ expect(@provider.config_file_contents(:add, target: @new_resource.target, gateway: @new_resource.gateway)).to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}\n/)
end
it "should not include ' via $gateway ' when a gateway is not specified" do
- expect(@provider.generate_command(:add)).not_to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}\n/)
+ expect(@provider.generate_command(:add).join(" ")).not_to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}\n/)
end
end
@@ -216,10 +226,22 @@ describe Chef::Provider::Route do
route_file = StringIO.new
expect(File).to receive(:new).with("/etc/sysconfig/network-scripts/route-eth0", "w").and_return(route_file)
- #Chef::Log.should_receive(:debug).with("route[10.0.0.10] writing route.eth0\n10.0.0.10 via 10.0.0.9\n")
@run_context.resource_collection << @new_resource
@provider.generate_config
end
+
+ end
+ %w{ centos redhat fedora }.each do |platform|
+ it "should write a default route file on #{platform} platform" do
+ @node.automatic_attrs[:platform] = platform
+
+ route_file = StringIO.new
+ allow(File).to receive(:exist?).with("/etc/sysconfig/network").and_return(false)
+ expect(File).to receive(:new).with("/etc/sysconfig/network", "w").and_return(route_file)
+ @run_context.resource_collection << @default_resource
+ @default_provider.generate_config
+ expect(route_file.string).to match(/GATEWAY=10\.0\.0\.9/)
+ end
end
it "should put all routes for a device in a route config file" do
@@ -230,13 +252,19 @@ describe Chef::Provider::Route do
@run_context.resource_collection << Chef::Resource::Route.new("192.168.1.0/24 via 192.168.0.1")
@run_context.resource_collection << Chef::Resource::Route.new("192.168.2.0/24 via 192.168.0.1")
@run_context.resource_collection << Chef::Resource::Route.new("192.168.3.0/24 via 192.168.0.1")
+ @run_context.resource_collection << Chef::Resource::Route.new("Complex Route").tap do |r|
+ r.target "192.168.4.0"
+ r.gateway "192.168.0.1"
+ r.netmask "255.255.255.0"
+ end
@provider.action = :add
@provider.generate_config
- expect(route_file.string.split("\n").size).to eq(3)
+ expect(route_file.string.split("\n").size).to eq(4)
expect(route_file.string).to match(/^192\.168\.1\.0\/24 via 192\.168\.0\.1$/)
expect(route_file.string).to match(/^192\.168\.2\.0\/24 via 192\.168\.0\.1$/)
expect(route_file.string).to match(/^192\.168\.3\.0\/24 via 192\.168\.0\.1$/)
+ expect(route_file.string).to match(/^192\.168\.4\.0\/24 via 192\.168\.0\.1$/)
end
end
end
diff --git a/spec/unit/provider/script_spec.rb b/spec/unit/provider/script_spec.rb
index 4e8d8bdf59..951d6546c3 100644
--- a/spec/unit/provider/script_spec.rb
+++ b/spec/unit/provider/script_spec.rb
@@ -25,12 +25,12 @@ describe Chef::Provider::Script, "action_run" do
let(:run_context) { Chef::RunContext.new(node, {}, events) }
- let(:new_resource) {
+ let(:new_resource) do
new_resource = Chef::Resource::Script.new("run some perl code")
new_resource.code "$| = 1; print 'i like beans'"
new_resource.interpreter "perl"
new_resource
- }
+ end
let(:provider) { Chef::Provider::Script.new(new_resource, run_context) }
@@ -56,12 +56,55 @@ describe Chef::Provider::Script, "action_run" do
end
end
- context "#set_owner_and_group" do
- it "sets the owner and group for the script file" do
- new_resource.user "toor"
- new_resource.group "wheel"
- expect(FileUtils).to receive(:chown).with("toor", "wheel", tempfile.path)
- provider.set_owner_and_group
+ context "when configuring the script file's security" do
+ context "when not running on Windows" do
+ before do
+ allow(::Chef::Platform).to receive(:windows?).and_return(false)
+ end
+ context "#set_owner_and_group" do
+ it "sets the owner and group for the script file" do
+ new_resource.user "toor"
+ new_resource.group "wheel"
+ expect(FileUtils).to receive(:chown).with("toor", "wheel", tempfile.path)
+ provider.set_owner_and_group
+ end
+ end
+ end
+
+ context "when running on Windows" do
+ before do
+ allow(::Chef::Platform).to receive(:windows?).and_return(true)
+ expect(new_resource.user).to eq(nil)
+ stub_const("Chef::ReservedNames::Win32::API::Security::GENERIC_READ", 1)
+ stub_const("Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE", 4)
+ stub_const("Chef::ReservedNames::Win32::Security", Class.new)
+ stub_const("Chef::ReservedNames::Win32::Security::SecurableObject", Class.new)
+ stub_const("Chef::ReservedNames::Win32::Security::SID", Class.new)
+ stub_const("Chef::ReservedNames::Win32::Security::ACE", Class.new)
+ stub_const("Chef::ReservedNames::Win32::Security::ACL", Class.new)
+ end
+
+ context "when an alternate user is not specified" do
+ it "does not attempt to set the script file's security descriptor" do
+ expect(provider).to receive(:grant_alternate_user_read_access)
+ expect(Chef::ReservedNames::Win32::Security::SecurableObject).not_to receive(:new)
+ provider.set_owner_and_group
+ end
+ end
+
+ context "when an alternate user is specified" do
+ let(:security_descriptor) { instance_double("Chef::ReservedNames::Win32::Security::SecurityDescriptor", :dacl => []) }
+ let(:securable_object) { instance_double("Chef::ReservedNames::Win32::Security::SecurableObject", :security_descriptor => security_descriptor, :dacl= => nil) }
+ it "sets the script file's security descriptor" do
+ new_resource.user("toor")
+ expect(Chef::ReservedNames::Win32::Security::SecurableObject).to receive(:new).and_return(securable_object)
+ expect(Chef::ReservedNames::Win32::Security::SID).to receive(:from_account).and_return(nil)
+ expect(Chef::ReservedNames::Win32::Security::ACE).to receive(:access_allowed).and_return(nil)
+ expect(Chef::ReservedNames::Win32::Security::ACL).to receive(:create).and_return(nil)
+ expect(securable_object).to receive(:dacl=)
+ provider.set_owner_and_group
+ end
+ end
end
end
@@ -87,9 +130,9 @@ describe Chef::Provider::Script, "action_run" do
end
describe "when running the script" do
- let (:default_opts) {
+ let (:default_opts) do
{ timeout: 3600, returns: 0, log_level: :info, log_tag: "script[run some perl code]" }
- }
+ end
before do
allow(STDOUT).to receive(:tty?).and_return(false)
@@ -100,7 +143,7 @@ describe Chef::Provider::Script, "action_run" do
end
it "should call shell_out! with the command" do
- expect(provider).to receive(:shell_out!).with(provider.command, default_opts).and_return(true)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with(provider.command, default_opts).and_return(true)
provider.action_run
end
diff --git a/spec/unit/provider/service/arch_service_spec.rb b/spec/unit/provider/service/arch_service_spec.rb
index 506a1616c5..010b4a989f 100644
--- a/spec/unit/provider/service/arch_service_spec.rb
+++ b/spec/unit/provider/service/arch_service_spec.rb
@@ -221,7 +221,7 @@ RUNNING_PS
# end
it "should call the start command if one is specified" do
- allow(@new_resource).to receive(:start_command).and_return("/etc/rc.d/chef startyousillysally")
+ @new_resource.start_command("/etc/rc.d/chef startyousillysally")
expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/chef startyousillysally")
@provider.start_service()
end
@@ -247,7 +247,7 @@ RUNNING_PS
# end
it "should call the stop command if one is specified" do
- allow(@new_resource).to receive(:stop_command).and_return("/etc/rc.d/chef itoldyoutostop")
+ @new_resource.stop_command("/etc/rc.d/chef itoldyoutostop")
expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/chef itoldyoutostop")
@provider.stop_service()
end
@@ -274,13 +274,13 @@ RUNNING_PS
# end
it "should call 'restart' on the service_name if the resource supports it" do
- allow(@new_resource).to receive(:supports).and_return({ :restart => true })
+ @new_resource.supports({ :restart => true })
expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} restart")
@provider.restart_service()
end
it "should call the restart_command if one has been specified" do
- allow(@new_resource).to receive(:restart_command).and_return("/etc/rc.d/chef restartinafire")
+ @new_resource.restart_command("/etc/rc.d/chef restartinafire")
expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} restartinafire")
@provider.restart_service()
end
@@ -309,13 +309,13 @@ RUNNING_PS
# end
it "should call 'reload' on the service if it supports it" do
- allow(@new_resource).to receive(:supports).and_return({ :reload => true })
+ @new_resource.supports({ :reload => true })
expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} reload")
@provider.reload_service()
end
it "should should run the user specified reload command if one is specified and the service doesn't support reload" do
- allow(@new_resource).to receive(:reload_command).and_return("/etc/rc.d/chef lollerpants")
+ @new_resource.reload_command("/etc/rc.d/chef lollerpants")
expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} lollerpants")
@provider.reload_service()
end
diff --git a/spec/unit/provider/service/debian_service_spec.rb b/spec/unit/provider/service/debian_service_spec.rb
index 2192671370..25e7c9acc2 100644
--- a/spec/unit/provider/service/debian_service_spec.rb
+++ b/spec/unit/provider/service/debian_service_spec.rb
@@ -32,6 +32,17 @@ describe Chef::Provider::Service::Debian do
@provider.current_resource = @current_resource
@pid, @stdin, @stdout, @stderr = nil, nil, nil, nil
+ allow(File).to receive(:exist?).with("/etc/init.d/chef").and_return true
+ end
+
+ let(:init_lines) do
+ [
+ "### BEGIN INIT INFO",
+ "# Required-Start: hostname $local_fs",
+ "# Default-Start: 2 3 4 5",
+ "# Default-Stop: 0 1 6",
+ "### END INIT INFO",
+ ]
end
describe "load_current_resource" do
@@ -39,31 +50,22 @@ describe Chef::Provider::Service::Debian do
expect(File).to receive(:exists?).with("/usr/sbin/update-rc.d") .and_return(false)
@provider.define_resource_requirements
- expect {
+ expect do
@provider.process_resource_requirements
- }.to raise_error(Chef::Exceptions::Service)
+ end.to raise_error(Chef::Exceptions::Service)
end
context "when update-rc.d shows init linked to rc*.d/" do
before do
allow(@provider).to receive(:assert_update_rcd_available)
+ allow(File).to receive(:readlines).with("/etc/init.d/chef").and_return(init_lines)
- result = <<-UPDATE_RC_D_SUCCESS
- Removing any system startup links for /etc/init.d/chef ...
- /etc/rc0.d/K20chef
- /etc/rc1.d/K20chef
- /etc/rc2.d/S20chef
- /etc/rc3.d/S20chef
- /etc/rc4.d/S20chef
- /etc/rc5.d/S20chef
- /etc/rc6.d/K20chef
- UPDATE_RC_D_SUCCESS
-
- @stdout = StringIO.new(result)
- @stderr = StringIO.new
- @status = double("Status", :exitstatus => 0, :stdout => @stdout)
- allow(@provider).to receive(:shell_out!).and_return(@status)
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ [0, 1, 6].each do |stop|
+ allow(Dir).to receive(:glob).with("/etc/rc#{stop}.d/[SK][0-9][0-9]chef").and_return(["/etc/rc#{stop}.d/K20chef"])
+ end
+ [2, 3, 4, 5].each do |start|
+ allow(Dir).to receive(:glob).with("/etc/rc#{start}.d/[SK][0-9][0-9]chef").and_return(["/etc/rc#{start}.d/S20chef"])
+ end
end
it "says the service is enabled" do
@@ -75,18 +77,34 @@ describe Chef::Provider::Service::Debian do
expect(@provider.load_current_resource).to equal(@current_resource)
expect(@current_resource.enabled).to be_truthy
end
+
+ it "stores the start/stop priorities of the service" do
+ @provider.load_current_resource
+ expect(@provider.current_resource.priority).to eq(
+ {
+ "2" => [:start, "20"],
+ "3" => [:start, "20"],
+ "4" => [:start, "20"],
+ "5" => [:start, "20"],
+ "0" => [:stop, "20"],
+ "1" => [:stop, "20"],
+ "6" => [:stop, "20"]
+ })
+ end
end
context "when update-rc.d shows init isn't linked to rc*.d/" do
before do
allow(@provider).to receive(:assert_update_rcd_available)
- @status = double("Status", :exitstatus => 0)
- @stdout = StringIO.new(
- " Removing any system startup links for /etc/init.d/chef ...")
- @stderr = StringIO.new
- @status = double("Status", :exitstatus => 0, :stdout => @stdout)
- allow(@provider).to receive(:shell_out!).and_return(@status)
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+
+ allow(File).to receive(:readlines).with("/etc/init.d/chef").and_return(init_lines)
+
+ [0, 1, 6].each do |stop|
+ allow(Dir).to receive(:glob).with("/etc/rc#{stop}.d/[SK][0-9][0-9]chef").and_return([])
+ end
+ [2, 3, 4, 5].each do |start|
+ allow(Dir).to receive(:glob).with("/etc/rc#{start}.d/[SK][0-9][0-9]chef").and_return([])
+ end
end
it "says the service is disabled" do
@@ -100,150 +118,6 @@ describe Chef::Provider::Service::Debian do
end
end
- context "when update-rc.d fails" do
- before do
- @status = double("Status", :exitstatus => -1)
- allow(@provider).to receive(:popen4).and_return(@status)
- end
-
- it "raises an error" do
- @provider.define_resource_requirements
- expect {
- @provider.process_resource_requirements
- }.to raise_error(Chef::Exceptions::Service)
- end
- end
-
- { "Debian/Lenny and older" => {
- "linked" => {
- "stdout" => <<-STDOUT,
- Removing any system startup links for /etc/init.d/chef ...
- /etc/rc0.d/K20chef
- /etc/rc1.d/K20chef
- /etc/rc2.d/S20chef
- /etc/rc3.d/S20chef
- /etc/rc4.d/S20chef
- /etc/rc5.d/S20chef
- /etc/rc6.d/K20chef
- STDOUT
- "stderr" => "",
- "priorities" => {
- "0" => [:stop, "20"],
- "1" => [:stop, "20"],
- "2" => [:start, "20"],
- "3" => [:start, "20"],
- "4" => [:start, "20"],
- "5" => [:start, "20"],
- "6" => [:stop, "20"],
- },
- },
- "not linked" => {
- "stdout" => " Removing any system startup links for /etc/init.d/chef ...",
- "stderr" => "",
- },
- },
- "Debian/Squeeze and earlier" => {
- "linked" => {
- "stdout" => "update-rc.d: using dependency based boot sequencing",
- "stderr" => <<-STDERR,
-insserv: remove service /etc/init.d/../rc0.d/K20chef-client
- insserv: remove service /etc/init.d/../rc1.d/K20chef-client
- insserv: remove service /etc/init.d/../rc2.d/S20chef-client
- insserv: remove service /etc/init.d/../rc3.d/S20chef-client
- insserv: remove service /etc/init.d/../rc4.d/S20chef-client
- insserv: remove service /etc/init.d/../rc5.d/S20chef-client
- insserv: remove service /etc/init.d/../rc6.d/K20chef-client
- insserv: dryrun, not creating .depend.boot, .depend.start, and .depend.stop
- STDERR
- "priorities" => {
- "0" => [:stop, "20"],
- "1" => [:stop, "20"],
- "2" => [:start, "20"],
- "3" => [:start, "20"],
- "4" => [:start, "20"],
- "5" => [:start, "20"],
- "6" => [:stop, "20"],
- },
- },
- "not linked" => {
- "stdout" => "update-rc.d: using dependency based boot sequencing",
- "stderr" => "",
- },
- },
- "Debian/Wheezy and earlier, a service only starting at run level S" => {
- "linked" => {
- "stdout" => "",
- "stderr" => <<-STDERR,
-insserv: remove service /etc/init.d/../rc0.d/K06rpcbind
-insserv: remove service /etc/init.d/../rc1.d/K06rpcbind
-insserv: remove service /etc/init.d/../rc6.d/K06rpcbind
-insserv: remove service /etc/init.d/../rcS.d/S13rpcbind
-insserv: dryrun, not creating .depend.boot, .depend.start, and .depend.stop
- STDERR
- "priorities" => {
- "0" => [:stop, "06"],
- "1" => [:stop, "06"],
- "6" => [:stop, "06"],
- "S" => [:start, "13"],
- },
- },
- "not linked" => {
- "stdout" => "",
- "stderr" => "insserv: dryrun, not creating .depend.boot, .depend.start, and .depend.stop",
- },
- },
- }.each do |model, expected_results|
- context "on #{model}" do
- context "when update-rc.d shows init linked to rc*.d/" do
- before do
- allow(@provider).to receive(:assert_update_rcd_available)
-
- @stdout = StringIO.new(expected_results["linked"]["stdout"])
- @stderr = StringIO.new(expected_results["linked"]["stderr"])
- @status = double("Status", :exitstatus => 0, :stdout => @stdout)
- allow(@provider).to receive(:shell_out!).and_return(@status)
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
- end
-
- it "says the service is enabled" do
- expect(@provider.service_currently_enabled?(@provider.get_priority)).to be_truthy
- end
-
- it "stores the 'enabled' state" do
- allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource)
- expect(@provider.load_current_resource).to equal(@current_resource)
- expect(@current_resource.enabled).to be_truthy
- end
-
- it "stores the start/stop priorities of the service" do
- @provider.load_current_resource
- expect(@provider.current_resource.priority).to eq(expected_results["linked"]["priorities"])
- end
- end
-
- context "when update-rc.d shows init isn't linked to rc*.d/" do
- before do
- allow(@provider).to receive(:assert_update_rcd_available)
- @stdout = StringIO.new(expected_results["not linked"]["stdout"])
- @stderr = StringIO.new(expected_results["not linked"]["stderr"])
- @status = double("Status", :exitstatus => 0, :stdout => @stdout)
- allow(@provider).to receive(:shell_out!).and_return(@status)
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
- end
-
- it "says the service is disabled" do
- expect(@provider.service_currently_enabled?(@provider.get_priority)).to be_falsey
- end
-
- it "stores the 'disabled' state" do
- allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource)
- expect(@provider.load_current_resource).to equal(@current_resource)
- expect(@current_resource.enabled).to be_falsey
- end
- end
- end
- end
-
end
describe "action_enable" do
diff --git a/spec/unit/provider/service/freebsd_service_spec.rb b/spec/unit/provider/service/freebsd_service_spec.rb
index 68d4d63991..10eb3c1a14 100644
--- a/spec/unit/provider/service/freebsd_service_spec.rb
+++ b/spec/unit/provider/service/freebsd_service_spec.rb
@@ -257,10 +257,11 @@ PS_SAMPLE
end
context "when the enable variable partial matches (left) some other service and we are disabled" do
- let(:lines) { [
+ let(:lines) do
+ [
%Q{thing_#{new_resource.service_name}_enable="YES"},
%Q{#{new_resource.service_name}_enable="NO"},
- ] }
+ ] end
it "sets enabled based on the exact match (false)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be false
@@ -268,10 +269,11 @@ PS_SAMPLE
end
context "when the enable variable partial matches (right) some other service and we are disabled" do
- let(:lines) { [
+ let(:lines) do
+ [
%Q{#{new_resource.service_name}_thing_enable="YES"},
%Q{#{new_resource.service_name}_enable="NO"},
- ] }
+ ] end
it "sets enabled based on the exact match (false)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be false
@@ -279,10 +281,11 @@ PS_SAMPLE
end
context "when the enable variable partial matches (left) some other disabled service and we are enabled" do
- let(:lines) { [
+ let(:lines) do
+ [
%Q{thing_#{new_resource.service_name}_enable="NO"},
%Q{#{new_resource.service_name}_enable="YES"},
- ] }
+ ] end
it "sets enabled based on the exact match (true)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be true
@@ -290,10 +293,11 @@ PS_SAMPLE
end
context "when the enable variable partial matches (right) some other disabled service and we are enabled" do
- let(:lines) { [
+ let(:lines) do
+ [
%Q{#{new_resource.service_name}_thing_enable="NO"},
%Q{#{new_resource.service_name}_enable="YES"},
- ] }
+ ] end
it "sets enabled based on the exact match (true)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be true
diff --git a/spec/unit/provider/service/init_service_spec.rb b/spec/unit/provider/service/init_service_spec.rb
index 4b31e9c399..9d53303a76 100644
--- a/spec/unit/provider/service/init_service_spec.rb
+++ b/spec/unit/provider/service/init_service_spec.rb
@@ -83,7 +83,7 @@ PS
describe "when a status command has been specified" do
before do
- allow(@new_resource).to receive(:status_command).and_return("/etc/init.d/chefhasmonkeypants status")
+ @new_resource.status_command("/etc/init.d/chefhasmonkeypants status")
end
it "should run the services status command if one has been specified" do
@@ -95,7 +95,7 @@ PS
describe "when an init command has been specified" do
before do
- allow(@new_resource).to receive(:init_command).and_return("/opt/chef-server/service/erchef")
+ @new_resource.init_command("/opt/chef-server/service/erchef")
@provider = Chef::Provider::Service::Init.new(@new_resource, @run_context)
end
diff --git a/spec/unit/provider/service/invokercd_service_spec.rb b/spec/unit/provider/service/invokercd_service_spec.rb
index 57b13d0c51..7a7f1e0831 100644
--- a/spec/unit/provider/service/invokercd_service_spec.rb
+++ b/spec/unit/provider/service/invokercd_service_spec.rb
@@ -83,7 +83,7 @@ PS
describe "when a status command has been specified" do
before do
- allow(@new_resource).to receive(:status_command).and_return("/usr/sbin/invoke-rc.d chefhasmonkeypants status")
+ @new_resource.status_command("/usr/sbin/invoke-rc.d chefhasmonkeypants status")
end
it "should run the services status command if one has been specified" do
diff --git a/spec/unit/provider/service/macosx_spec.rb b/spec/unit/provider/service/macosx_spec.rb
index 12f97431ba..5f8bbc4a39 100644
--- a/spec/unit/provider/service/macosx_spec.rb
+++ b/spec/unit/provider/service/macosx_spec.rb
@@ -44,6 +44,7 @@ describe Chef::Provider::Service::Macosx do
context "when service name is given as" do
let(:node) { Chef::Node.new }
let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
let(:run_context) { Chef::RunContext.new(node, {}, events) }
let(:provider) { described_class.new(new_resource, run_context) }
let(:launchctl_stdout) { StringIO.new }
@@ -73,8 +74,12 @@ XML
end
let(:service_label) { "io.redis.redis-server" }
before do
+ allow(run_context).to receive(:logger).and_return(logger)
allow(Dir).to receive(:glob).and_return([plist], [])
- allow(Etc).to receive(:getlogin).and_return("igor")
+ @stat = double("File::Stat", { :uid => 501 })
+ allow(File).to receive(:stat).and_return(@stat)
+ @getpwuid = double("Etc::Passwd", { :name => "mikedodge04" })
+ allow(Etc).to receive(:getpwuid).and_return(@getpwuid)
allow(node).to receive(:[]).with("platform_version").and_return(platform_version)
cmd = "launchctl list #{service_label}"
allow(provider).to receive(:shell_out_with_systems_locale).
@@ -257,7 +262,7 @@ SVC_LIST
it "shows warning message if service is already running" do
allow(current_resource).to receive(:running).and_return(true)
- expect(Chef::Log).to receive(:debug).with("macosx_service[#{service_name}] already running, not starting")
+ expect(logger).to receive(:trace).with("macosx_service[#{service_name}] already running, not starting")
provider.start_service
end
@@ -289,7 +294,7 @@ SVC_LIST
it "shows warning message if service is not running" do
allow(current_resource).to receive(:running).and_return(false)
- expect(Chef::Log).to receive(:debug).with("macosx_service[#{service_name}] not running, not stopping")
+ expect(logger).to receive(:trace).with("macosx_service[#{service_name}] not running, not stopping")
provider.stop_service
end
@@ -322,7 +327,7 @@ SVC_LIST
it "stops and then starts service" do
expect(provider).to receive(:unload_service)
- expect(provider).to receive(:load_service);
+ expect(provider).to receive(:load_service)
provider.restart_service
end
diff --git a/spec/unit/provider/service/openbsd_service_spec.rb b/spec/unit/provider/service/openbsd_service_spec.rb
index b11015a63a..872a3bc400 100644
--- a/spec/unit/provider/service/openbsd_service_spec.rb
+++ b/spec/unit/provider/service/openbsd_service_spec.rb
@@ -174,10 +174,11 @@ describe Chef::Provider::Service::Openbsd do
end
context "when the enable variable partial matches (left) some other service and we are disabled" do
- let(:lines) { [
+ let(:lines) do
+ [
%Q{thing_#{provider.builtin_service_enable_variable_name}="YES"},
%Q{#{provider.builtin_service_enable_variable_name}="NO"},
- ] }
+ ] end
it "sets enabled based on the exact match (false)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be false
@@ -185,10 +186,11 @@ describe Chef::Provider::Service::Openbsd do
end
context "when the enable variable partial matches (right) some other service and we are disabled" do
- let(:lines) { [
+ let(:lines) do
+ [
%Q{#{provider.builtin_service_enable_variable_name}_thing="YES"},
%Q{#{provider.builtin_service_enable_variable_name}},
- ] }
+ ] end
it "sets enabled based on the exact match (false)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be false
@@ -196,10 +198,11 @@ describe Chef::Provider::Service::Openbsd do
end
context "when the enable variable partial matches (left) some other disabled service and we are enabled" do
- let(:lines) { [
+ let(:lines) do
+ [
%Q{thing_#{provider.builtin_service_enable_variable_name}="NO"},
%Q{#{provider.builtin_service_enable_variable_name}="YES"},
- ] }
+ ] end
it "sets enabled based on the exact match (true)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be true
@@ -207,10 +210,11 @@ describe Chef::Provider::Service::Openbsd do
end
context "when the enable variable partial matches (right) some other disabled service and we are enabled" do
- let(:lines) { [
+ let(:lines) do
+ [
%Q{#{provider.builtin_service_enable_variable_name}_thing="NO"},
%Q{#{provider.builtin_service_enable_variable_name}="YES"},
- ] }
+ ] end
it "sets enabled based on the exact match (true)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be true
diff --git a/spec/unit/provider/service/simple_service_spec.rb b/spec/unit/provider/service/simple_service_spec.rb
index 499e0cc2d3..1fdae83dad 100644
--- a/spec/unit/provider/service/simple_service_spec.rb
+++ b/spec/unit/provider/service/simple_service_spec.rb
@@ -106,7 +106,7 @@ NOMOCKINGSTRINGSPLZ
describe "when starting the service" do
it "should call the start command if one is specified" do
- allow(@new_resource).to receive(:start_command).and_return("#{@new_resource.start_command}")
+ @new_resource.start_command("#{@new_resource.start_command}")
expect(@provider).to receive(:shell_out_with_systems_locale!).with("#{@new_resource.start_command}")
@provider.start_service()
end
diff --git a/spec/unit/provider/service/solaris_smf_service_spec.rb b/spec/unit/provider/service/solaris_smf_service_spec.rb
index c6835bed64..a5148c236a 100644
--- a/spec/unit/provider/service/solaris_smf_service_spec.rb
+++ b/spec/unit/provider/service/solaris_smf_service_spec.rb
@@ -118,6 +118,7 @@ describe Chef::Provider::Service::Solaris do
it "should create a current resource with the name of the new resource" do
expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", { :returns => [0, 1] }).and_return(@enabled_svc_status)
expect(Chef::Resource::Service).to receive(:new).and_return(@current_resource)
+ expect(@provider.maintenance).to be_falsey
@provider.load_current_resource
end
@@ -162,7 +163,7 @@ describe Chef::Provider::Service::Solaris do
end
it "should call svcadm enable -s chef" do
- expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", { :returns => [0, 1] }).and_return(@enabled_svc_status)
+ expect(@provider).to receive(:shell_out!).twice.with("/bin/svcs", "-l", "chef", { :returns => [0, 1] }).and_return(@enabled_svc_status)
expect(@provider).not_to receive(:shell_out!).with("/usr/sbin/svcadm", "clear", @current_resource.service_name)
expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm", "enable", "-s", @current_resource.service_name).and_return(@success)
@provider.load_current_resource
@@ -172,7 +173,7 @@ describe Chef::Provider::Service::Solaris do
end
it "should call svcadm enable -s chef for start_service" do
- expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", { :returns => [0, 1] }).and_return(@enabled_svc_status)
+ expect(@provider).to receive(:shell_out!).twice.with("/bin/svcs", "-l", "chef", { :returns => [0, 1] }).and_return(@enabled_svc_status)
expect(@provider).not_to receive(:shell_out!).with("/usr/sbin/svcadm", "clear", @current_resource.service_name)
expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm", "enable", "-s", @current_resource.service_name).and_return(@success)
@provider.load_current_resource
@@ -182,7 +183,7 @@ describe Chef::Provider::Service::Solaris do
it "should call svcadm clear chef for start_service when state maintenance" do
# we are in maint mode
- expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", { :returns => [0, 1] }).and_return(@maintenance_svc_status)
+ expect(@provider).to receive(:shell_out!).twice.with("/bin/svcs", "-l", "chef", { :returns => [0, 1] }).and_return(@maintenance_svc_status)
expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm", "clear", @current_resource.service_name).and_return(@success)
expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm", "enable", "-s", @current_resource.service_name).and_return(@success)
@@ -198,6 +199,33 @@ describe Chef::Provider::Service::Solaris do
end
end
+ describe "when enabling the service recursively" do
+ before(:each) do
+ @provider.current_resource = @current_resource
+ end
+
+ it "should call svcadm enable -s -r chef" do
+ @new_resource.options("-r")
+ expect(@provider).to receive(:shell_out!).twice.with("/bin/svcs", "-l", "chef", { :returns => [0, 1] }).and_return(@enabled_svc_status)
+ expect(@provider).not_to receive(:shell_out!).with("/usr/sbin/svcadm", "clear", @current_resource.service_name)
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm", "enable", "-s", "-r", @current_resource.service_name).and_return(@success)
+ @provider.load_current_resource
+ expect(@provider.enable_service).to be_truthy
+ expect(@current_resource.enabled).to be_truthy
+ end
+
+ it "should call svcadm enable -s -r -t chef when passed an array of options" do
+ @new_resource.options(["-r", "-t"])
+ expect(@provider).to receive(:shell_out!).twice.with("/bin/svcs", "-l", "chef", { :returns => [0, 1] }).and_return(@enabled_svc_status)
+ expect(@provider).not_to receive(:shell_out!).with("/usr/sbin/svcadm", "clear", @current_resource.service_name)
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm", "enable", "-s", "-r", "-t", @current_resource.service_name).and_return(@success)
+ @provider.load_current_resource
+ expect(@provider.enable_service).to be_truthy
+ expect(@current_resource.enabled).to be_truthy
+ end
+
+ end
+
describe "when disabling the service" do
before(:each) do
@provider.current_resource = @current_resource
@@ -215,7 +243,16 @@ describe Chef::Provider::Service::Solaris do
expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", { :returns => [0, 1] }).and_return(@disabled_svc_status)
expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm", "disable", "-s", "chef").and_return(@success)
@provider.load_current_resource
- expect(@provider.stop_service).to be_truthy
+ expect(@provider.disable_service).to be_truthy
+ expect(@current_resource.enabled).to be_falsey
+ end
+
+ it "should call svcadm disable chef with options" do
+ @new_resource.options("-t")
+ expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", { :returns => [0, 1] }).and_return(@disabled_svc_status)
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm", "disable", "-s", "-t", "chef").and_return(@success)
+ @provider.load_current_resource
+ expect(@provider.disable_service).to be_truthy
expect(@current_resource.enabled).to be_falsey
end
diff --git a/spec/unit/provider/service/systemd_service_spec.rb b/spec/unit/provider/service/systemd_service_spec.rb
index e0a94127b7..a9c11b7c18 100644
--- a/spec/unit/provider/service/systemd_service_spec.rb
+++ b/spec/unit/provider/service/systemd_service_spec.rb
@@ -21,22 +21,15 @@ require "spec_helper"
describe Chef::Provider::Service::Systemd do
- let(:node) {
- node = Chef::Node.new
- node.default["etc"] = Hash.new
- node.default["etc"]["passwd"] = {
- "joe" => {
- "uid" => 10000,
- },
- }
- node
- }
+ let(:node) { Chef::Node.new }
let(:events) { Chef::EventDispatch::Dispatcher.new }
let(:run_context) { Chef::RunContext.new(node, {}, events) }
- let(:service_name) { "rsyslog.service" }
+ let(:service_name) { "rsyslog\\x2d.service" }
+
+ let(:service_name_escaped) { "rsyslog\\\\x2d.service" }
let(:new_resource) { Chef::Resource::Service.new(service_name) }
@@ -54,6 +47,7 @@ describe Chef::Provider::Service::Systemd do
before(:each) do
allow(Chef::Resource::Service).to receive(:new).with(service_name).and_return(current_resource)
+ allow(Etc).to receive(:getpwuid).and_return(OpenStruct.new(uid: 10000))
end
describe "load_current_resource" do
@@ -182,13 +176,13 @@ describe Chef::Provider::Service::Systemd do
context "when a user is not specified" do
it "should call '#{systemctl_path} --system start service_name' if no start command is specified" do
- expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --system start #{service_name}", {}).and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --system start #{service_name_escaped}", {}).and_return(shell_out_success)
provider.start_service
end
it "should not call '#{systemctl_path} --system start service_name' if it is already running" do
current_resource.running(true)
- expect(provider).not_to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --system start #{service_name}", {})
+ expect(provider).not_to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --system start #{service_name_escaped}", {})
provider.start_service
end
end
@@ -196,14 +190,14 @@ describe Chef::Provider::Service::Systemd do
context "when a user is specified" do
it "should call '#{systemctl_path} --user start service_name' if no start command is specified" do
current_resource.user("joe")
- expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --user start #{service_name}", { "environment" => { "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/10000/bus" }, "user" => "joe" }).and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --user start #{service_name_escaped}", { :environment => { "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/10000/bus" }, :user => "joe" }).and_return(shell_out_success)
provider.start_service
end
it "should not call '#{systemctl_path} --user start service_name' if it is already running" do
current_resource.running(true)
current_resource.user("joe")
- expect(provider).not_to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --user start #{service_name}", { "environment" => { "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/10000/bus" }, "user" => "joe" })
+ expect(provider).not_to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --user start #{service_name_escaped}", { :environment => { "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/10000/bus" }, :user => "joe" })
provider.start_service
end
end
@@ -217,7 +211,7 @@ describe Chef::Provider::Service::Systemd do
it "should call '#{systemctl_path} --system restart service_name' if no restart command is specified" do
current_resource.running(true)
- expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --system restart #{service_name}", {}).and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --system restart #{service_name_escaped}", {}).and_return(shell_out_success)
provider.restart_service
end
@@ -234,7 +228,7 @@ describe Chef::Provider::Service::Systemd do
context "when a reload command is not specified" do
it "should call '#{systemctl_path} --system reload service_name' if the service is running" do
current_resource.running(true)
- expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --system reload #{service_name}", {}).and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --system reload #{service_name_escaped}", {}).and_return(shell_out_success)
provider.reload_service
end
@@ -255,13 +249,13 @@ describe Chef::Provider::Service::Systemd do
it "should call '#{systemctl_path} --system stop service_name' if no stop command is specified" do
current_resource.running(true)
- expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --system stop #{service_name}", {}).and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --system stop #{service_name_escaped}", {}).and_return(shell_out_success)
provider.stop_service
end
it "should not call '#{systemctl_path} --system stop service_name' if it is already stopped" do
current_resource.running(false)
- expect(provider).not_to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --system stop #{service_name}", {})
+ expect(provider).not_to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --system stop #{service_name_escaped}", {})
provider.stop_service
end
end
@@ -274,12 +268,12 @@ describe Chef::Provider::Service::Systemd do
end
it "should call '#{systemctl_path} --system enable service_name' to enable the service" do
- expect(provider).to receive(:shell_out!).with("#{systemctl_path} --system enable #{service_name}", {}).and_return(shell_out_success)
+ expect(provider).to receive(:shell_out!).with("#{systemctl_path} --system enable #{service_name_escaped}", {}).and_return(shell_out_success)
provider.enable_service
end
it "should call '#{systemctl_path} --system disable service_name' to disable the service" do
- expect(provider).to receive(:shell_out!).with("#{systemctl_path} --system disable #{service_name}", {}).and_return(shell_out_success)
+ expect(provider).to receive(:shell_out!).with("#{systemctl_path} --system disable #{service_name_escaped}", {}).and_return(shell_out_success)
provider.disable_service
end
end
@@ -292,12 +286,12 @@ describe Chef::Provider::Service::Systemd do
end
it "should call '#{systemctl_path} --system mask service_name' to mask the service" do
- expect(provider).to receive(:shell_out!).with("#{systemctl_path} --system mask #{service_name}", {}).and_return(shell_out_success)
+ expect(provider).to receive(:shell_out!).with("#{systemctl_path} --system mask #{service_name_escaped}", {}).and_return(shell_out_success)
provider.mask_service
end
it "should call '#{systemctl_path} --system unmask service_name' to unmask the service" do
- expect(provider).to receive(:shell_out!).with("#{systemctl_path} --system unmask #{service_name}", {}).and_return(shell_out_success)
+ expect(provider).to receive(:shell_out!).with("#{systemctl_path} --system unmask #{service_name_escaped}", {}).and_return(shell_out_success)
provider.unmask_service
end
end
@@ -310,12 +304,12 @@ describe Chef::Provider::Service::Systemd do
end
it "should return true if '#{systemctl_path} --system is-active service_name' returns 0" do
- expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-active #{service_name} --quiet", {}).and_return(shell_out_success)
+ expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-active #{service_name_escaped} --quiet", {}).and_return(shell_out_success)
expect(provider.is_active?).to be true
end
it "should return false if '#{systemctl_path} --system is-active service_name' returns anything except 0" do
- expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-active #{service_name} --quiet", {}).and_return(shell_out_failure)
+ expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-active #{service_name_escaped} --quiet", {}).and_return(shell_out_failure)
expect(provider.is_active?).to be false
end
end
@@ -328,12 +322,12 @@ describe Chef::Provider::Service::Systemd do
end
it "should return true if '#{systemctl_path} --system is-enabled service_name' returns 0" do
- expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-enabled #{service_name} --quiet", {}).and_return(shell_out_success)
+ expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-enabled #{service_name_escaped} --quiet", {}).and_return(shell_out_success)
expect(provider.is_enabled?).to be true
end
it "should return false if '#{systemctl_path} --system is-enabled service_name' returns anything except 0" do
- expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-enabled #{service_name} --quiet", {}).and_return(shell_out_failure)
+ expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-enabled #{service_name_escaped} --quiet", {}).and_return(shell_out_failure)
expect(provider.is_enabled?).to be false
end
end
@@ -346,22 +340,22 @@ describe Chef::Provider::Service::Systemd do
end
it "should return true if '#{systemctl_path} --system is-enabled service_name' returns 'masked' and returns anything except 0" do
- expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-enabled #{service_name}", {}).and_return(double(:stdout => "masked", :exitstatus => shell_out_failure))
+ expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-enabled #{service_name_escaped}", {}).and_return(double(:stdout => "masked", :exitstatus => shell_out_failure))
expect(provider.is_masked?).to be true
end
it "should return true if '#{systemctl_path} --system is-enabled service_name' outputs 'masked-runtime' and returns anything except 0" do
- expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-enabled #{service_name}", {}).and_return(double(:stdout => "masked-runtime", :exitstatus => shell_out_failure))
+ expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-enabled #{service_name_escaped}", {}).and_return(double(:stdout => "masked-runtime", :exitstatus => shell_out_failure))
expect(provider.is_masked?).to be true
end
it "should return false if '#{systemctl_path} --system is-enabled service_name' returns 0" do
- expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-enabled #{service_name}", {}).and_return(double(:stdout => "enabled", :exitstatus => shell_out_success))
+ expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-enabled #{service_name_escaped}", {}).and_return(double(:stdout => "enabled", :exitstatus => shell_out_success))
expect(provider.is_masked?).to be false
end
it "should return false if '#{systemctl_path} --system is-enabled service_name' returns anything except 0 and outputs an error'" do
- expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-enabled #{service_name}", {}).and_return(double(:stdout => "Failed to get unit file state for #{service_name}: No such file or directory", :exitstatus => shell_out_failure))
+ expect(provider).to receive(:shell_out).with("#{systemctl_path} --system is-enabled #{service_name_escaped}", {}).and_return(double(:stdout => "Failed to get unit file state for #{service_name}: No such file or directory", :exitstatus => shell_out_failure))
expect(provider.is_masked?).to be false
end
end
diff --git a/spec/unit/provider/service/upstart_service_spec.rb b/spec/unit/provider/service/upstart_service_spec.rb
index fd9ea0573c..d6ecfc2c43 100644
--- a/spec/unit/provider/service/upstart_service_spec.rb
+++ b/spec/unit/provider/service/upstart_service_spec.rb
@@ -71,12 +71,8 @@ describe Chef::Provider::Service::Upstart do
@current_resource = Chef::Resource::Service.new("rsyslog")
allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource)
- @status = double("Status", :exitstatus => 0)
- allow(@provider).to receive(:popen4).and_return(@status)
- @stdin = StringIO.new
- @stdout = StringIO.new
- @stderr = StringIO.new
- @pid = double("PID")
+ @status = double("Status", exitstatus: 0, stdout: "", stderr: "")
+ allow(@provider).to receive(:shell_out).and_return(@status)
allow(::File).to receive(:exists?).and_return(true)
allow(::File).to receive(:open).and_return(true)
@@ -100,59 +96,65 @@ describe Chef::Provider::Service::Upstart do
end
it "should run '/sbin/status rsyslog'" do
- expect(@provider).to receive(:popen4).with("/sbin/status rsyslog").and_return(@status)
+ expect(@provider).to receive(:shell_out).with("/sbin/status rsyslog").and_return(@status)
@provider.load_current_resource
end
describe "when the status command uses the new format" do
- before do
+ it "should set running to true if the goal state is 'start'" do
+ expect(@status).to receive(:stdout).and_return("rsyslog start/running")
+ @provider.load_current_resource
+ expect(@current_resource.running).to be_truthy
end
- it "should set running to true if the status command returns 0" do
- @stdout = StringIO.new("rsyslog start/running")
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ it "should set running to true if the goal state is 'start' but current state is not 'running'" do
+ expect(@status).to receive(:stdout).and_return("rsyslog start/starting")
@provider.load_current_resource
expect(@current_resource.running).to be_truthy
end
- it "should set running to false if the status command returns anything except 0" do
- @stdout = StringIO.new("rsyslog stop/waiting")
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ it "should set running to false if the goal state is 'stop'" do
+ expect(@status).to receive(:stdout).and_return("rsyslog stop/waiting")
@provider.load_current_resource
expect(@current_resource.running).to be_falsey
end
end
describe "when the status command uses the new format with an instance" do
- before do
+ it "should set running to true if the goal state is 'start'" do
+ expect(@status).to receive(:stdout).and_return("rsyslog (test) start/running, process 100")
+ @provider.load_current_resource
+ expect(@current_resource.running).to be_truthy
end
- it "should set running to true if the status command returns 0" do
- @stdout = StringIO.new("rsyslog (test) start/running, process 100")
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ it "should set running to true if the goal state is 'start' but current state is not 'running'" do
+ expect(@status).to receive(:stdout).and_return("rsyslog (test) start/starting, process 100")
@provider.load_current_resource
expect(@current_resource.running).to be_truthy
end
- it "should set running to false if the status command returns anything except 0" do
- @stdout = StringIO.new("rsyslog (test) stop/waiting, process 100")
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ it "should set running to false if the goal state is 'stop'" do
+ expect(@status).to receive(:stdout).and_return("rsyslog (test) stop/waiting, process 100")
@provider.load_current_resource
expect(@current_resource.running).to be_falsey
end
end
describe "when the status command uses the old format" do
- it "should set running to true if the status command returns 0" do
- @stdout = StringIO.new("rsyslog (start) running, process 32225")
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ it "should set running to true if the goal state is 'start'" do
+ expect(@status).to receive(:stdout).and_return("rsyslog (start) running, process 32225")
+ @provider.load_current_resource
+ expect(@current_resource.running).to be_truthy
+ end
+
+ it "should set running to true if the goal state is 'start' but current state is not 'running'" do
+ expect(@status).to receive(:stdout).and_return("rsyslog (start) starting, process 32225")
@provider.load_current_resource
expect(@current_resource.running).to be_truthy
end
- it "should set running to false if the status command returns anything except 0" do
- @stdout = StringIO.new("rsyslog (stop) waiting")
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ it "should set running to false if the goal state is 'stop'" do
+ expect(@status).to receive(:stdout).and_return("rsyslog (stop) waiting")
@provider.load_current_resource
expect(@current_resource.running).to be_falsey
end
@@ -192,7 +194,7 @@ describe Chef::Provider::Service::Upstart do
describe "when a status command has been specified" do
before do
- allow(@new_resource).to receive(:status_command).and_return("/bin/chefhasmonkeypants status")
+ @new_resource.status_command("/bin/chefhasmonkeypants status")
end
it "should run the services status command if one has been specified" do
@@ -214,8 +216,8 @@ describe Chef::Provider::Service::Upstart do
end
end
- it "should track state when we fail to obtain service status via upstart_state" do
- expect(@provider).to receive(:upstart_state).and_raise Chef::Exceptions::Exec
+ it "should track state when we fail to obtain service status via upstart_goal_state" do
+ expect(@provider).to receive(:upstart_goal_state).and_raise Chef::Exceptions::Exec
@provider.load_current_resource
expect(@provider.instance_variable_get("@command_success")).to eq(false)
end
@@ -263,18 +265,20 @@ describe Chef::Provider::Service::Upstart do
end
it "should call the start command if one is specified" do
- allow(@new_resource).to receive(:start_command).and_return("/sbin/rsyslog startyousillysally")
+ @provider.upstart_service_running = false
+ @new_resource.start_command("/sbin/rsyslog startyousillysally")
expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog startyousillysally")
@provider.start_service()
end
it "should call '/sbin/start service_name' if no start command is specified" do
+ @provider.upstart_service_running = false
expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/start #{@new_resource.service_name}").and_return(shell_out_success)
@provider.start_service()
end
it "should not call '/sbin/start service_name' if it is already running" do
- allow(@current_resource).to receive(:running).and_return(true)
+ @provider.upstart_service_running = true
expect(@provider).not_to receive(:shell_out_with_systems_locale!)
@provider.start_service()
end
@@ -290,18 +294,21 @@ describe Chef::Provider::Service::Upstart do
it "should call the restart command if one is specified" do
allow(@current_resource).to receive(:running).and_return(true)
- allow(@new_resource).to receive(:restart_command).and_return("/sbin/rsyslog restartyousillysally")
+ @new_resource.restart_command("/sbin/rsyslog restartyousillysally")
expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog restartyousillysally")
@provider.restart_service()
end
- it "should call '/sbin/restart service_name' if no restart command is specified" do
- allow(@current_resource).to receive(:running).and_return(true)
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/restart #{@new_resource.service_name}").and_return(shell_out_success)
+ it "should call start/sleep/stop if no restart command is specified" do
+ @provider.upstart_service_running = true
+ expect(@provider).to receive(:stop_service)
+ expect(@provider).to receive(:sleep).with(1)
+ expect(@provider).to receive(:start_service)
@provider.restart_service()
end
it "should call '/sbin/start service_name' if restart_service is called for a stopped service" do
+ @provider.upstart_service_running = false
allow(@current_resource).to receive(:running).and_return(false)
expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/start #{@new_resource.service_name}").and_return(shell_out_success)
@provider.restart_service()
@@ -309,7 +316,7 @@ describe Chef::Provider::Service::Upstart do
it "should call the reload command if one is specified" do
allow(@current_resource).to receive(:running).and_return(true)
- allow(@new_resource).to receive(:reload_command).and_return("/sbin/rsyslog reloadyousillysally")
+ @new_resource.reload_command("/sbin/rsyslog reloadyousillysally")
expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog reloadyousillysally")
@provider.reload_service()
end
@@ -321,22 +328,24 @@ describe Chef::Provider::Service::Upstart do
end
it "should call the stop command if one is specified" do
- allow(@current_resource).to receive(:running).and_return(true)
- allow(@new_resource).to receive(:stop_command).and_return("/sbin/rsyslog stopyousillysally")
+ @provider.upstart_service_running = true
+ @new_resource.stop_command("/sbin/rsyslog stopyousillysally")
expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog stopyousillysally")
@provider.stop_service()
end
it "should call '/sbin/stop service_name' if no stop command is specified" do
- allow(@current_resource).to receive(:running).and_return(true)
+ @provider.upstart_service_running = true
expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/stop #{@new_resource.service_name}").and_return(shell_out_success)
@provider.stop_service()
end
it "should not call '/sbin/stop service_name' if it is already stopped" do
+ @provider.upstart_service_running = false
allow(@current_resource).to receive(:running).and_return(false)
expect(@provider).not_to receive(:shell_out_with_systems_locale!).with("/sbin/stop #{@new_resource.service_name}")
@provider.stop_service()
+ expect(@upstart_service_running).to be_falsey
end
end
end
diff --git a/spec/unit/provider/service/windows_spec.rb b/spec/unit/provider/service/windows_spec.rb
index f944d8f6c6..24c3e07f39 100644
--- a/spec/unit/provider/service/windows_spec.rb
+++ b/spec/unit/provider/service/windows_spec.rb
@@ -20,16 +20,72 @@
require "spec_helper"
require "mixlib/shellout"
-describe Chef::Provider::Service::Windows, "load_current_resource" do
+describe Chef::Provider::Service::Windows, "load_current_resource", :windows_only do
include_context "Win32"
- let(:new_resource) { Chef::Resource::WindowsService.new("chef") }
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+
+ let(:chef_service_name) { "chef-client" }
+ let(:new_resource) { Chef::Resource::WindowsService.new(chef_service_name) }
+
+ # Actual response from Win32::Service.config_info('chef-client')
+ let(:chef_service_binary_path_name) do
+ 'C:\\opscode\\chef\\embedded\\bin\\ruby.exe C:\\opscode\\chef\\bin\\chef-windows-service'
+ end
+ let(:chef_service_config_info) do
+ double("Struct::ServiceConfigInfo",
+ service_type: "own process",
+ start_type: "auto start",
+ error_control: "ignore",
+ binary_path_name: chef_service_binary_path_name,
+ load_order_group: "",
+ tag_id: 0,
+ dependencies: ["Winmgmt"],
+ service_start_name: "LocalSystem",
+ display_name: "Chef Client Service"
+ )
+ end
+
+ # Actual response from Win32::Service.services
+ let(:chef_service_info) do
+ double("Struct::ServiceInfo",
+ service_name: chef_service_name,
+ display_name: "Chef Client Service",
+ service_type: "own process",
+ current_state: "running",
+ controls_accepted: [],
+ win32_exit_code: 1077,
+ service_specific_exit_code: 0,
+ check_point: 0,
+ wait_hint: 0,
+ binary_path_name: chef_service_binary_path_name,
+ start_type: "auto start",
+ error_control: "ignore",
+ load_order_group: "",
+ tag_id: 0,
+ start_name: "LocalSystem",
+ dependencies: ["Winmgmt"],
+ description: "Runs Chef Client on regular, configurable intervals.",
+ interactive: false,
+ pid: 0,
+ service_flags: 0,
+ reset_period: 0,
+ reboot_message: nil,
+ command: nil,
+ num_actions: 0,
+ actions: nil,
+ delayed_start: 1
+ )
+ end
+
let(:provider) do
- prvdr = Chef::Provider::Service::Windows.new(new_resource,
- Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new))
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ allow(run_context).to receive(:logger).and_return(logger)
+ prvdr = Chef::Provider::Service::Windows.new(new_resource, run_context)
prvdr.current_resource = Chef::Resource::WindowsService.new("current-chef")
prvdr
end
+
let(:service_right) { Chef::Provider::Service::Windows::SERVICE_RIGHT }
before(:all) do
@@ -43,11 +99,35 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
double("StatusStruct", :current_state => "running"))
- allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
- double("ConfigStruct", :start_type => "auto start"))
+ allow(Win32::Service).to receive(:config_info).with(new_resource.service_name)
+ .and_return(chef_service_config_info)
+
+ # Real response from Win32::Service.services
+ allow(Win32::Service).to receive(:services).and_return([
+ # Add chef_service_info to our stubbed response so our tests that are expecting the service to exist work
+ chef_service_info,
+ double("Struct::ServiceInfo", service_name: "ACPI", display_name: "Microsoft ACPI Driver", service_type: "kernel driver", current_state: "running", controls_accepted: ["stop"], win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: '\\SystemRoot\\System32\\drivers\\ACPI.sys', start_type: "boot start", error_control: "critical", load_order_group: "Core", tag_id: 2, start_name: "", dependencies: [], description: "", interactive: false, pid: 0, service_flags: 0, reset_period: 0, reboot_message: nil, command: nil, num_actions: 0, actions: nil, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "cdrom", display_name: "CD-ROM Driver", service_type: "kernel driver", current_state: "running", controls_accepted: ["stop"], win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: '\\SystemRoot\\System32\\drivers\\cdrom.sys', start_type: "system start", error_control: "normal", load_order_group: "SCSI CDROM Class", tag_id: 3, start_name: "", dependencies: [], description: "", interactive: false, pid: 0, service_flags: 0, reset_period: 0, reboot_message: nil, command: nil, num_actions: 0, actions: nil, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "CryptSvc", display_name: "Cryptographic Services", service_type: "share process", current_state: "running", controls_accepted: ["shutdown", "stop", "session change"], win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: 'C:\\Windows\\system32\\svchost.exe -k NetworkService', start_type: "auto start", error_control: "normal", load_order_group: "", tag_id: 0, start_name: 'NT Authority\\NetworkService', dependencies: ["RpcSs"], description: "Provides three management services: Catalog Database Service, which confirms the signatures of Windows files and allows new programs to be installed; Protected Root Service, which adds and removes Trusted Root Certification Authority certificates from this computer; and Automatic Root Certificate Update Service, which retrieves root certificates from Windows Update and enable scenarios such as SSL. If this service is stopped, these management services will not function properly. If this service is disabled, any services that explicitly depend on it will fail to start.", interactive: false, pid: 932, service_flags: 0, reset_period: 86400, reboot_message: nil, command: nil, num_actions: 3, actions: { 1 => { action_type: "restart", delay: 60000 }, 2 => { action_type: "none", delay: 0 }, 3 => { action_type: "none", delay: 0 } }, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "DcomLaunch", display_name: "DCOM Server Process Launcher", service_type: "share process", current_state: "running", controls_accepted: ["session change"], win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: 'C:\\Windows\\system32\\svchost.exe -k DcomLaunch', start_type: "auto start", error_control: "normal", load_order_group: "COM Infrastructure", tag_id: 0, start_name: "LocalSystem", dependencies: [], description: "The DCOMLAUNCH service launches COM and DCOM servers in response to object activation requests. If this service is stopped or disabled, programs using COM or DCOM will not function properly. It is strongly recommended that you have the DCOMLAUNCH service running.", interactive: false, pid: 552, service_flags: 0, reset_period: 0, reboot_message: nil, command: nil, num_actions: 1, actions: { 1 => { action_type: "reboot", delay: 60000 } }, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "Dfsc", display_name: "DFS Namespace Client Driver", service_type: "file system driver", current_state: "running", controls_accepted: ["stop"], win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: 'System32\\Drivers\\dfsc.sys', start_type: "system start", error_control: "normal", load_order_group: "Network", tag_id: 0, start_name: "", dependencies: ["Mup"], description: "Client driver for access to DFS Namespaces", interactive: false, pid: 0, service_flags: 0, reset_period: 0, reboot_message: nil, command: nil, num_actions: 0, actions: nil, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "Dhcp", display_name: "DHCP Client", service_type: "share process", current_state: "running", controls_accepted: %w{shutdown stop}, win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: 'C:\\Windows\\system32\\svchost.exe -k LocalServiceNetworkRestricted', start_type: "auto start", error_control: "normal", load_order_group: "TDI", tag_id: 0, start_name: 'NT Authority\\LocalService', dependencies: %w{NSI Tdx Afd}, description: "Registers and updates IP addresses and DNS records for this computer. If this service is stopped, this computer will not receive dynamic IP addresses and DNS updates. If this service is disabled, any services that explicitly depend on it will fail to start.", interactive: false, pid: 780, service_flags: 0, reset_period: 86400, reboot_message: nil, command: nil, num_actions: 3, actions: { 1 => { action_type: "restart", delay: 120000 }, 2 => { action_type: "restart", delay: 300000 }, 3 => { action_type: "none", delay: 0 } }, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "EventLog", display_name: "Windows Event Log", service_type: "share process", current_state: "running", controls_accepted: %w{shutdown stop}, win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: 'C:\\Windows\\System32\\svchost.exe -k LocalServiceNetworkRestricted', start_type: "auto start", error_control: "normal", load_order_group: "Event Log", tag_id: 0, start_name: 'NT AUTHORITY\\LocalService', dependencies: [], description: "This service manages events and event logs. It supports logging events, querying events, subscribing to events, archiving event logs, and managing event metadata. It can display events in both XML and plain text format. Stopping this service may compromise security and reliability of the system.", interactive: false, pid: 780, service_flags: 0, reset_period: 86400, reboot_message: nil, command: nil, num_actions: 3, actions: { 1 => { action_type: "restart", delay: 60000 }, 2 => { action_type: "restart", delay: 120000 }, 3 => { action_type: "none", delay: 0 } }, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "EventSystem", display_name: "COM+ Event System", service_type: "share process", current_state: "running", controls_accepted: ["stop"], win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: 'C:\\Windows\\system32\\svchost.exe -k LocalService', start_type: "auto start", error_control: "normal", load_order_group: "", tag_id: 0, start_name: 'NT AUTHORITY\\LocalService', dependencies: ["rpcss"], description: "Supports System Event Notification Service (SENS), which provides automatic distribution of events to subscribing Component Object Model (COM) components. If the service is stopped, SENS will close and will not be able to provide logon and logoff notifications. If this service is disabled, any services that explicitly depend on it will fail to start.", interactive: false, pid: 844, service_flags: 0, reset_period: 86400, reboot_message: nil, command: nil, num_actions: 3, actions: { 1 => { action_type: "restart", delay: 1000 }, 2 => { action_type: "restart", delay: 5000 }, 3 => { action_type: "none", delay: 0 } }, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "LanmanWorkstation", display_name: "Workstation", service_type: "share process", current_state: "running", controls_accepted: ["pause continue", "stop", "power event"], win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: 'C:\\Windows\\System32\\svchost.exe -k NetworkService', start_type: "auto start", error_control: "normal", load_order_group: "NetworkProvider", tag_id: 0, start_name: 'NT AUTHORITY\\NetworkService', dependencies: %w{Bowser MRxSmb20 NSI}, description: "Creates and maintains client network connections to remote servers using the SMB protocol. If this service is stopped, these connections will be unavailable. If this service is disabled, any services that explicitly depend on it will fail to start.", interactive: false, pid: 932, service_flags: 0, reset_period: 86400, reboot_message: nil, command: nil, num_actions: 3, actions: { 1 => { action_type: "restart", delay: 60000 }, 2 => { action_type: "restart", delay: 120000 }, 3 => { action_type: "none", delay: 0 } }, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "lmhosts", display_name: "TCP/IP NetBIOS Helper", service_type: "share process", current_state: "running", controls_accepted: ["stop"], win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: 'C:\\Windows\\system32\\svchost.exe -k LocalServiceNetworkRestricted', start_type: "auto start", error_control: "normal", load_order_group: "TDI", tag_id: 0, start_name: 'NT AUTHORITY\\LocalService', dependencies: %w{NetBT Afd}, description: "Provides support for the NetBIOS over TCP/IP (NetBT) service and NetBIOS name resolution for clients on the network, therefore enabling users to share files, print, and log on to the network. If this service is stopped, these functions might be unavailable. If this service is disabled, any services that explicitly depend on it will fail to start.", interactive: false, pid: 780, service_flags: 0, reset_period: 86400, reboot_message: nil, command: nil, num_actions: 3, actions: { 1 => { action_type: "restart", delay: 100 }, 2 => { action_type: "restart", delay: 100 }, 3 => { action_type: "none", delay: 0 } }, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "MpsSvc", display_name: "Windows Firewall", service_type: "share process", current_state: "running", controls_accepted: ["stop"], win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: 'C:\\Windows\\system32\\svchost.exe -k LocalServiceNoNetwork', start_type: "auto start", error_control: "normal", load_order_group: "NetworkProvider", tag_id: 0, start_name: 'NT Authority\\LocalService', dependencies: %w{mpsdrv bfe}, description: "Windows Firewall helps protect your computer by preventing unauthorized users from gaining access to your computer through the Internet or a network.", interactive: false, pid: 340, service_flags: 0, reset_period: 86400, reboot_message: nil, command: nil, num_actions: 3, actions: { 1 => { action_type: "restart", delay: 120000 }, 2 => { action_type: "restart", delay: 300000 }, 3 => { action_type: "none", delay: 0 } }, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "mrxsmb", display_name: "SMB MiniRedirector Wrapper and Engine", service_type: "file system driver", current_state: "running", controls_accepted: ["stop"], win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: 'system32\\DRIVERS\\mrxsmb.sys', start_type: "demand start", error_control: "normal", load_order_group: "Network", tag_id: 5, start_name: "", dependencies: ["rdbss"], description: "Implements the framework for the SMB filesystem redirector", interactive: false, pid: 0, service_flags: 0, reset_period: 0, reboot_message: nil, command: nil, num_actions: 0, actions: nil, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "Ntfs", display_name: "Ntfs", service_type: "file system driver", current_state: "running", controls_accepted: ["stop"], win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: "", start_type: "demand start", error_control: "normal", load_order_group: "Boot File System", tag_id: 0, start_name: "", dependencies: [], description: "", interactive: false, pid: 0, service_flags: 0, reset_period: 0, reboot_message: nil, command: nil, num_actions: 0, actions: nil, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "Tcpip", display_name: "TCP/IP Protocol Driver", service_type: "kernel driver", current_state: "running", controls_accepted: ["stop"], win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: '\\SystemRoot\\System32\\drivers\\tcpip.sys', start_type: "boot start", error_control: "normal", load_order_group: "PNP_TDI", tag_id: 3, start_name: "", dependencies: [], description: "TCP/IP Protocol Driver", interactive: false, pid: 0, service_flags: 0, reset_period: 0, reboot_message: nil, command: nil, num_actions: 0, actions: nil, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "W32Time", display_name: "Windows Time", service_type: "share process", current_state: "running", controls_accepted: ["netbind change", "param change", "shutdown", "stop", "hardware profile change", "power event"], win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: 'C:\\Windows\\system32\\svchost.exe -k LocalService', start_type: "demand start", error_control: "normal", load_order_group: "", tag_id: 0, start_name: 'NT AUTHORITY\\LocalService', dependencies: [], description: "Maintains date and time synchronization on all clients and servers in the network. If this service is stopped, date and time synchronization will be unavailable. If this service is disabled, any services that explicitly depend on it will fail to start.", interactive: false, pid: 844, service_flags: 0, reset_period: 86400, reboot_message: nil, command: nil, num_actions: 3, actions: { 1 => { action_type: "restart", delay: 60000 }, 2 => { action_type: "restart", delay: 120000 }, 3 => { action_type: "none", delay: 0 } }, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "Winmgmt", display_name: "Windows Management Instrumentation", service_type: "share process", current_state: "running", controls_accepted: ["pause continue", "shutdown", "stop"], win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: 'C:\\Windows\\system32\\svchost.exe -k netsvcs', start_type: "auto start", error_control: "ignore", load_order_group: "", tag_id: 0, start_name: "localSystem", dependencies: ["RPCSS"], description: "Provides a common interface and object model to access management information about operating system, devices, applications and services. If this service is stopped, most Windows-based software will not function properly. If this service is disabled, any services that explicitly depend on it will fail to start.", interactive: false, pid: 812, service_flags: 0, reset_period: 86400, reboot_message: nil, command: nil, num_actions: 3, actions: { 1 => { action_type: "restart", delay: 120000 }, 2 => { action_type: "restart", delay: 300000 }, 3 => { action_type: "none", delay: 0 } }, delayed_start: 0),
+ double("Struct::ServiceInfo", service_name: "WinRM", display_name: "Windows Remote Management (WS-Management)", service_type: "share process", current_state: "running", controls_accepted: %w{shutdown stop}, win32_exit_code: 0, service_specific_exit_code: 0, check_point: 0, wait_hint: 0, binary_path_name: 'C:\\Windows\\System32\\svchost.exe -k NetworkService', start_type: "auto start", error_control: "normal", load_order_group: "", tag_id: 0, start_name: 'NT AUTHORITY\\NetworkService', dependencies: %w{RPCSS HTTP}, description: "Windows Remote Management (WinRM) service implements the WS-Management protocol for remote management. WS-Management is a standard web services protocol used for remote software and hardware management. The WinRM service listens on the network for WS-Management requests and processes them. The WinRM Service needs to be configured with a listener using winrm.cmd command line tool or through Group Policy in order for it to listen over the network. The WinRM service provides access to WMI data and enables event collection. Event collection and subscription to events require that the service is running. WinRM messages use HTTP and HTTPS as transports. The WinRM service does not depend on IIS but is preconfigured to share a port with IIS on the same machine. The WinRM service reserves the /wsman URL prefix. To prevent conflicts with IIS, administrators should ensure that any websites hosted on IIS do not use the /wsman URL prefix.", interactive: false, pid: 932, service_flags: 0, reset_period: 86400, reboot_message: nil, command: nil, num_actions: 3, actions: { 1 => { action_type: "restart", delay: 120000 }, 2 => { action_type: "restart", delay: 300000 }, 3 => { action_type: "none", delay: 0 } }, delayed_start: 0),
+ ])
allow(Win32::Service).to receive(:exists?).and_return(true)
allow(Win32::Service).to receive(:configure).and_return(Win32::Service)
allow(Chef::ReservedNames::Win32::Security).to receive(:get_account_right).and_return([])
+ allow(Chef::ReservedNames::Win32::Security).to receive(:add_account_right).with("LocalSystem", "SeServiceLogonRight").and_return(0)
end
after(:each) do
@@ -58,28 +138,365 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
it "sets the current resources service name to the new resources service name" do
provider.load_current_resource
- expect(provider.current_resource.service_name).to eq("chef")
+ expect(provider.current_resource.service_name).to eq(chef_service_name)
end
it "returns the current resource" do
expect(provider.load_current_resource).to equal(provider.current_resource)
end
- it "sets the current resources status" do
- provider.load_current_resource
- expect(provider.current_resource.running).to be_truthy
- end
-
it "sets the current resources start type" do
provider.load_current_resource
expect(provider.current_resource.enabled).to be_truthy
end
- it "does not set the current resources start type if it is neither AUTO START or DISABLED" do
- allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
- double("ConfigStruct", :start_type => "manual"))
- provider.load_current_resource
- expect(provider.current_resource.enabled).to be_nil
+ context "service does not exist" do
+ before do
+ allow(Win32::Service).to receive(:exists?).with(chef_service_name).and_return(false)
+ end
+
+ %w{running enabled startup_type error_control binary_path_name
+ load_order_group dependencies run_as_user display_name }.each do |prop|
+ it "does not set #{prop}" do
+ expect(provider.current_resource.running).to be_nil
+ end
+ end
+ end
+
+ context "service exists" do
+ before do
+ allow(Win32::Service).to receive(:exists?).with(chef_service_name).and_return(true)
+ allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
+ double("Struct::ServiceConfigInfo",
+ service_type: "share process",
+ start_type: "demand start",
+ error_control: "normal",
+ binary_path_name: 'C:\\Windows\\system32\\svchost.exe -k LocalServiceNetworkRestricted',
+ load_order_group: "TDI",
+ tag_id: 0,
+ dependencies: %w{NSI Tdx Afd},
+ service_start_name: 'NT Authority\\LocalService',
+ display_name: "DHCP Client"
+ ))
+ end
+
+ context "startup_type is neither :automatic or :disabled" do
+ before do
+ allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
+ double("Struct::ServiceConfigInfo",
+ service_type: "share process",
+ start_type: "demand start",
+ error_control: "normal",
+ binary_path_name: 'C:\\Windows\\system32\\svchost.exe -k LocalServiceNetworkRestricted',
+ load_order_group: "TDI",
+ tag_id: 0,
+ dependencies: %w{NSI Tdx Afd},
+ service_start_name: 'NT Authority\\LocalService',
+ display_name: "DHCP Client"
+ ))
+ end
+
+ it "does not set the current resources enabled" do
+ provider.load_current_resource
+ expect(provider.current_resource.enabled).to be_nil
+ end
+ end
+
+ it "sets the current resources running to true if it's running" do
+ allow(provider).to receive(:current_state).and_return("running")
+ provider.load_current_resource
+ expect(provider.current_resource.running).to be true
+ end
+
+ it "sets the current resources running to false if it's in any other state" do
+ allow(provider).to receive(:current_state).and_return("other state")
+ provider.load_current_resource
+ expect(provider.current_resource.running).to be false
+ end
+
+ it "sets startup_type" do
+ expect(provider.current_resource.startup_type).to be_truthy
+ end
+
+ it "sets error_control" do
+ provider.load_current_resource
+ expect(provider.current_resource.error_control).to be_truthy
+ end
+
+ it "sets binary_path_name" do
+ provider.load_current_resource
+ expect(provider.current_resource.binary_path_name).to be_truthy
+ end
+
+ it "sets load_order_group" do
+ provider.load_current_resource
+ expect(provider.current_resource.load_order_group).to be_truthy
+ end
+
+ it "sets dependencies" do
+ provider.load_current_resource
+ expect(provider.current_resource.dependencies).to be_truthy
+ end
+
+ it "sets run_as_user" do
+ provider.load_current_resource
+ expect(provider.current_resource.run_as_user).to be_truthy
+ end
+
+ it "sets display_name" do
+ provider.load_current_resource
+ expect(provider.current_resource.display_name).to be_truthy
+ end
+
+ it "sets delayed start to true if delayed start is enabled" do
+ allow(chef_service_info).to receive(:delayed_start).and_return(1)
+ provider.load_current_resource
+ expect(provider.current_resource.delayed_start).to be true
+ end
+
+ it "sets delayed start to false if delayed start is disabled" do
+ allow(chef_service_info).to receive(:delayed_start).and_return(0)
+ provider.load_current_resource
+ expect(provider.current_resource.delayed_start).to be false
+ end
+ end
+
+ describe Chef::Provider::Service::Windows, "action_create" do
+ before do
+ provider.new_resource.binary_path_name = chef_service_binary_path_name
+ end
+
+ context "service exists" do
+ before do
+ allow(Win32::Service).to receive(:exists?).with(chef_service_name).and_return(true)
+ end
+
+ it "logs debug message" do
+ expect(logger).to receive(:trace).with("windows_service[#{chef_service_name}] already exists - nothing to do")
+ provider.action_create
+ end
+
+ it "does not converge" do
+ provider.action_create
+ expect(provider.resource_updated?).to be false
+ end
+
+ it "does not create service" do
+ expect(Win32::Service).to_not receive(:new)
+ provider.action_create
+ end
+
+ it "does not call converge_delayed_start" do
+ expect(provider).to_not receive(:converge_delayed_start)
+ provider.action_create
+ end
+ end
+
+ context "service does not exist" do
+ before do
+ allow(Win32::Service).to receive(:exists?).with(chef_service_name).and_return(false)
+ allow(Win32::Service).to receive(:new).with(anything).and_return(true)
+ end
+
+ it "converges resource" do
+ provider.action_create
+ expect(provider.resource_updated?).to be true
+ end
+
+ it "creates service" do
+ expect(Win32::Service).to receive(:new)
+ provider.action_create
+ end
+
+ it "creates service with correct configuration" do
+ expect(Win32::Service).to receive(:new).with(
+ service_name: chef_service_name,
+ service_type: 16,
+ start_type: 2,
+ error_control: 1,
+ binary_path_name: chef_service_binary_path_name,
+ service_start_name: "LocalSystem",
+ desired_access: 983551
+ )
+ provider.action_create
+ end
+
+ it "calls converge_delayed_start" do
+ expect(provider).to receive(:converge_delayed_start)
+ provider.action_create
+ end
+ end
+ end
+
+ describe Chef::Provider::Service::Windows, "action_delete" do
+ context "service exists" do
+ before do
+ allow(Win32::Service).to receive(:exists?).with(chef_service_name).and_return(true)
+ allow(Win32::Service).to receive(:delete).with(chef_service_name).and_return(true)
+ end
+
+ it "converges resource" do
+ provider.action_delete
+ expect(provider.resource_updated?).to be true
+ end
+
+ it "deletes service" do
+ expect(Win32::Service).to receive(:delete).with(chef_service_name)
+ provider.action_delete
+ end
+ end
+
+ context "service does not exist" do
+ before do
+ allow(Win32::Service).to receive(:exists?).with(chef_service_name).and_return(false)
+ end
+
+ it "logs debug message" do
+ expect(logger).to receive(:trace).with("windows_service[#{chef_service_name}] does not exist - nothing to do")
+ provider.action_delete
+ end
+
+ it "does not converge" do
+ provider.action_delete
+ expect(provider.resource_updated?).to be false
+ end
+
+ it "does not delete service" do
+ expect(Win32::Service).to_not receive(:delete)
+ provider.action_delete
+ end
+ end
+ end
+
+ describe Chef::Provider::Service::Windows, "action_configure" do
+ context "service exists" do
+ before do
+ allow(Win32::Service).to receive(:exists?).with(chef_service_name).and_return(true)
+ allow(Win32::Service).to receive(:configure).with(anything).and_return(true)
+ end
+
+ it "works around #6300 if run_as_user is default" do
+ new_resource.run_as_user = new_resource.class.properties[:run_as_user].default
+ expect(provider.new_resource).to receive(:run_as_user=)
+ .with(new_resource.class.properties[:run_as_user].default)
+ provider.action_configure
+ end
+
+ # Attributes that are Strings
+ %i{binary_path_name load_order_group dependencies run_as_user
+ display_name description}.each do |attr|
+ it "configures service if #{attr} has changed" do
+ provider.current_resource.send("#{attr}=", "old value")
+ provider.new_resource.send("#{attr}=", "new value")
+
+ expect(Win32::Service).to receive(:configure)
+ provider.action_configure
+ end
+ end
+
+ # Attributes that are Integers
+ %i{service_type error_control}.each do |attr|
+ it "configures service if #{attr} has changed" do
+ provider.current_resource.send("#{attr}=", 1)
+ provider.new_resource.send("#{attr}=", 2)
+
+ expect(Win32::Service).to receive(:configure)
+ provider.action_configure
+ end
+ end
+
+ it "configures service if startup_type has changed" do
+ provider.current_resource.startup_type = :automatic
+ provider.new_resource.startup_type = :manual
+
+ expect(Win32::Service).to receive(:configure)
+ provider.action_configure
+ end
+
+ it "calls converge_delayed_start" do
+ expect(provider).to receive(:converge_delayed_start)
+ provider.action_configure
+ end
+ end
+
+ context "service does not exist" do
+ before do
+ allow(Win32::Service).to receive(:exists?).with(chef_service_name).and_return(false)
+ end
+
+ it "logs warning" do
+ expect(logger).to receive(:warn)
+ .with("windows_service[#{chef_service_name}] does not exist. Maybe you need to prepend action :create")
+ provider.action_configure
+ end
+
+ it "does not converge" do
+ provider.action_configure
+ expect(provider.resource_updated?).to be false
+ end
+
+ it "does not convigure service" do
+ expect(Win32::Service).to_not receive(:configure)
+ provider.action_configure
+ end
+
+ it "does not call converge_delayed_start" do
+ expect(provider).to_not receive(:converge_delayed_start)
+ provider.action_configure
+ end
+ end
+ end
+
+ describe Chef::Provider::Service::Windows, "converge_delayed_start" do
+ before do
+ allow(Win32::Service).to receive(:configure).and_return(true)
+ end
+
+ it "works around #6300 if delayed_start is default" do
+ new_resource.delayed_start = new_resource.class.properties[:delayed_start].default
+ expect(provider.new_resource).to receive(:delayed_start=)
+ .with(new_resource.class.properties[:delayed_start].default)
+ provider.send(:converge_delayed_start)
+ end
+
+ context "delayed start needs to be updated" do
+ before do
+ provider.current_resource.delayed_start = false
+ provider.new_resource.delayed_start = true
+ end
+
+ it "configures delayed start" do
+ expect(Win32::Service).to receive(:configure)
+ provider.send(:converge_delayed_start)
+ end
+
+ it "configures delayed start with correct params" do
+ expect(Win32::Service).to receive(:configure).with(service_name: chef_service_name, delayed_start: 1)
+ provider.send(:converge_delayed_start)
+ end
+
+ it "converges resource" do
+ provider.send(:converge_delayed_start)
+ expect(provider.resource_updated?).to be true
+ end
+ end
+
+ context "delayed start does not need to be updated" do
+ before do
+ provider.current_resource.delayed_start = false
+ provider.new_resource.delayed_start = false
+ end
+
+ it "does not configure delayed start" do
+ expect(Win32::Service).to_not receive(:configure)
+ provider.send(:converge_delayed_start)
+ end
+
+ it "does not converge" do
+ provider.send(:converge_delayed_start)
+ expect(provider.resource_updated?).to be false
+ end
+ end
end
describe Chef::Provider::Service::Windows, "start_service" do
@@ -90,7 +507,7 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
end
it "calls the start command if one is specified" do
- new_resource.start_command "sc start chef"
+ new_resource.start_command "sc start #{chef_service_name}"
expect(provider).to receive(:shell_out!).with("#{new_resource.start_command}").and_return("Starting custom service")
provider.start_service
expect(new_resource.updated_by_last_action?).to be_truthy
@@ -148,18 +565,10 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
end
describe "running as a different account" do
- let(:old_run_as_user) { new_resource.run_as_user }
- let(:old_run_as_password) { new_resource.run_as_password }
-
- before {
+ before do
new_resource.run_as_user(".\\wallace")
new_resource.run_as_password("Wensleydale")
- }
-
- after {
- new_resource.run_as_user(old_run_as_user)
- new_resource.run_as_password(old_run_as_password)
- }
+ end
it "calls #grant_service_logon if the :run_as_user and :run_as_password attributes are present" do
expect(Win32::Service).to receive(:start)
@@ -173,6 +582,14 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
expect(Chef::ReservedNames::Win32::Security).not_to receive(:add_account_right).with("wallace", service_right)
provider.start_service
end
+
+ it "skips the rights check for LocalSystem" do
+ new_resource.run_as_user("LocalSystem")
+ expect(Win32::Service).to receive(:start)
+ expect(Chef::ReservedNames::Win32::Security).not_to receive(:get_account_right)
+ expect(Chef::ReservedNames::Win32::Security).not_to receive(:add_account_right)
+ provider.start_service
+ end
end
end
@@ -185,7 +602,7 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
end
it "calls the stop command if one is specified" do
- new_resource.stop_command "sc stop chef"
+ new_resource.stop_command "sc stop #{chef_service_name}"
expect(provider).to receive(:shell_out!).with("#{new_resource.stop_command}").and_return("Stopping custom service")
provider.stop_service
expect(new_resource.updated_by_last_action?).to be_truthy
@@ -369,17 +786,17 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
end
describe Chef::Provider::Service::Windows, "action_configure_startup" do
- { :automatic => "auto start", :manual => "demand start", :disabled => "disabled" }.each do |type, win32|
+ %i{automatic manual disabled}.each do |type|
it "sets the startup type to #{type} if it is something else" do
new_resource.startup_type(type)
- allow(provider).to receive(:current_start_type).and_return("fire")
+ allow(provider).to receive(:current_startup_type).and_return(:fire)
expect(provider).to receive(:set_startup_type).with(type)
provider.action_configure_startup
end
it "leaves the startup type as #{type} if it is already set" do
new_resource.startup_type(type)
- allow(provider).to receive(:current_start_type).and_return(win32)
+ allow(provider).to receive(:current_startup_type).and_return(type)
expect(provider).not_to receive(:set_startup_type).with(type)
provider.action_configure_startup
end
@@ -409,17 +826,17 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
shared_context "testing private methods" do
- let(:private_methods) {
+ let(:private_methods) do
described_class.private_instance_methods
- }
+ end
- before {
+ before do
described_class.send(:public, *private_methods)
- }
+ end
- after {
+ after do
described_class.send(:private, *private_methods)
- }
+ end
end
describe "grant_service_logon" do
diff --git a/spec/unit/provider/service_spec.rb b/spec/unit/provider/service_spec.rb
index d775297658..c788cb223e 100644
--- a/spec/unit/provider/service_spec.rb
+++ b/spec/unit/provider/service_spec.rb
@@ -123,7 +123,7 @@ describe Chef::Provider::Service do
it "should raise an exception if reload isn't supported" do
@new_resource.supports(:reload => false)
- allow(@new_resource).to receive(:reload_command).and_return(false)
+ @new_resource.reload_command(false)
expect { @provider.run_action(:reload) }.to raise_error(Chef::Exceptions::UnsupportedAction)
end
diff --git a/spec/unit/provider/subversion_spec.rb b/spec/unit/provider/subversion_spec.rb
index ac2a8dd754..55d6dc5d24 100644
--- a/spec/unit/provider/subversion_spec.rb
+++ b/spec/unit/provider/subversion_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,9 +32,18 @@ describe Chef::Provider::Subversion do
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
@provider = Chef::Provider::Subversion.new(@resource, @run_context)
+ @original_env = ENV.to_hash
+ # Generated command lines would include any environmental proxies
+ ENV.delete("http_proxy")
+ ENV.delete("https_proxy")
end
- it "converts resource attributes to options for run_command and popen4" do
+ after do
+ ENV.clear
+ ENV.update(@original_env)
+ end
+
+ it "converts resource attributes to options for shell_out" do
expect(@provider.run_options).to eq({})
@resource.user "deployninja"
expect(@provider.run_options).to eq({ :user => "deployninja" })
diff --git a/spec/unit/provider/systemd_unit_spec.rb b/spec/unit/provider/systemd_unit_spec.rb
index e71885f32d..15d5944992 100644
--- a/spec/unit/provider/systemd_unit_spec.rb
+++ b/spec/unit/provider/systemd_unit_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Nathan Williams (<nath.e.will@gmail.com>)
-# Copyright:: Copyright (c), Nathan Williams
+# Copyright:: Copyright 2016-2018, Nathan Williams
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,27 +19,20 @@
require "spec_helper"
describe Chef::Provider::SystemdUnit do
- let(:node) do
- Chef::Node.new.tap do |n|
- n.default["etc"] = {}
- n.default["etc"]["passwd"] = {
- "joe" => {
- "uid" => 1_000,
- },
- }
- end
- end
+ let(:node) { Chef::Node.new }
let(:events) { Chef::EventDispatch::Dispatcher.new }
let(:run_context) { Chef::RunContext.new(node, {}, events) }
- let(:unit_name) { "sysstat-collect.timer" }
+ let(:unit_name) { "sysstat-collect\\x2d.timer" }
+ let(:unit_name_escaped) { "sysstat-collect\\\\x2d.timer" }
let(:user_name) { "joe" }
let(:current_resource) { Chef::Resource::SystemdUnit.new(unit_name) }
let(:new_resource) { Chef::Resource::SystemdUnit.new(unit_name) }
let(:provider) { Chef::Provider::SystemdUnit.new(new_resource, run_context) }
- let(:unit_path_system) { "/etc/systemd/system/sysstat-collect.timer" }
- let(:unit_path_user) { "/etc/systemd/user/sysstat-collect.timer" }
- let(:unit_content_string) { "[Unit]\nDescription=Run system activity accounting tool every 10 minutes\n\n[Timer]\nOnCalendar=*:00/10\n\n[Install]\nWantedBy=sysstat.service" }
+ let(:unit_path_system) { "/etc/systemd/system/sysstat-collect\\x2d.timer" }
+ let(:unit_path_user) { "/etc/systemd/user/sysstat-collect\\x2d.timer" }
+ let(:unit_content_string) { "[Unit]\nDescription = Run system activity accounting tool every 10 minutes\n\n[Timer]\nOnCalendar = *:00/10\n\n[Install]\nWantedBy = sysstat.service\n" }
+ let(:malformed_content_string) { "derp" }
let(:unit_content_hash) do
{
@@ -57,8 +50,8 @@ describe Chef::Provider::SystemdUnit do
let(:user_cmd_opts) do
{
- "user" => "joe",
- "environment" => {
+ :user => "joe",
+ :environment => {
"DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/1000/bus",
},
}
@@ -81,11 +74,36 @@ describe Chef::Provider::SystemdUnit do
end
before(:each) do
+ allow(Etc).to receive(:getpwuid).and_return(OpenStruct.new(uid: 1000))
allow(Chef::Resource::SystemdUnit).to receive(:new)
.with(unit_name)
.and_return(current_resource)
end
+ describe "define_resource_requirements" do
+ before(:each) do
+ provider.action = :create
+ allow(provider).to receive(:active?).and_return(false)
+ allow(provider).to receive(:enabled?).and_return(false)
+ allow(provider).to receive(:masked?).and_return(false)
+ allow(provider).to receive(:static?).and_return(false)
+ end
+
+ it "accepts valid resource requirements" do
+ new_resource.content(unit_content_string)
+ provider.load_current_resource
+ provider.define_resource_requirements
+ expect { provider.process_resource_requirements }.to_not raise_error
+ end
+
+ it "rejects failed resource requirements" do
+ new_resource.content(malformed_content_string)
+ provider.load_current_resource
+ provider.define_resource_requirements
+ expect { provider.process_resource_requirements }.to raise_error(IniParse::ParseError)
+ end
+ end
+
describe "load_current_resource" do
before(:each) do
allow(provider).to receive(:active?).and_return(false)
@@ -232,7 +250,7 @@ describe Chef::Provider::SystemdUnit do
.and_return(systemctl_path)
end
- describe "creates/deletes the unit" do
+ describe "creates/deletes/presets/reverts the unit" do
it "creates the unit file when it does not exist" do
allow(provider).to receive(:manage_unit_file)
.with(:create)
@@ -327,6 +345,22 @@ describe Chef::Provider::SystemdUnit do
expect(provider).to_not receive(:manage_unit_file)
provider.action_delete
end
+
+ it "presets the unit" do
+ new_resource.user("joe")
+ expect(provider).to receive(:shell_out_with_systems_locale!)
+ .with("#{systemctl_path} --user preset #{unit_name_escaped}", user_cmd_opts)
+ .and_return(shell_out_success)
+ provider.action_preset
+ end
+
+ it "reverts the unit" do
+ new_resource.user("joe")
+ expect(provider).to receive(:shell_out_with_systems_locale!)
+ .with("#{systemctl_path} --user revert #{unit_name_escaped}", user_cmd_opts)
+ .and_return(shell_out_success)
+ provider.action_revert
+ end
end
context "when no user is specified" do
@@ -350,16 +384,38 @@ describe Chef::Provider::SystemdUnit do
expect(provider).to_not receive(:manage_unit_file)
provider.action_delete
end
+
+ it "presets the unit" do
+ expect(provider).to receive(:shell_out_with_systems_locale!)
+ .with("#{systemctl_path} --system preset #{unit_name_escaped}", {})
+ .and_return(shell_out_success)
+ provider.action_preset
+ end
+
+ it "reverts the unit" do
+ expect(provider).to receive(:shell_out_with_systems_locale!)
+ .with("#{systemctl_path} --system revert #{unit_name_escaped}", {})
+ .and_return(shell_out_success)
+ provider.action_revert
+ end
end
end
- describe "enables/disables the unit" do
+ describe "enables/disables/reenables the unit" do
context "when a user is specified" do
+ it "reenables the unit" do
+ current_resource.user(user_name)
+ expect(provider).to receive(:shell_out_with_systems_locale!)
+ .with("#{systemctl_path} --user reenable #{unit_name_escaped}", user_cmd_opts)
+ .and_return(shell_out_success)
+ provider.action_reenable
+ end
+
it "enables the unit when it is disabled" do
current_resource.user(user_name)
current_resource.enabled(false)
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --user enable #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user enable #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_success)
provider.action_enable
end
@@ -382,7 +438,7 @@ describe Chef::Provider::SystemdUnit do
current_resource.user(user_name)
current_resource.enabled(true)
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --user disable #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user disable #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_success)
provider.action_disable
end
@@ -403,10 +459,17 @@ describe Chef::Provider::SystemdUnit do
end
context "when no user is specified" do
+ it "reenables the unit" do
+ expect(provider).to receive(:shell_out_with_systems_locale!)
+ .with("#{systemctl_path} --system reenable #{unit_name_escaped}", {})
+ .and_return(shell_out_success)
+ provider.action_reenable
+ end
+
it "enables the unit when it is disabled" do
current_resource.enabled(false)
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --system enable #{unit_name}", {})
+ .with("#{systemctl_path} --system enable #{unit_name_escaped}", {})
.and_return(shell_out_success)
provider.action_enable
end
@@ -426,7 +489,7 @@ describe Chef::Provider::SystemdUnit do
it "disables the unit when it is enabled" do
current_resource.enabled(true)
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --system disable #{unit_name}", {})
+ .with("#{systemctl_path} --system disable #{unit_name_escaped}", {})
.and_return(shell_out_success)
provider.action_disable
end
@@ -452,7 +515,7 @@ describe Chef::Provider::SystemdUnit do
current_resource.user(user_name)
current_resource.masked(false)
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --user mask #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user mask #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_success)
provider.action_mask
end
@@ -468,7 +531,7 @@ describe Chef::Provider::SystemdUnit do
current_resource.user(user_name)
current_resource.masked(true)
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --user unmask #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user unmask #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_success)
provider.action_unmask
end
@@ -485,7 +548,7 @@ describe Chef::Provider::SystemdUnit do
it "masks the unit when it is unmasked" do
current_resource.masked(false)
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --system mask #{unit_name}", {})
+ .with("#{systemctl_path} --system mask #{unit_name_escaped}", {})
.and_return(shell_out_success)
provider.action_mask
end
@@ -499,7 +562,7 @@ describe Chef::Provider::SystemdUnit do
it "unmasks the unit when it is masked" do
current_resource.masked(true)
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --system unmask #{unit_name}", {})
+ .with("#{systemctl_path} --system unmask #{unit_name_escaped}", {})
.and_return(shell_out_success)
provider.action_unmask
end
@@ -518,7 +581,7 @@ describe Chef::Provider::SystemdUnit do
current_resource.user(user_name)
current_resource.active(false)
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --user start #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user start #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_success)
provider.action_start
end
@@ -534,7 +597,7 @@ describe Chef::Provider::SystemdUnit do
current_resource.user(user_name)
current_resource.active(true)
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --user stop #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user stop #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_success)
provider.action_stop
end
@@ -551,7 +614,7 @@ describe Chef::Provider::SystemdUnit do
it "starts the unit when it is inactive" do
current_resource.active(false)
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --system start #{unit_name}", {})
+ .with("#{systemctl_path} --system start #{unit_name_escaped}", {})
.and_return(shell_out_success)
provider.action_start
end
@@ -565,7 +628,7 @@ describe Chef::Provider::SystemdUnit do
it "stops the unit when it is active" do
current_resource.active(true)
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --system stop #{unit_name}", {})
+ .with("#{systemctl_path} --system stop #{unit_name_escaped}", {})
.and_return(shell_out_success)
provider.action_stop
end
@@ -583,7 +646,7 @@ describe Chef::Provider::SystemdUnit do
it "restarts the unit" do
current_resource.user(user_name)
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --user restart #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user restart #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_success)
provider.action_restart
end
@@ -592,7 +655,7 @@ describe Chef::Provider::SystemdUnit do
current_resource.user(user_name)
current_resource.active(true)
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --user reload #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user reload #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_success)
provider.action_reload
end
@@ -608,7 +671,7 @@ describe Chef::Provider::SystemdUnit do
context "when no user is specified" do
it "restarts the unit" do
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --system restart #{unit_name}", {})
+ .with("#{systemctl_path} --system restart #{unit_name_escaped}", {})
.and_return(shell_out_success)
provider.action_restart
end
@@ -616,7 +679,7 @@ describe Chef::Provider::SystemdUnit do
it "reloads the unit if active" do
current_resource.active(true)
expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} --system reload #{unit_name}", {})
+ .with("#{systemctl_path} --system reload #{unit_name_escaped}", {})
.and_return(shell_out_success)
provider.action_reload
end
@@ -629,6 +692,69 @@ describe Chef::Provider::SystemdUnit do
end
end
+ describe "try-restarts the unit" do
+ context "when a user is specified" do
+ it "try-restarts the unit" do
+ current_resource.user(user_name)
+ expect(provider).to receive(:shell_out_with_systems_locale!)
+ .with("#{systemctl_path} --user try-restart #{unit_name_escaped}", user_cmd_opts)
+ .and_return(shell_out_success)
+ provider.action_try_restart
+ end
+ end
+
+ context "when no user is specified" do
+ it "try-restarts the unit" do
+ expect(provider).to receive(:shell_out_with_systems_locale!)
+ .with("#{systemctl_path} --system try-restart #{unit_name_escaped}", {})
+ .and_return(shell_out_success)
+ provider.action_try_restart
+ end
+ end
+ end
+
+ describe "reload-or-restarts the unit" do
+ context "when a user is specified" do
+ it "reload-or-restarts the unit" do
+ current_resource.user(user_name)
+ expect(provider).to receive(:shell_out_with_systems_locale!)
+ .with("#{systemctl_path} --user reload-or-restart #{unit_name_escaped}", user_cmd_opts)
+ .and_return(shell_out_success)
+ provider.action_reload_or_restart
+ end
+ end
+
+ context "when no user is specified" do
+ it "reload-or-restarts the unit" do
+ expect(provider).to receive(:shell_out_with_systems_locale!)
+ .with("#{systemctl_path} --system reload-or-restart #{unit_name_escaped}", {})
+ .and_return(shell_out_success)
+ provider.action_reload_or_restart
+ end
+ end
+ end
+
+ describe "reload-or-try-restarts the unit" do
+ context "when a user is specified" do
+ it "reload-or-try-restarts the unit" do
+ current_resource.user(user_name)
+ expect(provider).to receive(:shell_out_with_systems_locale!)
+ .with("#{systemctl_path} --user reload-or-try-restart #{unit_name_escaped}", user_cmd_opts)
+ .and_return(shell_out_success)
+ provider.action_reload_or_try_restart
+ end
+ end
+
+ context "when no user is specified" do
+ it "reload-or-try-restarts the unit" do
+ expect(provider).to receive(:shell_out_with_systems_locale!)
+ .with("#{systemctl_path} --system reload-or-try-restart #{unit_name_escaped}", {})
+ .and_return(shell_out_success)
+ provider.action_reload_or_try_restart
+ end
+ end
+ end
+
describe "#active?" do
before(:each) do
provider.current_resource = current_resource
@@ -639,7 +765,7 @@ describe Chef::Provider::SystemdUnit do
it "returns true when unit is active" do
current_resource.user(user_name)
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --user is-active #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user is-active #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_success)
expect(provider.active?).to be true
end
@@ -647,7 +773,7 @@ describe Chef::Provider::SystemdUnit do
it "returns false when unit is inactive" do
current_resource.user(user_name)
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --user is-active #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user is-active #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_failure)
expect(provider.active?).to be false
end
@@ -656,14 +782,14 @@ describe Chef::Provider::SystemdUnit do
context "when no user is specified" do
it "returns true when unit is active" do
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --system is-active #{unit_name}", {})
+ .with("#{systemctl_path} --system is-active #{unit_name_escaped}", {})
.and_return(shell_out_success)
expect(provider.active?).to be true
end
it "returns false when unit is not active" do
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --system is-active #{unit_name}", {})
+ .with("#{systemctl_path} --system is-active #{unit_name_escaped}", {})
.and_return(shell_out_failure)
expect(provider.active?).to be false
end
@@ -680,7 +806,7 @@ describe Chef::Provider::SystemdUnit do
it "returns true when unit is enabled" do
current_resource.user(user_name)
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --user is-enabled #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user is-enabled #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_success)
expect(provider.enabled?).to be true
end
@@ -688,7 +814,7 @@ describe Chef::Provider::SystemdUnit do
it "returns false when unit is not enabled" do
current_resource.user(user_name)
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --user is-enabled #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user is-enabled #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_failure)
expect(provider.enabled?).to be false
end
@@ -697,14 +823,14 @@ describe Chef::Provider::SystemdUnit do
context "when no user is specified" do
it "returns true when unit is enabled" do
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --system is-enabled #{unit_name}", {})
+ .with("#{systemctl_path} --system is-enabled #{unit_name_escaped}", {})
.and_return(shell_out_success)
expect(provider.enabled?).to be true
end
it "returns false when unit is not enabled" do
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --system is-enabled #{unit_name}", {})
+ .with("#{systemctl_path} --system is-enabled #{unit_name_escaped}", {})
.and_return(shell_out_failure)
expect(provider.enabled?).to be false
end
@@ -721,7 +847,7 @@ describe Chef::Provider::SystemdUnit do
it "returns true when the unit is masked" do
current_resource.user(user_name)
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --user status #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user status #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_masked)
expect(provider.masked?).to be true
end
@@ -729,7 +855,7 @@ describe Chef::Provider::SystemdUnit do
it "returns false when the unit is not masked" do
current_resource.user(user_name)
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --user status #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user status #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_static)
expect(provider.masked?).to be false
end
@@ -738,14 +864,14 @@ describe Chef::Provider::SystemdUnit do
context "when no user is specified" do
it "returns true when the unit is masked" do
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --system status #{unit_name}", {})
+ .with("#{systemctl_path} --system status #{unit_name_escaped}", {})
.and_return(shell_out_masked)
expect(provider.masked?).to be true
end
it "returns false when the unit is not masked" do
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --system status #{unit_name}", {})
+ .with("#{systemctl_path} --system status #{unit_name_escaped}", {})
.and_return(shell_out_static)
expect(provider.masked?).to be false
end
@@ -762,7 +888,7 @@ describe Chef::Provider::SystemdUnit do
it "returns true when the unit is static" do
current_resource.user(user_name)
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --user is-enabled #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user is-enabled #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_static)
expect(provider.static?).to be true
end
@@ -770,7 +896,7 @@ describe Chef::Provider::SystemdUnit do
it "returns false when the unit is not static" do
current_resource.user(user_name)
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --user is-enabled #{unit_name}", user_cmd_opts)
+ .with("#{systemctl_path} --user is-enabled #{unit_name_escaped}", user_cmd_opts)
.and_return(shell_out_masked)
expect(provider.static?).to be false
end
@@ -779,14 +905,14 @@ describe Chef::Provider::SystemdUnit do
context "when no user is specified" do
it "returns true when the unit is static" do
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --system is-enabled #{unit_name}", {})
+ .with("#{systemctl_path} --system is-enabled #{unit_name_escaped}", {})
.and_return(shell_out_static)
expect(provider.static?).to be true
end
it "returns false when the unit is not static" do
expect(provider).to receive(:shell_out)
- .with("#{systemctl_path} --system is-enabled #{unit_name}", {})
+ .with("#{systemctl_path} --system is-enabled #{unit_name_escaped}", {})
.and_return(shell_out_masked)
expect(provider.static?).to be false
end
diff --git a/spec/unit/provider/template/content_spec.rb b/spec/unit/provider/template/content_spec.rb
index 0f936c8f11..8f30d8f868 100644
--- a/spec/unit/provider/template/content_spec.rb
+++ b/spec/unit/provider/template/content_spec.rb
@@ -20,13 +20,13 @@ require "spec_helper"
describe Chef::Provider::Template::Content do
- let(:enclosing_directory) {
+ let(:enclosing_directory) do
canonicalize_path(Dir.mktmpdir)
- }
+ end
- let(:resource_path) {
+ let(:resource_path) do
canonicalize_path(File.expand_path(File.join(enclosing_directory, "openldap_stuff.conf")))
- }
+ end
let(:new_resource) do
double("Chef::Resource::Template (new)",
@@ -46,10 +46,10 @@ describe Chef::Provider::Template::Content do
:helper_modules => [])
end
- let(:rendered_file_locations) {
+ let(:rendered_file_locations) do
[Dir.tmpdir + "/openldap_stuff.conf",
enclosing_directory + "/openldap_stuff.conf"]
- }
+ end
let(:run_context) do
cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks"))
@@ -101,9 +101,9 @@ describe Chef::Provider::Template::Content do
end
context "when creating a tempfile in destdir fails" do
- let(:enclosing_directory) {
+ let(:enclosing_directory) do
canonicalize_path("/nonexisting/path")
- }
+ end
it "returns a tempfile in the tempdir when :file_deployment_uses_destdir is set to :auto" do
Chef::Config[:file_staging_uses_destdir] = :auto
diff --git a/spec/unit/provider/template_spec.rb b/spec/unit/provider/template_spec.rb
index 488039ad18..9f69772bc8 100644
--- a/spec/unit/provider/template_spec.rb
+++ b/spec/unit/provider/template_spec.rb
@@ -26,13 +26,14 @@ require "support/shared/unit/provider/file"
describe Chef::Provider::Template do
let(:node) { double("Chef::Node") }
let(:events) { double("Chef::Events").as_null_object } # mock all the methods
- let(:run_context) { double("Chef::RunContext", :node => node, :events => events) }
- let(:enclosing_directory) {
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+ let(:run_context) { double("Chef::RunContext", :node => node, :events => events, :logger => logger) }
+ let(:enclosing_directory) do
canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
- }
- let(:resource_path) {
+ end
+ let(:resource_path) do
canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
- }
+ end
# Subject
@@ -58,15 +59,12 @@ describe Chef::Provider::Template do
context "when creating the template" do
- let(:node) { double("Chef::Node") }
- let(:events) { double("Chef::Events").as_null_object } # mock all the methods
- let(:run_context) { double("Chef::RunContext", :node => node, :events => events) }
- let(:enclosing_directory) {
+ let(:enclosing_directory) do
canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
- }
- let(:resource_path) {
+ end
+ let(:resource_path) do
canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
- }
+ end
# Subject
diff --git a/spec/unit/provider/user/aix_spec.rb b/spec/unit/provider/user/aix_spec.rb
new file mode 100644
index 0000000000..c70fdd9a57
--- /dev/null
+++ b/spec/unit/provider/user/aix_spec.rb
@@ -0,0 +1,97 @@
+# Copyright:: Copyright 2017, Chef Software Inc.
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "mixlib/shellout"
+require "spec_helper"
+
+describe Chef::Provider::User::Aix do
+
+ let(:shellcmdresult) do
+ Struct.new(:stdout, :stderr, :exitstatus)
+ end
+
+ let(:node) do
+ Chef::Node.new.tap do |node|
+ node.automatic["platform"] = "solaris2"
+ end
+ end
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:new_resource) do
+ Chef::Resource::User::AixUser.new("adam", @run_context)
+ end
+ let(:current_resource) do
+ Chef::Resource::User::AixUser.new("adam", @run_context).tap do |cr|
+ cr.home "/home/adam"
+ end
+ end
+
+ subject(:provider) do
+ described_class.new(new_resource, run_context).tap do |p|
+ p.current_resource = current_resource
+ end
+ end
+
+ describe "when we set a password" do
+ before do
+ new_resource.password "Ostagazuzulum"
+ end
+
+ it "should call chpasswd correctly" do
+ expect(provider).to receive(:shell_out!).with("echo 'adam:Ostagazuzulum' | chpasswd -e").and_return true
+ provider.manage_user
+ end
+ end
+
+ describe "#create_user" do
+ context "with a system user" do
+ before { new_resource.system(true) }
+ it "should add the user to the system group" do
+ expect(provider).to receive(:shell_out!).with("useradd", "-g", "system", "adam")
+ provider.create_user
+ end
+ end
+
+ context "with manage_home" do
+ before do
+ new_resource.manage_home(true)
+ new_resource.home("/home/adam")
+ allow(provider).to receive(:updating_home?).and_return(true)
+ end
+
+ it "should create the home directory" do
+ allow(provider).to receive(:shell_out!).with("usermod", "-d", "/home/adam", "adam")
+ expect(FileUtils).to receive(:mkdir_p).and_return(true)
+ provider.manage_user
+ end
+
+ it "should move an existing home dir" do
+ allow(provider).to receive(:shell_out!).with("usermod", "-d", "/mnt/home/adam", "adam")
+ new_resource.home("/mnt/home/adam")
+ allow(File).to receive(:directory?).with("/home/adam").and_return(true)
+ expect(FileUtils).to receive(:mv).with("/home/adam", "/mnt/home/adam")
+ provider.manage_user
+ end
+
+ it "should not pass -m" do
+ allow(FileUtils).to receive(:mkdir_p).and_return(true)
+ expect(provider).to receive(:shell_out!).with("usermod", "-d", "/home/adam", "adam")
+ provider.manage_user
+ end
+ end
+ end
+end
diff --git a/spec/unit/provider/user/dscl_spec.rb b/spec/unit/provider/user/dscl_spec.rb
index bf8b3169d7..956c32664d 100644
--- a/spec/unit/provider/user/dscl_spec.rb
+++ b/spec/unit/provider/user/dscl_spec.rb
@@ -24,52 +24,52 @@ describe Chef::Provider::User::Dscl do
before do
allow(ChefConfig).to receive(:windows?) { false }
end
- let(:shellcmdresult) {
+ let(:shellcmdresult) do
Struct.new(:stdout, :stderr, :exitstatus)
- }
- let(:node) {
+ end
+ let(:node) do
node = Chef::Node.new
allow(node).to receive(:[]).with(:platform_version).and_return(mac_version)
allow(node).to receive(:[]).with(:platform).and_return("mac_os_x")
node
- }
+ end
- let(:events) {
+ let(:events) do
Chef::EventDispatch::Dispatcher.new
- }
+ end
- let(:run_context) {
+ let(:run_context) do
Chef::RunContext.new(node, {}, events)
- }
+ end
- let(:new_resource) {
- r = Chef::Resource::User.new("toor")
+ let(:new_resource) do
+ r = Chef::Resource::User::DsclUser.new("toor")
r.password(password)
r.salt(salt)
r.iterations(iterations)
r
- }
+ end
- let(:provider) {
+ let(:provider) do
Chef::Provider::User::Dscl.new(new_resource, run_context)
- }
+ end
- let(:mac_version) {
+ let(:mac_version) do
"10.9.1"
- }
+ end
let(:password) { nil }
let(:salt) { nil }
let(:iterations) { nil }
- let(:salted_sha512_password) {
+ let(:salted_sha512_password) do
"0f543f021c63255e64e121a3585601b8ecfedf6d2\
705ddac69e682a33db5dbcdb9b56a2520bc8fff63a\
2ba6b7984c0737ff0b7949455071581f7affcd536d\
402b6cdb097"
- }
+ end
- let(:salted_sha512_pbkdf2_password) {
+ let(:salted_sha512_pbkdf2_password) do
"c734b6e4787c3727bb35e29fdd92b97c\
1de12df509577a045728255ec7c6c5f5\
c18efa05ed02b682ffa7ebc05119900e\
@@ -78,24 +78,24 @@ b1d4880833aa7a190afc13e2bf0936b8\
9464a8c234f3919082400b4f939bb77b\
c5adbbac718b7eb99463a7b679571e0f\
1c9fef2ef08d0b9e9c2bcf644eed2ffc"
- }
+ end
- let(:salted_sha512_pbkdf2_salt) {
+ let(:salted_sha512_pbkdf2_salt) do
"2d942d8364a9ccf2b8e5cb7ed1ff58f78\
e29dbfee7f9db58859144d061fd0058"
- }
+ end
- let(:salted_sha512_pbkdf2_iterations) {
+ let(:salted_sha512_pbkdf2_iterations) do
25000
- }
+ end
- let(:vagrant_sha_512) {
+ let(:vagrant_sha_512) do
"6f75d7190441facc34291ebbea1fc756b242d4f\
e9bcff141bccb84f1979e27e539539aa31f9f7dcc92c0cea959\
ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
- }
+ end
- let(:vagrant_sha_512_pbkdf2) {
+ let(:vagrant_sha_512_pbkdf2) do
"12601a90db17cbf\
8ba4808e6382fb0d3b9d8a6c1a190477bf680ab21afb\
6065467136e55cc208a6f74156e3daf20fb13369ef4b\
@@ -103,51 +103,51 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
47cca84341a7f93a27147343f89fb843fb46c0017d2\
64afa4976baacf941b915bd1ec1ca24c30b3e759e02\
403e02f59fe7ff5938a7636c"
- }
+ end
- let(:vagrant_sha_512_pbkdf2_salt) {
+ let(:vagrant_sha_512_pbkdf2_salt) do
"ee954be472fdc60ddf89484781433993625f006af6ec810c08f49a7e413946a1"
- }
+ end
- let(:vagrant_sha_512_pbkdf2_iterations) {
+ let(:vagrant_sha_512_pbkdf2_iterations) do
34482
- }
+ end
describe "when shelling out to dscl" do
it "should run dscl with the supplied cmd /Path args" do
shell_return = shellcmdresult.new("stdout", "err", 0)
- expect(provider).to receive(:shell_out).with("dscl . -cmd /Path args").and_return(shell_return)
- expect(provider.run_dscl("cmd /Path args")).to eq("stdout")
+ expect(provider).to receive(:shell_out).with("dscl", ".", "-cmd", "/Path", "args").and_return(shell_return)
+ expect(provider.run_dscl("cmd", "/Path", "args")).to eq("stdout")
end
it "returns an empty string from delete commands" do
shell_return = shellcmdresult.new("out", "err", 23)
- expect(provider).to receive(:shell_out).with("dscl . -delete /Path args").and_return(shell_return)
- expect(provider.run_dscl("delete /Path args")).to eq("")
+ expect(provider).to receive(:shell_out).with("dscl", ".", "-delete", "/Path", "args").and_return(shell_return)
+ expect(provider.run_dscl("delete", "/Path", "args")).to eq("")
end
it "should raise an exception for any other command" do
shell_return = shellcmdresult.new("out", "err", 23)
- expect(provider).to receive(:shell_out).with("dscl . -cmd /Path arguments").and_return(shell_return)
- expect { provider.run_dscl("cmd /Path arguments") }.to raise_error(Chef::Exceptions::DsclCommandFailed)
+ expect(provider).to receive(:shell_out).with("dscl", ".", "-cmd", "/Path", "arguments").and_return(shell_return)
+ expect { provider.run_dscl("cmd", "/Path", "arguments") }.to raise_error(Chef::Exceptions::DsclCommandFailed)
end
it "raises an exception when dscl reports 'no such key'" do
shell_return = shellcmdresult.new("No such key: ", "err", 23)
- expect(provider).to receive(:shell_out).with("dscl . -cmd /Path args").and_return(shell_return)
- expect { provider.run_dscl("cmd /Path args") }.to raise_error(Chef::Exceptions::DsclCommandFailed)
+ expect(provider).to receive(:shell_out).with("dscl", ".", "-cmd", "/Path", "args").and_return(shell_return)
+ expect { provider.run_dscl("cmd", "/Path", "args") }.to raise_error(Chef::Exceptions::DsclCommandFailed)
end
it "raises an exception when dscl reports 'eDSRecordNotFound'" do
shell_return = shellcmdresult.new("<dscl_cmd> DS Error: -14136 (eDSRecordNotFound)", "err", -14136)
- expect(provider).to receive(:shell_out).with("dscl . -cmd /Path args").and_return(shell_return)
- expect { provider.run_dscl("cmd /Path args") }.to raise_error(Chef::Exceptions::DsclCommandFailed)
+ expect(provider).to receive(:shell_out).with("dscl", ".", "-cmd", "/Path", "args").and_return(shell_return)
+ expect { provider.run_dscl("cmd", "/Path", "args") }.to raise_error(Chef::Exceptions::DsclCommandFailed)
end
end
describe "get_free_uid" do
before do
- expect(provider).to receive(:run_dscl).with("list /Users uid").and_return("\nwheel 200\nstaff 201\nbrahms 500\nchopin 501\n")
+ expect(provider).to receive(:run_dscl).with("list", "/Users", "uid").and_return("\nwheel 200\nstaff 201\nbrahms 500\nchopin 501\n")
end
describe "when resource is configured as system" do
@@ -177,7 +177,7 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
describe "when called with a user id" do
before do
- expect(provider).to receive(:run_dscl).with("list /Users uid").and_return("\naj 500\n")
+ expect(provider).to receive(:run_dscl).with("list", "/Users", "uid").and_return("\naj 500\n")
end
it "should return true for a used uid number" do
@@ -198,8 +198,8 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
end
it "finds a valid, unused uid when none is specified" do
- expect(provider).to receive(:run_dscl).with("list /Users uid").and_return("")
- expect(provider).to receive(:run_dscl).with("create /Users/toor UniqueID 501")
+ expect(provider).to receive(:run_dscl).with("list", "/Users", "uid").and_return("")
+ expect(provider).to receive(:run_dscl).with("create", "/Users/toor", "UniqueID", 501)
expect(provider).to receive(:get_free_uid).and_return(501)
provider.dscl_set_uid
expect(new_resource.uid).to eq(501)
@@ -207,19 +207,45 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
it "sets the uid specified in the resource" do
new_resource.uid(1000)
- expect(provider).to receive(:run_dscl).with("create /Users/toor UniqueID 1000").and_return(true)
- expect(provider).to receive(:run_dscl).with("list /Users uid").and_return("")
+ expect(provider).to receive(:run_dscl).with("create", "/Users/toor", "UniqueID", 1000).and_return(true)
+ expect(provider).to receive(:run_dscl).with("list", "/Users", "uid").and_return("")
provider.dscl_set_uid
end
end
+ describe "current_home_exists?" do
+ let(:current_resource) do
+ new_resource.dup
+ end
+
+ before do
+ provider.current_resource = current_resource
+ end
+
+ it "returns false for nil home dir" do
+ current_resource.home nil
+ expect(provider.current_home_exists?).to be_falsey
+ end
+
+ it "is false for empty string" do
+ current_resource.home ""
+ expect(provider.current_home_exists?).to be_falsey
+ end
+
+ it "is true for existing directory" do
+ current_resource.home "/Users/blah"
+ allow(::File).to receive(:exist?).with("/Users/blah").and_return(true)
+ expect(provider.current_home_exists?).to be_truthy
+ end
+ end
+
describe "when modifying the home directory" do
- let(:current_resource) {
+ let(:current_resource) do
new_resource.dup
- }
+ end
before do
- new_resource.supports({ :manage_home => true })
+ new_resource.manage_home true
new_resource.home("/Users/toor")
provider.current_resource = current_resource
@@ -227,7 +253,7 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
it "deletes the home directory when resource#home is nil" do
new_resource.instance_variable_set(:@home, nil)
- expect(provider).to receive(:run_dscl).with("delete /Users/toor NFSHomeDirectory").and_return(true)
+ expect(provider).to receive(:run_dscl).with("delete", "/Users/toor", "NFSHomeDirectory").and_return(true)
provider.dscl_set_home
end
@@ -237,39 +263,40 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
end
it "moves the users home to the new location if it exists and the target location is different" do
- new_resource.supports(:manage_home => true)
+ new_resource.manage_home true
current_home = CHEF_SPEC_DATA + "/old_home_dir"
current_home_files = [current_home + "/my-dot-emacs", current_home + "/my-dot-vim"]
current_resource.home(current_home)
new_resource.gid(23)
- allow(::File).to receive(:exists?).with("/old/home/toor").and_return(true)
- allow(::File).to receive(:exists?).with("/Users/toor").and_return(true)
+ allow(::File).to receive(:exist?).with("/old/home/toor").and_return(true)
+ allow(::File).to receive(:exist?).with("/Users/toor").and_return(true)
+ allow(::File).to receive(:exist?).with(current_home).and_return(true)
expect(FileUtils).to receive(:mkdir_p).with("/Users/toor").and_return(true)
expect(FileUtils).to receive(:rmdir).with(current_home)
expect(::Dir).to receive(:glob).with("#{CHEF_SPEC_DATA}/old_home_dir/*", ::File::FNM_DOTMATCH).and_return(current_home_files)
- expect(FileUtils).to receive(:mv).with(current_home_files, "/Users/toor", :force => true)
+ expect(FileUtils).to receive(:mv).with(current_home_files, "/Users/toor", force: true)
expect(FileUtils).to receive(:chown_R).with("toor", "23", "/Users/toor")
- expect(provider).to receive(:run_dscl).with("create /Users/toor NFSHomeDirectory '/Users/toor'")
+ expect(provider).to receive(:run_dscl).with("create", "/Users/toor", "NFSHomeDirectory", "/Users/toor")
provider.dscl_set_home
end
it "should raise an exception when the systems user template dir (skel) cannot be found" do
- allow(::File).to receive(:exists?).and_return(false, false, false)
+ allow(::File).to receive(:exist?).and_return(false, false, false)
expect { provider.dscl_set_home }.to raise_error(Chef::Exceptions::User)
end
it "should run ditto to copy any missing files from skel to the new home dir" do
- expect(::File).to receive(:exists?).with("/System/Library/User\ Template/English.lproj").and_return(true)
+ expect(::File).to receive(:exist?).with("/System/Library/User\ Template/English.lproj").and_return(true)
expect(FileUtils).to receive(:chown_R).with("toor", "", "/Users/toor")
- expect(provider).to receive(:shell_out!).with("ditto '/System/Library/User Template/English.lproj' '/Users/toor'")
+ expect(provider).to receive(:shell_out!).with("ditto", "/System/Library/User Template/English.lproj", "/Users/toor")
provider.ditto_home
end
it "creates the user's NFSHomeDirectory and home directory" do
- expect(provider).to receive(:run_dscl).with("create /Users/toor NFSHomeDirectory '/Users/toor'").and_return(true)
+ expect(provider).to receive(:run_dscl).with("create", "/Users/toor", "NFSHomeDirectory", "/Users/toor").and_return(true)
expect(provider).to receive(:ditto_home)
provider.dscl_set_home
end
@@ -280,8 +307,8 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
let(:plutil_exists) { true }
before do
- allow(::File).to receive(:exists?).with("/usr/bin/dscl").and_return(dscl_exists)
- allow(::File).to receive(:exists?).with("/usr/bin/plutil").and_return(plutil_exists)
+ allow(::File).to receive(:exist?).with("/usr/bin/dscl").and_return(dscl_exists)
+ allow(::File).to receive(:exist?).with("/usr/bin/plutil").and_return(plutil_exists)
end
def run_requirements
@@ -307,9 +334,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
end
describe "when on Mac 10.6" do
- let(:mac_version) {
+ let(:mac_version) do
"10.6.5"
- }
+ end
it "should raise an error" do
expect { run_requirements }.to raise_error(Chef::Exceptions::User)
@@ -317,9 +344,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
end
describe "when on Mac 10.7" do
- let(:mac_version) {
+ let(:mac_version) do
"10.7.5"
- }
+ end
describe "when password is SALTED-SHA512" do
let(:password) { salted_sha512_password }
@@ -340,9 +367,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
[ "10.9", "10.10"].each do |version|
describe "when on Mac #{version}" do
- let(:mac_version) {
+ let(:mac_version) do
"#{version}.2"
- }
+ end
describe "when password is SALTED-SHA512" do
let(:password) { salted_sha512_password }
@@ -379,8 +406,8 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
let(:user_plist_file) { nil }
before do
- expect(provider).to receive(:shell_out).with("dscacheutil '-flushcache'")
- expect(provider).to receive(:shell_out).with("plutil -convert xml1 -o - /var/db/dslocal/nodes/Default/users/toor.plist") do
+ expect(provider).to receive(:shell_out).with("dscacheutil", "-flushcache")
+ expect(provider).to receive(:shell_out).with("plutil", "-convert", "xml1", "-o", "-", "/var/db/dslocal/nodes/Default/users/toor.plist") do
if user_plist_file.nil?
shellcmdresult.new("Can not find the file", "Sorry!!", 1)
else
@@ -388,7 +415,7 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
end
end
- if !user_plist_file.nil?
+ unless user_plist_file.nil?
expect(provider).to receive(:convert_binary_plist_to_xml).and_return(File.read(File.join(CHEF_SPEC_DATA, "mac_users/#{user_plist_file}.shadow.xml")))
end
end
@@ -413,9 +440,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
let(:password) { "something" } # Load password during load_current_resource
describe "on 10.7" do
- let(:mac_version) {
+ let(:mac_version) do
"10.7.5"
- }
+ end
let(:user_plist_file) { "10.7" }
@@ -478,9 +505,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
end
describe "on 10.8" do
- let(:mac_version) {
+ let(:mac_version) do
"10.8.3"
- }
+ end
let(:user_plist_file) { "10.8" }
@@ -504,9 +531,9 @@ e68d1f9821b26689312366")
describe "on 10.7 upgraded to 10.8" do
# In this scenario user password is still in 10.7 format
- let(:mac_version) {
+ let(:mac_version) do
"10.8.3"
- }
+ end
let(:user_plist_file) { "10.7-8" }
@@ -542,9 +569,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
end
describe "on 10.9" do
- let(:mac_version) {
+ let(:mac_version) do
"10.9.1"
- }
+ end
let(:user_plist_file) { "10.9" }
@@ -646,9 +673,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
describe "prepare_password_shadow_info" do
describe "when on Mac 10.7" do
- let(:mac_version) {
+ let(:mac_version) do
"10.7.1"
- }
+ end
describe "when the password is plain text" do
let(:password) { "vagrant" }
@@ -676,9 +703,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
["10.8", "10.9", "10.10"].each do |version|
describe "when on Mac #{version}" do
- let(:mac_version) {
+ let(:mac_version) do
"#{version}.1"
- }
+ end
describe "when the password is plain text" do
let(:password) { "vagrant" }
@@ -723,7 +750,7 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
expect(provider).to receive(:prepare_password_shadow_info).and_return({})
mock_shellout = double("Mock::Shellout")
allow(mock_shellout).to receive(:run_command)
- expect(Mixlib::ShellOut).to receive(:new).and_return(mock_shellout)
+ expect(provider).to receive(:shell_out).and_return(mock_shellout)
expect(provider).to receive(:read_user_info)
expect(provider).to receive(:dscl_set)
expect(provider).to receive(:sleep).with(3)
@@ -751,29 +778,29 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
end
it "creates the user and sets the comment field" do
- expect(provider).to receive(:run_dscl).with("create /Users/toor").and_return(true)
+ expect(provider).to receive(:run_dscl).with("create", "/Users/toor").and_return(true)
provider.dscl_create_user
end
it "sets the comment field" do
- expect(provider).to receive(:run_dscl).with("create /Users/toor RealName '#mockssuck'").and_return(true)
+ expect(provider).to receive(:run_dscl).with("create", "/Users/toor", "RealName", "#mockssuck").and_return(true)
provider.dscl_create_comment
end
it "sets the comment field to username" do
new_resource.comment nil
- expect(provider).to receive(:run_dscl).with("create /Users/toor RealName '#mockssuck'").and_return(true)
+ expect(provider).to receive(:run_dscl).with("create", "/Users/toor", "RealName", "#mockssuck").and_return(true)
provider.dscl_create_comment
expect(new_resource.comment).to eq("#mockssuck")
end
it "should run run_dscl with create /Users/user PrimaryGroupID to set the users primary group" do
- expect(provider).to receive(:run_dscl).with("create /Users/toor PrimaryGroupID '1001'").and_return(true)
+ expect(provider).to receive(:run_dscl).with("create", "/Users/toor", "PrimaryGroupID", 1001).and_return(true)
provider.dscl_set_gid
end
it "should run run_dscl with create /Users/user UserShell to set the users login shell" do
- expect(provider).to receive(:run_dscl).with("create /Users/toor UserShell '/usr/bin/false'").and_return(true)
+ expect(provider).to receive(:run_dscl).with("create", "/Users/toor", "UserShell", "/usr/bin/false").and_return(true)
provider.dscl_set_shell
end
end
@@ -785,21 +812,21 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
end
it "should map the group name to a numeric ID when the group exists" do
- expect(provider).to receive(:run_dscl).with("read /Groups/newgroup PrimaryGroupID").ordered.and_return("PrimaryGroupID: 1001\n")
- expect(provider).to receive(:run_dscl).with("create /Users/toor PrimaryGroupID '1001'").ordered.and_return(true)
+ expect(provider).to receive(:run_dscl).with("read", "/Groups/newgroup", "PrimaryGroupID").ordered.and_return("PrimaryGroupID: 1001\n")
+ expect(provider).to receive(:run_dscl).with("create", "/Users/toor", "PrimaryGroupID", "1001").ordered.and_return(true)
provider.dscl_set_gid
end
it "should raise an exception when the group does not exist" do
shell_return = shellcmdresult.new("<dscl_cmd> DS Error: -14136 (eDSRecordNotFound)", "err", -14136)
- expect(provider).to receive(:shell_out).with("dscl . -read /Groups/newgroup PrimaryGroupID").and_return(shell_return)
+ expect(provider).to receive(:shell_out).with("dscl", ".", "-read", "/Groups/newgroup", "PrimaryGroupID").and_return(shell_return)
expect { provider.dscl_set_gid }.to raise_error(Chef::Exceptions::GroupIDNotFound)
end
end
it "should set group ID to 20 if it's not specified" do
new_resource.gid nil
- expect(provider).to receive(:run_dscl).with("create /Users/toor PrimaryGroupID '20'").ordered.and_return(true)
+ expect(provider).to receive(:run_dscl).with("create", "/Users/toor", "PrimaryGroupID", 20).ordered.and_return(true)
provider.dscl_set_gid
expect(new_resource.gid).to eq(20)
end
@@ -847,8 +874,8 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
describe "when the user exists" do
before do
- expect(provider).to receive(:shell_out).with("dscacheutil '-flushcache'")
- expect(provider).to receive(:shell_out).with("plutil -convert xml1 -o - /var/db/dslocal/nodes/Default/users/toor.plist") do
+ expect(provider).to receive(:shell_out).with("dscacheutil", "-flushcache")
+ expect(provider).to receive(:shell_out).with("plutil", "-convert", "xml1", "-o", "-", "/var/db/dslocal/nodes/Default/users/toor.plist") do
shellcmdresult.new(File.read(File.join(CHEF_SPEC_DATA, "mac_users/10.9.plist.xml")), "", 0)
end
provider.load_current_resource
@@ -856,13 +883,13 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
describe "when Chef is removing the user" do
it "removes the user from the groups and deletes home directory when the resource is configured to manage home" do
- new_resource.supports({ :manage_home => true })
- expect(provider).to receive(:run_dscl).with("list /Groups").and_return("my_group\nyour_group\nreal_group\n")
- expect(provider).to receive(:run_dscl).with("read /Groups/my_group").and_raise(Chef::Exceptions::DsclCommandFailed) # Empty group
- expect(provider).to receive(:run_dscl).with("read /Groups/your_group").and_return("GroupMembership: not_you")
- expect(provider).to receive(:run_dscl).with("read /Groups/real_group").and_return("GroupMembership: toor")
- expect(provider).to receive(:run_dscl).with("delete /Groups/real_group GroupMembership 'toor'")
- expect(provider).to receive(:run_dscl).with("delete /Users/toor")
+ new_resource.manage_home true
+ expect(provider).to receive(:run_dscl).with("list", "/Groups").and_return("my_group\nyour_group\nreal_group\n")
+ expect(provider).to receive(:run_dscl).with("read", "/Groups/my_group").and_raise(Chef::Exceptions::DsclCommandFailed) # Empty group
+ expect(provider).to receive(:run_dscl).with("read", "/Groups/your_group").and_return("GroupMembership: not_you")
+ expect(provider).to receive(:run_dscl).with("read", "/Groups/real_group").and_return("GroupMembership: toor")
+ expect(provider).to receive(:run_dscl).with("delete", "/Groups/real_group", "GroupMembership", "toor")
+ expect(provider).to receive(:run_dscl).with("delete", "/Users/toor")
expect(FileUtils).to receive(:rm_rf).with("/Users/vagrant")
provider.remove_user
end
@@ -885,7 +912,7 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
end
it "can unlock the user" do
- expect(provider).to receive(:run_dscl).with("create /Users/toor AuthenticationAuthority ';ShadowHash;HASHLIST:<SALTED-SHA512-PBKDF2>'")
+ expect(provider).to receive(:run_dscl).with("create", "/Users/toor", "AuthenticationAuthority", ";ShadowHash;HASHLIST:<SALTED-SHA512-PBKDF2>")
provider.unlock_user
end
end
@@ -893,7 +920,7 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
describe "when locking the user" do
it "should run run_dscl with append /Users/user AuthenticationAuthority ;DisabledUser; to lock the user account" do
- expect(provider).to receive(:run_dscl).with("append /Users/toor AuthenticationAuthority ';DisabledUser;'")
+ expect(provider).to receive(:run_dscl).with("append", "/Users/toor", "AuthenticationAuthority", ";DisabledUser;")
provider.lock_user
end
end
diff --git a/spec/unit/provider/user/linux_spec.rb b/spec/unit/provider/user/linux_spec.rb
new file mode 100644
index 0000000000..5092c8f4b9
--- /dev/null
+++ b/spec/unit/provider/user/linux_spec.rb
@@ -0,0 +1,74 @@
+#
+# Author:: Adam Jacob (<adam@chef.io>)
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/provider/user/useradd"
+
+describe Chef::Provider::User::Linux do
+
+ subject(:provider) do
+ p = described_class.new(@new_resource, @run_context)
+ p.current_resource = @current_resource
+ p
+ end
+
+ supported_useradd_options = {
+ "comment" => "-c",
+ "gid" => "-g",
+ "uid" => "-u",
+ "shell" => "-s",
+ "password" => "-p",
+ }
+
+ include_examples "a useradd-based user provider", supported_useradd_options
+
+ describe "manage_home behavior" do
+ before(:each) do
+ @new_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
+ @current_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
+ end
+
+ it "throws an error when trying to set supports manage_home: true" do
+ expect { @new_resource.supports( manage_home: true ) }.to raise_error(NoMethodError)
+ end
+
+ it "throws an error when trying to set supports non_unique: true" do
+ expect { @new_resource.supports( non_unique: true ) }.to raise_error(NoMethodError)
+ end
+
+ it "defaults manage_home to false" do
+ expect( @new_resource.manage_home ).to be false
+ end
+
+ it "by default manage_home is false and we use -M" do
+ expect( provider.useradd_options ).to eql(["-M"])
+ end
+
+ it "setting manage_home to false includes -M" do
+ @new_resource.manage_home false
+ expect( provider.useradd_options ).to eql(["-M"])
+ end
+
+ it "setting manage_home to true includes -m" do
+ @new_resource.manage_home true
+ expect( provider.useradd_options ).to eql(["-m"])
+ end
+ end
+end
diff --git a/spec/unit/provider/user/pw_spec.rb b/spec/unit/provider/user/pw_spec.rb
index 1e9fda9f7e..079fd44ef5 100644
--- a/spec/unit/provider/user/pw_spec.rb
+++ b/spec/unit/provider/user/pw_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Haynes (<sh@nomitor.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,7 @@ describe Chef::Provider::User::Pw do
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::User.new("adam")
+ @new_resource = Chef::Resource::User::PwUser.new("adam")
@new_resource.comment "Adam Jacob"
@new_resource.uid 1000
@new_resource.gid 1000
@@ -32,9 +32,9 @@ describe Chef::Provider::User::Pw do
@new_resource.shell "/usr/bin/zsh"
@new_resource.password "abracadabra"
- @new_resource.supports :manage_home => true
+ @new_resource.manage_home true
- @current_resource = Chef::Resource::User.new("adam")
+ @current_resource = Chef::Resource::User::PwUser.new("adam")
@current_resource.comment "Adam Jacob"
@current_resource.uid 1000
@current_resource.gid 1000
@@ -63,35 +63,30 @@ describe Chef::Provider::User::Pw do
it "should set the option for #{attribute} if the new resources #{attribute} is not null" do
allow(@new_resource).to receive(attribute).and_return("hola")
- expect(@provider.set_options).to eql(" #{@new_resource.username} #{option} '#{@new_resource.send(attribute)}' -m")
- end
-
- it "should set the option for #{attribute} if the new resources #{attribute} is not null, without homedir management" do
- allow(@new_resource).to receive(:supports).and_return({ :manage_home => false })
- allow(@new_resource).to receive(attribute).and_return("hola")
- expect(@provider.set_options).to eql(" #{@new_resource.username} #{option} '#{@new_resource.send(attribute)}'")
+ expect(@provider.set_options).to eql([ @new_resource.username, option, @new_resource.send(attribute), "-m"])
end
end
it "should combine all the possible options" do
- match_string = " adam"
- field_list.sort { |a, b| a[0] <=> b[0] }.each do |attribute, option|
+ match_array = [ "adam" ]
+ field_list.sort_by { |a| a[0] }.each do |attribute, option|
allow(@new_resource).to receive(attribute).and_return("hola")
- match_string << " #{option} 'hola'"
+ match_array << option
+ match_array << "hola"
end
- match_string << " -m"
- expect(@provider.set_options).to eql(match_string)
+ match_array << "-m"
+ expect(@provider.set_options).to eql(match_array)
end
end
describe "create_user" do
before(:each) do
- allow(@provider).to receive(:run_command).and_return(true)
+ allow(@provider).to receive(:shell_out!).and_return(true)
allow(@provider).to receive(:modify_password).and_return(true)
end
it "should run pw useradd with the return of set_options" do
- expect(@provider).to receive(:run_command).with({ :command => "pw useradd adam -m" }).and_return(true)
+ expect(@provider).to receive(:shell_out!).with("pw", "useradd", "adam", "-m").and_return(true)
@provider.create_user
end
@@ -103,12 +98,12 @@ describe Chef::Provider::User::Pw do
describe "manage_user" do
before(:each) do
- allow(@provider).to receive(:run_command).and_return(true)
+ allow(@provider).to receive(:shell_out!).and_return(true)
allow(@provider).to receive(:modify_password).and_return(true)
end
it "should run pw usermod with the return of set_options" do
- expect(@provider).to receive(:run_command).with({ :command => "pw usermod adam -m" }).and_return(true)
+ expect(@provider).to receive(:shell_out!).with("pw", "usermod", "adam", "-m").and_return(true)
@provider.manage_user
end
@@ -120,13 +115,13 @@ describe Chef::Provider::User::Pw do
describe "remove_user" do
it "should run pw userdel with the new resources user name" do
- @new_resource.supports :manage_home => false
- expect(@provider).to receive(:run_command).with({ :command => "pw userdel #{@new_resource.username}" }).and_return(true)
+ @new_resource.manage_home false
+ expect(@provider).to receive(:shell_out!).with("pw", "userdel", @new_resource.username).and_return(true)
@provider.remove_user
end
it "should run pw userdel with the new resources user name and -r if manage_home is true" do
- expect(@provider).to receive(:run_command).with({ :command => "pw userdel #{@new_resource.username} -r" }).and_return(true)
+ expect(@provider).to receive(:shell_out!).with("pw", "userdel", @new_resource.username, "-r").and_return(true)
@provider.remove_user
end
end
@@ -145,90 +140,78 @@ describe Chef::Provider::User::Pw do
describe "when locking the user" do
it "should run pw lock with the new resources username" do
- expect(@provider).to receive(:run_command).with({ :command => "pw lock #{@new_resource.username}" })
+ expect(@provider).to receive(:shell_out!).with("pw", "lock", @new_resource.username)
@provider.lock_user
end
end
describe "when unlocking the user" do
it "should run pw unlock with the new resources username" do
- expect(@provider).to receive(:run_command).with({ :command => "pw unlock #{@new_resource.username}" })
+ expect(@provider).to receive(:shell_out!).with("pw", "unlock", @new_resource.username)
@provider.unlock_user
end
end
describe "when modifying the password" do
before(:each) do
- @status = double("Status", :exitstatus => 0)
- allow(@provider).to receive(:popen4).and_return(@status)
- @pid, @stdin, @stdout, @stderr = nil, nil, nil, nil
+ @status = double("Status", exitstatus: 0)
+ allow(@provider).to receive(:shell_out!).and_return(@status)
end
describe "and the new password has not been specified" do
before(:each) do
- allow(@new_resource).to receive(:password).and_return(nil)
+ @new_resource.password(nil)
end
it "logs an appropriate message" do
- expect(Chef::Log).to receive(:debug).with("user[adam] no change needed to password")
@provider.modify_password
end
end
describe "and the new password has been specified" do
before(:each) do
- allow(@new_resource).to receive(:password).and_return("abracadabra")
+ @new_resource.password("abracadabra")
end
it "should check for differences in password between the new and current resources" do
expect(@current_resource).to receive(:password)
- expect(@new_resource).to receive(:password)
+ expect(@new_resource).to receive(:password).and_call_original.at_least(:once)
@provider.modify_password
end
end
describe "and the passwords are identical" do
before(:each) do
- allow(@new_resource).to receive(:password).and_return("abracadabra")
+ @new_resource.password("abracadabra")
allow(@current_resource).to receive(:password).and_return("abracadabra")
end
it "logs an appropriate message" do
- expect(Chef::Log).to receive(:debug).with("user[adam] no change needed to password")
@provider.modify_password
end
end
describe "and the passwords are different" do
before(:each) do
- allow(@new_resource).to receive(:password).and_return("abracadabra")
+ @new_resource.password("abracadabra")
allow(@current_resource).to receive(:password).and_return("sesame")
end
it "should log an appropriate message" do
- expect(Chef::Log).to receive(:debug).with("user[adam] updating password")
@provider.modify_password
end
it "should run pw usermod with the username and the option -H 0" do
- expect(@provider).to receive(:popen4).with("pw usermod adam -H 0", :waitlast => true).and_return(@status)
- @provider.modify_password
- end
-
- it "should send the new password to the stdin of pw usermod" do
- @stdin = StringIO.new
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ expect(@provider).to receive(:shell_out!).with("pw usermod adam -H 0", { :input => "abracadabra" }).and_return(@status)
@provider.modify_password
- expect(@stdin.string).to eq("abracadabra\n")
end
it "should raise an exception if pw usermod fails" do
- expect(@status).to receive(:exitstatus).and_return(1)
- expect { @provider.modify_password }.to raise_error(Chef::Exceptions::User)
+ expect(@provider).to receive(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed)
+ expect { @provider.modify_password }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
end
it "should not raise an exception if pw usermod succeeds" do
- expect(@status).to receive(:exitstatus).and_return(0)
expect { @provider.modify_password }.not_to raise_error
end
end
@@ -236,16 +219,16 @@ describe Chef::Provider::User::Pw do
describe "when loading the current state" do
before do
- @provider.new_resource = Chef::Resource::User.new("adam")
+ @provider.new_resource = Chef::Resource::User::PwUser.new("adam")
end
it "should raise an error if the required binary /usr/sbin/pw doesn't exist" do
- expect(File).to receive(:exists?).with("/usr/sbin/pw").and_return(false)
+ expect(File).to receive(:exist?).with("/usr/sbin/pw").and_return(false)
expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::User)
end
it "shouldn't raise an error if /usr/sbin/pw exists" do
- allow(File).to receive(:exists?).and_return(true)
+ allow(File).to receive(:exist?).and_return(true)
expect { @provider.load_current_resource }.not_to raise_error
end
end
diff --git a/spec/unit/provider/user/solaris_spec.rb b/spec/unit/provider/user/solaris_spec.rb
index 07a39a1f0c..1935336308 100644
--- a/spec/unit/provider/user/solaris_spec.rb
+++ b/spec/unit/provider/user/solaris_spec.rb
@@ -25,30 +25,35 @@ require "spec_helper"
describe Chef::Provider::User::Solaris do
- let(:shellcmdresult) {
+ let(:shellcmdresult) do
Struct.new(:stdout, :stderr, :exitstatus)
- }
+ end
- subject(:provider) do
- p = described_class.new(@new_resource, @run_context)
- p.current_resource = @current_resource
+ let(:node) do
+ Chef::Node.new.tap do |node|
+ node.automatic["platform"] = "solaris2"
+ end
+ end
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:new_resource) do
+ Chef::Resource::User::SolarisUser.new("adam", @run_context)
+ end
+ let(:current_resource) do
+ Chef::Resource::User::SolarisUser.new("adam", @run_context)
+ end
- # Prevent the useradd-based provider tests from trying to write /etc/shadow
- allow(p).to receive(:write_shadow_file)
- p
+ subject(:provider) do
+ described_class.new(new_resource, run_context).tap do |p|
+ p.current_resource = current_resource
+ # Prevent the useradd-based provider tests from trying to write /etc/shadow
+ allow(p).to receive(:write_shadow_file)
+ end
end
describe "when we want to set a password" do
before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
-
- @new_resource = Chef::Resource::User.new("adam", @run_context)
- @current_resource = Chef::Resource::User.new("adam", @run_context)
-
- @new_resource.password "hocus-pocus"
-
+ new_resource.password "hocus-pocus"
end
it "should use its own shadow file writer to set the password" do
@@ -68,54 +73,88 @@ describe Chef::Provider::User::Solaris do
# may not be able to write to /etc for tests...
temp_file = Tempfile.new("shadow")
allow(Tempfile).to receive(:new).with("shadow", "/etc").and_return(temp_file)
- @new_resource.password "verysecurepassword"
+ new_resource.password "verysecurepassword"
provider.manage_user
expect(::File.open(password_file.path, "r").read).to match(/adam:verysecurepassword:/)
password_file.unlink
end
end
- describe "when managing user locked status" do
- before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
+ describe "#create_user" do
+ context "with a system user" do
+ before { new_resource.system(true) }
+ it "should not pass -r" do
+ expect(provider).to receive(:shell_out!).with("useradd", "adam")
+ provider.create_user
+ end
+ end
- @new_resource = Chef::Resource::User.new("dave")
- @current_resource = @new_resource.dup
+ context "with manage_home" do
+ before { new_resource.manage_home(true) }
+ it "should not pass -r" do
+ expect(provider).to receive(:shell_out!).with("useradd", "-m", "adam")
+ provider.create_user
+ end
+ end
+ end
- @provider = Chef::Provider::User::Solaris.new(@new_resource, @run_context)
- @provider.current_resource = @current_resource
+ describe "when managing user locked status" do
+ let(:user_lock) { "adam:FOO:::::::" }
+ let(:shadow_file_contents) do
+ %W{
+ user1:LK:::::::
+ #{user_lock}
+ user2:NP:::::::
+ }
end
+
describe "when determining if the user is locked" do
+ before do
+ allow(IO).to receive(:read).and_return(shadow_file_contents.join("\n"))
+ end
+
+ context "when user does not exist" do
+ let(:user_lock) { "other_user:FOO:::::::" }
+
+ it "should raise a sensible error" do
+ expect { provider.check_lock }.to raise_error(Chef::Exceptions::User)
+ end
+ end
# locked shadow lines
[
- "dave:LK:::::::",
- "dave:*LK*:::::::",
- "dave:*LK*foobar:::::::",
- "dave:*LK*bahamas10:::::::",
- "dave:*LK*L....:::::::",
+ "adam:*LK*:::::::",
+ "adam:*LK*foobar:::::::",
+ "adam:*LK*bahamas10:::::::",
+ "adam:*LK*goonawaLK:::::::",
+ "adam:*LK*LKgir:::::::",
+ "adam:*LK*L....:::::::",
].each do |shadow|
- it "should return true if user is locked with #{shadow}" do
- shell_return = shellcmdresult.new(shadow + "\n", "", 0)
- expect(provider).to receive(:shell_out!).with("getent", "shadow", @new_resource.username).and_return(shell_return)
- expect(provider.check_lock).to eql(true)
+ context "for user 'adam' with entry '#{shadow}'" do
+ let(:user_lock) { shadow }
+
+ it "should return true" do
+ expect(provider.check_lock).to eql(true)
+ end
end
end
# unlocked shadow lines
[
- "dave:NP:::::::",
- "dave:*NP*:::::::",
- "dave:foobar:::::::",
- "dave:bahamas10:::::::",
- "dave:L...:::::::",
+ "adam:NP:::::::",
+ "adam:*NP*:::::::",
+ "adam:foobar:::::::",
+ "adam:bahamas10:::::::",
+ "adam:goonawaLK:::::::",
+ "adam:LKgir:::::::",
+ "adam:L...:::::::",
].each do |shadow|
- it "should return false if user is unlocked with #{shadow}" do
- shell_return = shellcmdresult.new(shadow + "\n", "", 0)
- expect(provider).to receive(:shell_out!).with("getent", "shadow", @new_resource.username).and_return(shell_return)
- expect(provider.check_lock).to eql(false)
+ context "for user 'adam' with entry '#{shadow}'" do
+ let(:user_lock) { shadow }
+
+ it "should return false" do
+ expect(provider.check_lock).to eql(false)
+ end
end
end
end
@@ -123,7 +162,7 @@ describe Chef::Provider::User::Solaris do
describe "when locking the user" do
it "should run passwd -l with the new resources username" do
shell_return = shellcmdresult.new("", "", 0)
- expect(provider).to receive(:shell_out!).with("passwd", "-l", @new_resource.username).and_return(shell_return)
+ expect(provider).to receive(:shell_out!).with("passwd", "-l", "adam").and_return(shell_return)
provider.lock_user
end
end
@@ -131,7 +170,7 @@ describe Chef::Provider::User::Solaris do
describe "when unlocking the user" do
it "should run passwd -u with the new resources username" do
shell_return = shellcmdresult.new("", "", 0)
- expect(provider).to receive(:shell_out!).with("passwd", "-u", @new_resource.username).and_return(shell_return)
+ expect(provider).to receive(:shell_out!).with("passwd", "-u", "adam").and_return(shell_return)
provider.unlock_user
end
end
diff --git a/spec/unit/provider/user/useradd_spec.rb b/spec/unit/provider/user/useradd_spec.rb
deleted file mode 100644
index 7c67449a86..0000000000
--- a/spec/unit/provider/user/useradd_spec.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-#
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-require "chef/provider/user/useradd"
-
-describe Chef::Provider::User::Useradd do
-
- subject(:provider) do
- p = described_class.new(@new_resource, @run_context)
- p.current_resource = @current_resource
- p
- end
-
- supported_useradd_options = {
- "comment" => "-c",
- "gid" => "-g",
- "uid" => "-u",
- "shell" => "-s",
- "password" => "-p",
- }
-
- include_examples "a useradd-based user provider", supported_useradd_options
-
- describe "manage_user" do
- # CHEF-5247: Chef::Provider::User::Solaris subclasses Chef::Provider::User::Useradd, but does not use usermod to change passwords.
- # Thus, a call to Solaris#manage_user calls Solaris#manage_password and Useradd#manage_user, but the latter should be a no-op.
- it "should not run the command if universal_options is an empty array" do
- allow(provider).to receive(:universal_options).and_return([])
- expect(provider).not_to receive(:shell_out!)
- provider.manage_user
- end
- end
-end
diff --git a/spec/unit/provider/user/windows_spec.rb b/spec/unit/provider/user/windows_spec.rb
index 4a62e6ec9d..179926e5e6 100644
--- a/spec/unit/provider/user/windows_spec.rb
+++ b/spec/unit/provider/user/windows_spec.rb
@@ -28,12 +28,15 @@ class Chef
end
describe Chef::Provider::User::Windows do
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+
before(:each) do
@node = Chef::Node.new
- @new_resource = Chef::Resource::User.new("monkey")
+ @new_resource = Chef::Resource::User::WindowsUser.new("monkey")
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
- @current_resource = Chef::Resource::User.new("monkey")
+ allow(@run_context).to receive(:logger).and_return(logger)
+ @current_resource = Chef::Resource::User::WindowsUser.new("monkey")
@net_user = double("Chef::Util::Windows::NetUser")
allow(Chef::Util::Windows::NetUser).to receive(:new).and_return(@net_user)
@@ -89,7 +92,7 @@ describe Chef::Provider::User::Windows do
describe "and the attributes do not match" do
before do
- @current_resource = Chef::Resource::User.new("adam")
+ @current_resource = Chef::Resource::User::WindowsUser.new("adam")
@current_resource.comment "Adam Jacob-foo"
@current_resource.uid 1111
@current_resource.gid 1111
@@ -127,19 +130,19 @@ describe Chef::Provider::User::Windows do
describe "when creating the user" do
it "should call @net_user.add with the return of set_options" do
- allow(@provider).to receive(:set_options).and_return(:name => "monkey")
- expect(@net_user).to receive(:add).with(:name => "monkey")
+ allow(@provider).to receive(:set_options).and_return(name: "monkey")
+ expect(@net_user).to receive(:add).with(name: "monkey")
@provider.create_user
end
end
describe "manage_user" do
before(:each) do
- allow(@provider).to receive(:set_options).and_return(:name => "monkey")
+ allow(@provider).to receive(:set_options).and_return(name: "monkey")
end
it "should call @net_user.update with the return of set_options" do
- expect(@net_user).to receive(:update).with(:name => "monkey")
+ expect(@net_user).to receive(:update).with(name: "monkey")
@provider.manage_user
end
end
diff --git a/spec/unit/provider/user_spec.rb b/spec/unit/provider/user_spec.rb
index 1cdbe462f7..9da933606e 100644
--- a/spec/unit/provider/user_spec.rb
+++ b/spec/unit/provider/user_spec.rb
@@ -58,7 +58,7 @@ describe Chef::Provider::User do
describe "executing load_current_resource" do
before(:each) do
@node = Chef::Node.new
- #@new_resource = double("Chef::Resource::User",
+ # @new_resource = double("Chef::Resource::User",
# :null_object => true,
# :username => "adam",
# :comment => "Adam Jacob",
@@ -68,7 +68,7 @@ describe Chef::Provider::User do
# :shell => "/usr/bin/zsh",
# :password => nil,
# :updated => nil
- #)
+ # )
allow(Chef::Resource::User).to receive(:new).and_return(@current_resource)
@pw_user = EtcPwnamIsh.new
@pw_user.name = "adam"
@@ -110,11 +110,11 @@ describe Chef::Provider::User do
# The mapping between the Chef::Resource::User and Getpwnam struct
user_attrib_map = {
- :uid => :uid,
- :gid => :gid,
- :comment => :gecos,
- :home => :dir,
- :shell => :shell,
+ uid: :uid,
+ gid: :gid,
+ comment: :gecos,
+ home: :dir,
+ shell: :shell,
}
user_attrib_map.each do |user_attrib, getpwnam_attrib|
it "should set the current resources #{user_attrib} based on getpwnam #{getpwnam_attrib}" do
@@ -129,7 +129,7 @@ describe Chef::Provider::User do
end
it "shouldn't try and convert the group gid if none has been supplied" do
- allow(@new_resource).to receive(:gid).and_return(nil)
+ @new_resource.gid(false)
expect(@provider).not_to receive(:convert_group_name)
@provider.load_current_resource
end
@@ -140,18 +140,16 @@ describe Chef::Provider::User do
describe "and running assertions" do
def self.shadow_lib_unavail?
- begin
- require "rubygems"
- require "shadow"
- rescue LoadError
- skip "ruby-shadow gem not installed for dynamic load test"
- true
- else
- false
- end
+ require "rubygems"
+ require "shadow"
+ rescue LoadError
+ skip "ruby-shadow gem not installed for dynamic load test"
+ true
+ else
+ false
end
- before (:each) do
+ before(:each) do
user = @pw_user.dup
user.name = "root"
user.passwd = "x"
@@ -161,15 +159,15 @@ describe Chef::Provider::User do
unless shadow_lib_unavail?
context "and we have the ruby-shadow gem" do
- skip "and we are not root (rerun this again as root)", :requires_unprivileged_user => true
+ skip "and we are not root (rerun this again as root)", requires_unprivileged_user: true
- context "and we are root", :requires_root => true do
+ context "and we are root", requires_root: true do
it "should pass assertions when ruby-shadow can be loaded" do
@provider.action = "create"
original_method = @provider.method(:require)
expect(@provider).to receive(:require) { |*args| original_method.call(*args) }
- passwd_info = Struct::PasswdEntry.new(:sp_namp => "adm ", :sp_pwdp => "$1$T0N0Q.lc$nyG6pFI3Dpqa5cxUz/57j0", :sp_lstchg => 14861, :sp_min => 0, :sp_max => 99999,
- :sp_warn => 7, :sp_inact => -1, :sp_expire => -1, :sp_flag => -1)
+ passwd_info = Struct::PasswdEntry.new(sp_namp: "adm ", sp_pwdp: "$1$T0N0Q.lc$nyG6pFI3Dpqa5cxUz/57j0", sp_lstchg: 14861, sp_min: 0, sp_max: 99999,
+ sp_warn: 7, sp_inact: -1, sp_expire: -1, sp_flag: -1)
expect(Shadow::Passwd).to receive(:getspnam).with("adam").and_return(passwd_info)
@provider.load_current_resource
@provider.define_resource_requirements
@@ -190,7 +188,7 @@ describe Chef::Provider::User do
end
describe "compare_user" do
- let(:mapping) {
+ let(:mapping) do
{
"username" => %w{adam Adam},
"comment" => ["Adam Jacob", "adam jacob"],
@@ -200,7 +198,7 @@ describe Chef::Provider::User do
"shell" => ["/usr/bin/zsh", "/bin/bash"],
"password" => %w{abcd 12345},
}
- }
+ end
%w{uid gid comment home shell password}.each do |attribute|
it "should return true if #{attribute} doesn't match" do
@@ -221,6 +219,12 @@ describe Chef::Provider::User do
it "should return false if the objects are identical" do
expect(@provider.compare_user).to eql(false)
end
+
+ it "should ignore differences in trailing slash in home paths" do
+ @new_resource.home "/home/adam"
+ @current_resource.home "/home/adam/"
+ expect(@provider.compare_user).to eql(false)
+ end
end
describe "action_create" do
diff --git a/spec/unit/provider/env_spec.rb b/spec/unit/provider/windows_env_spec.rb
index e99aee5ad1..a42a0509c5 100644
--- a/spec/unit/provider/env_spec.rb
+++ b/spec/unit/provider/windows_env_spec.rb
@@ -18,15 +18,16 @@
require "spec_helper"
-describe Chef::Provider::Env do
+describe Chef::Provider::WindowsEnv, :windows_only do
before do
@node = Chef::Node.new
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::Env.new("FOO")
+ @new_resource = Chef::Resource::WindowsEnv.new("FOO")
@new_resource.value("bar")
- @provider = Chef::Provider::Env.new(@new_resource, @run_context)
+ @new_resource.user("<System>")
+ @provider = Chef::Provider::WindowsEnv.new(@new_resource, @run_context)
end
it "assumes the key_name exists by default" do
@@ -47,12 +48,17 @@ describe Chef::Provider::Env do
expect(@provider.new_resource.name).to eq("FOO")
end
+ it "should create a current resource with the same user as the new resource" do
+ @provider.load_current_resource
+ expect(@provider.new_resource.user).to eq("<System>")
+ end
+
it "should set the key_name to the key name of the new resource" do
@provider.load_current_resource
expect(@provider.current_resource.key_name).to eq("FOO")
end
- it "should check if the key_name exists" do
+ it "should check if the key_name and user exist" do
expect(@provider).to receive(:env_key_exists).with("FOO").and_return(true)
@provider.load_current_resource
expect(@provider.key_exists).to be_truthy
@@ -65,7 +71,7 @@ describe Chef::Provider::Env do
end
it "should return the current resource" do
- expect(@provider.load_current_resource).to be_a_kind_of(Chef::Resource::Env)
+ expect(@provider.load_current_resource).to be_a_kind_of(Chef::Resource::WindowsEnv)
end
end
@@ -76,7 +82,7 @@ describe Chef::Provider::Env do
allow(@provider).to receive(:modify_env).and_return(true)
end
- it "should call create_env if the key does not exist" do
+ it "should call create_env if the key does not exist with user" do
expect(@provider).to receive(:create_env).and_return(true)
@provider.action_create
end
@@ -92,7 +98,7 @@ describe Chef::Provider::Env do
@provider.action_create
end
- it "should call modify_env if the key exists and values are not equal" do
+ it "should call modify_env if the key exists with provided user and values are not equal" do
@provider.key_exists = true
allow(@provider).to receive(:requires_modify_or_create?).and_return(true)
expect(@provider).to receive(:modify_env).and_return(true)
@@ -152,6 +158,12 @@ describe Chef::Provider::Env do
@provider.action_modify
end
+ it "should call modify_group if the key exists and users are not equal" do
+ expect(@provider).to receive(:requires_modify_or_create?).and_return(true)
+ expect(@provider).to receive(:modify_env).and_return(true)
+ @provider.action_modify
+ end
+
it "should set the new resources updated flag to true if modify_env is called" do
allow(@provider).to receive(:requires_modify_or_create?).and_return(true)
allow(@provider).to receive(:modify_env).and_return(true)
@@ -159,36 +171,37 @@ describe Chef::Provider::Env do
expect(@new_resource).to be_updated
end
- it "should not call modify_env if the key exists but the values are equal" do
+ it "should not call modify_env if the key exists with user but the values are equal" do
expect(@provider).to receive(:requires_modify_or_create?).and_return(false)
expect(@provider).not_to receive(:modify_env)
@provider.action_modify
end
- it "should raise a Chef::Exceptions::Env if the key doesn't exist" do
+ it "should raise a Chef::Exceptions::WindowsEnv if the key doesn't exist" do
@provider.key_exists = false
- expect { @provider.action_modify }.to raise_error(Chef::Exceptions::Env)
+ expect { @provider.action_modify }.to raise_error(Chef::Exceptions::WindowsEnv)
end
end
describe "delete_element" do
before(:each) do
- @current_resource = Chef::Resource::Env.new("FOO")
+ @current_resource = Chef::Resource::WindowsEnv.new("FOO")
@new_resource.delim ";"
@new_resource.value "C:/bar/bin"
+ @current_resource.user "<System>"
@current_resource.value "C:/foo/bin;C:/bar/bin"
@provider.current_resource = @current_resource
end
it "should return true if the element is not found" do
- allow(@new_resource).to receive(:value).and_return("C:/baz/bin")
+ @new_resource.value("C:/baz/bin")
expect(@provider.delete_element).to eql(true)
end
it "should return false if the delim not defined" do
- allow(@new_resource).to receive(:delim).and_return(nil)
+ @new_resource.delim(nil)
expect(@provider.delete_element).to eql(false)
end
@@ -280,7 +293,7 @@ describe Chef::Provider::Env do
allow(@provider).to receive(:create_env).and_return(true)
@new_resource.delim ";"
- @current_resource = Chef::Resource::Env.new("FOO")
+ @current_resource = Chef::Resource::WindowsEnv.new("FOO")
@current_resource.value "C:/foo/bin"
@provider.current_resource = @current_resource
end
@@ -307,4 +320,81 @@ describe Chef::Provider::Env do
expect(@new_resource.value).to eq("C:/foo;C:/bar;C:/baz;C:/foo/bar")
end
end
+
+ context "when environment variable is not PATH" do
+ let(:new_resource) do
+ new_resource = Chef::Resource::WindowsEnv.new("CHEF_WINDOWS_ENV_TEST")
+ new_resource.value("foo")
+ new_resource
+ end
+ let(:provider) do
+ provider = Chef::Provider::WindowsEnv.new(new_resource, run_context)
+ allow(provider).to receive(:env_obj).and_return(double("null object").as_null_object)
+ provider
+ end
+
+ describe "action_create" do
+ before do
+ ENV.delete("CHEF_WINDOWS_ENV_TEST")
+ provider.key_exists = false
+ end
+
+ it "should update the ruby ENV object when it creates the key" do
+ provider.action_create
+ expect(ENV["CHEF_WINDOWS_ENV_TEST"]).to eql("foo")
+ end
+ end
+
+ describe "action_modify" do
+ before do
+ ENV["CHEF_WINDOWS_ENV_TEST"] = "foo"
+ end
+
+ it "should update the ruby ENV object when it updates the value" do
+ expect(provider).to receive(:requires_modify_or_create?).and_return(true)
+ new_resource.value("foobar")
+ provider.action_modify
+ expect(ENV["CHEF_WINDOWS_ENV_TEST"]).to eql("foobar")
+ end
+
+ describe "action_delete" do
+ before do
+ ENV["CHEF_WINDOWS_ENV_TEST"] = "foo"
+ end
+
+ it "should update the ruby ENV object when it deletes the key" do
+ provider.action_delete
+ expect(ENV["CHEF_WINDOWS_ENV_TEST"]).to eql(nil)
+ end
+ end
+ end
+ end
+
+ context "when environment is PATH" do
+ describe "for PATH" do
+ let(:system_root) { "%SystemRoot%" }
+ let(:system_root_value) { 'D:\Windows' }
+ let(:new_resource) do
+ new_resource = Chef::Resource::WindowsEnv.new("PATH")
+ new_resource.value(system_root)
+ new_resource
+ end
+ let(:provider) do
+ provider = Chef::Provider::WindowsEnv.new(new_resource, run_context)
+ allow(provider).to receive(:env_obj).and_return(double("null object").as_null_object)
+ provider
+ end
+
+ before do
+ stub_const("ENV", { "PATH" => "" })
+ end
+
+ it "replaces Windows system variables" do
+ expect(provider).to receive(:requires_modify_or_create?).and_return(true)
+ expect(provider).to receive(:expand_path).with(system_root).and_return(system_root_value)
+ provider.action_modify
+ expect(ENV["PATH"]).to eql(system_root_value)
+ end
+ end
+ end
end
diff --git a/spec/unit/provider/windows_path_spec.rb b/spec/unit/provider/windows_path_spec.rb
new file mode 100644
index 0000000000..7eca3f3113
--- /dev/null
+++ b/spec/unit/provider/windows_path_spec.rb
@@ -0,0 +1,65 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2008-2017, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Provider::WindowsPath, :windows_only do
+ before(:all) do
+ @old_path = ENV["PATH"].dup
+ end
+
+ after(:all) do
+ ENV["PATH"] = @old_path
+ end
+
+ let(:new_resource) { Chef::Resource::WindowsPath.new("some_path") }
+
+ let(:provider) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ Chef::Provider::WindowsPath.new(new_resource, run_context)
+ end
+
+ describe "#load_current_resource" do
+ it "returns a current_resource" do
+ expect(provider.load_current_resource).to be_kind_of(Chef::Resource::WindowsPath)
+ end
+
+ it "sets the path of current resource as the path of new resource" do
+ current_resource = provider.load_current_resource
+ expect(current_resource.path).to eq("some_path")
+ end
+ end
+
+ describe "#action_add" do
+ it "uses env resource to add 'path' environment variable" do
+ allow(provider).to receive(:expand_env_vars)
+ expect(provider).to receive(:declare_resource).with(:env, "path")
+ provider.run_action(:add)
+ end
+ end
+
+ describe "#action_remove" do
+ it "uses env resource to remove 'path' environment variable" do
+ allow(provider).to receive(:expand_env_vars)
+ expect(provider).to receive(:declare_resource).with(:env, "path")
+ provider.run_action(:remove)
+ end
+ end
+end
diff --git a/spec/unit/provider/windows_task_spec.rb b/spec/unit/provider/windows_task_spec.rb
new file mode 100644
index 0000000000..603c9d1342
--- /dev/null
+++ b/spec/unit/provider/windows_task_spec.rb
@@ -0,0 +1,420 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Provider::WindowsTask, :windows_only do
+ let(:new_resource) { Chef::Resource::WindowsTask.new("sample_task") }
+ let(:current_resource) { Chef::Resource::WindowsTask.new() }
+
+ let(:provider) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ Chef::Provider::WindowsTask.new(new_resource, run_context)
+ end
+
+ describe "#load_current_resource" do
+ it "returns a current_resource" do
+ expect(provider.load_current_resource).to be_kind_of(Chef::Resource::WindowsTask)
+ end
+ end
+
+ describe "#set_start_day_and_time" do
+ it "sets the curret date and time start_day and start_time if nothing is provided by user" do
+ new_resource.start_day = nil
+ new_resource.start_time = nil
+ provider.send(:set_start_day_and_time)
+ expect(new_resource.start_day).not_to be_nil
+ expect(new_resource.start_time).not_to be_nil
+ end
+
+ it "does not set start_day and start_time if given by user" do
+ new_resource.start_day = "12/02/2017"
+ new_resource.start_time = "17:30"
+ provider.send(:set_start_day_and_time)
+ expect(new_resource.start_day).to eq("12/02/2017")
+ expect(new_resource.start_time).to eq("17:30")
+ end
+ end
+
+ describe "#trigger" do
+ it "returns the trigger values in hash format" do
+ new_resource.start_day "12/02/2017"
+ new_resource.start_time "17:30"
+ new_resource.frequency :minute
+ new_resource.frequency_modifier 15
+ new_resource.random_delay 60
+ result = {
+ :start_year => 2017,
+ :start_month => 12,
+ :start_day => 2,
+ :start_hour => 17,
+ :start_minute => 30,
+ :end_month => 0,
+ :end_day => 0,
+ :end_year => 0,
+ :trigger_type => 1,
+ :type => { :once => nil },
+ :random_minutes_interval => 60,
+ :minutes_interval => 15,
+ :run_on_last_day_of_month => false,
+ :run_on_last_week_of_month => false
+
+ }
+ expect(provider.send(:trigger)).to eq(result)
+ end
+ end
+
+ describe "#convert_hours_in_minutes" do
+ it "converts given hours in minutes" do
+ expect(provider.send(:convert_hours_in_minutes, 5)).to eq(300)
+ end
+ end
+
+ describe "#trigger_type" do
+ it "returns 1 if frequency :once" do
+ new_resource.frequency :once
+ expect(provider.send(:trigger_type)).to eq(1)
+ end
+
+ it "returns 2 if frequency :daily" do
+ new_resource.frequency :daily
+ expect(provider.send(:trigger_type)).to eq(2)
+ end
+
+ it "returns 3 if frequency :weekly" do
+ new_resource.frequency :weekly
+ expect(provider.send(:trigger_type)).to eq(3)
+ end
+
+ it "returns 4 if frequency :monthly" do
+ new_resource.frequency :monthly
+ expect(provider.send(:trigger_type)).to eq(4)
+ end
+
+ it "returns 5 if frequency :monthly and frequency_modifier is 'first, second'" do
+ new_resource.frequency :monthly
+ new_resource.frequency_modifier "first, second"
+ expect(provider.send(:trigger_type)).to eq(5)
+ end
+
+ it "returns 6 if frequency :on_idle" do
+ new_resource.frequency :on_idle
+ expect(provider.send(:trigger_type)).to eq(6)
+ end
+
+ it "returns 8 if frequency :onstart" do
+ new_resource.frequency :onstart
+ expect(provider.send(:trigger_type)).to eq(8)
+ end
+
+ it "returns 9 if frequency :on_logon" do
+ new_resource.frequency :on_logon
+ expect(provider.send(:trigger_type)).to eq(9)
+ end
+ end
+
+ describe "#type" do
+ it "returns type hash when frequency :once" do
+ new_resource.frequency :once
+ new_resource.frequency_modifier 2
+ result = provider.send(:type)
+ expect(result).to include(:once)
+ expect(result).to eq({ :once => nil })
+ end
+
+ it "returns type hash when frequency :daily" do
+ new_resource.frequency :daily
+ new_resource.frequency_modifier 2
+ result = provider.send(:type)
+ expect(result).to include(:days_interval)
+ expect(result).to eq({ days_interval: 2 })
+ end
+
+ it "returns type hash when frequency :weekly" do
+ new_resource.start_day "01/02/2018"
+ new_resource.frequency :weekly
+ new_resource.frequency_modifier 2
+ result = provider.send(:type)
+ expect(result).to include(:weeks_interval)
+ expect(result).to include(:days_of_week)
+ expect(result).to eq({ weeks_interval: 2, days_of_week: 4 })
+ end
+
+ it "returns type hash when frequency :monthly" do
+ new_resource.frequency :monthly
+ result = provider.send(:type)
+ expect(result).to include(:months)
+ expect(result).to include(:days)
+ expect(result).to eq({ months: 4095, days: 1 })
+ end
+
+ it "returns type hash when frequency :monthly with frequency_modifier 'first, second, third'" do
+ new_resource.start_day "01/02/2018"
+ new_resource.frequency :monthly
+ new_resource.frequency_modifier "First, Second, third"
+ result = provider.send(:type)
+ expect(result).to include(:months)
+ expect(result).to include(:days_of_week)
+ expect(result).to include(:weeks_of_month)
+ expect(result).to eq({ months: 4095, days_of_week: 4, weeks_of_month: 7 })
+ end
+
+ it "returns type hash when frequency :on_idle" do
+ new_resource.frequency :on_idle
+ result = provider.send(:type)
+ expect(result).to eq(nil)
+ end
+
+ it "returns type hash when frequency :onstart" do
+ new_resource.frequency :onstart
+ result = provider.send(:type)
+ expect(result).to eq(nil)
+ end
+
+ it "returns type hash when frequency :on_logon" do
+ new_resource.frequency :on_logon
+ result = provider.send(:type)
+ expect(result).to eq(nil)
+ end
+ end
+
+ describe "#weeks_of_month" do
+ it "returns the binary value 1 if frequency_modifier is set as 'first'" do
+ new_resource.frequency_modifier "first"
+ expect(provider.send(:weeks_of_month)).to eq(1)
+ end
+
+ it "returns the binary value 2 if frequency_modifier is set as 'second'" do
+ new_resource.frequency_modifier "second"
+ expect(provider.send(:weeks_of_month)).to eq(2)
+ end
+
+ it "returns the binary value 4 if frequency_modifier is set as 'third'" do
+ new_resource.frequency_modifier "third"
+ expect(provider.send(:weeks_of_month)).to eq(4)
+ end
+
+ it "returns the binary value 8 if frequency_modifier is set as 'fourth'" do
+ new_resource.frequency_modifier "fourth"
+ expect(provider.send(:weeks_of_month)).to eq(8)
+ end
+
+ it "returns the binary value 16 if frequency_modifier is set as 'last'" do
+ new_resource.frequency_modifier "last"
+ expect(provider.send(:weeks_of_month)).to eq(nil)
+ end
+ end
+
+ describe "#weeks_of_month" do
+ it "returns the binary value 1 if frequency_modifier is set as 'first'" do
+ new_resource.frequency_modifier "first"
+ expect(provider.send(:weeks_of_month)).to eq(1)
+ end
+
+ it "returns the binary value 2 if frequency_modifier is set as 'second'" do
+ new_resource.frequency_modifier "second"
+ expect(provider.send(:weeks_of_month)).to eq(2)
+ end
+
+ it "returns the binary value 4 if frequency_modifier is set as 'third'" do
+ new_resource.frequency_modifier "third"
+ expect(provider.send(:weeks_of_month)).to eq(4)
+ end
+
+ it "returns the binary value 8 if frequency_modifier is set as 'fourth'" do
+ new_resource.frequency_modifier "fourth"
+ expect(provider.send(:weeks_of_month)).to eq(8)
+ end
+
+ it "returns the binary value 16 if frequency_modifier is set as 'last'" do
+ new_resource.frequency_modifier "last"
+ expect(provider.send(:weeks_of_month)).to eq(nil)
+ end
+
+ it "returns the binary value 15 if frequency_modifier is set as 'first, second, third, fourth'" do
+ new_resource.frequency_modifier "first, second, third, fourth"
+ expect(provider.send(:weeks_of_month)).to eq(15)
+ end
+ end
+
+ # REF: https://msdn.microsoft.com/en-us/library/windows/desktop/aa382063(v=vs.85).aspx
+ describe "#days_of_month" do
+ it "returns the binary value 1 if day is set as 1" do
+ new_resource.day "1"
+ expect(provider.send(:days_of_month)).to eq(1)
+ end
+
+ it "returns the binary value 2 if day is set as 2" do
+ new_resource.day "2"
+ expect(provider.send(:days_of_month)).to eq(2)
+ end
+
+ it "returns the binary value 1073741824 if day is set as 31" do
+ new_resource.day "31"
+ expect(provider.send(:days_of_month)).to eq(1073741824)
+ end
+
+ it "returns the binary value 131072 if day is set as 18" do
+ new_resource.day "18"
+ expect(provider.send(:days_of_month)).to eq(131072)
+ end
+ end
+
+ #Ref : https://msdn.microsoft.com/en-us/library/windows/desktop/aa380729(v=vs.85).aspx
+ describe "#days_of_week" do
+ it "returns the binary value 2 if day is set as 'Mon'" do
+ new_resource.day "Mon"
+ expect(provider.send(:days_of_week)).to eq(2)
+ end
+
+ it "returns the binary value 4 if day is set as 'Tue'" do
+ new_resource.day "Tue"
+ expect(provider.send(:days_of_week)).to eq(4)
+ end
+
+ it "returns the binary value 8 if day is set as 'Wed'" do
+ new_resource.day "Wed"
+ expect(provider.send(:days_of_week)).to eq(8)
+ end
+
+ it "returns the binary value 16 if day is set as 'Thu'" do
+ new_resource.day "Thu"
+ expect(provider.send(:days_of_week)).to eq(16)
+ end
+
+ it "returns the binary value 32 if day is set as 'Fri'" do
+ new_resource.day "Fri"
+ expect(provider.send(:days_of_week)).to eq(32)
+ end
+
+ it "returns the binary value 64 if day is set as 'Sat'" do
+ new_resource.day "Sat"
+ expect(provider.send(:days_of_week)).to eq(64)
+ end
+
+ it "returns the binary value 1 if day is set as 'Sun'" do
+ new_resource.day "Sun"
+ expect(provider.send(:days_of_week)).to eq(1)
+ end
+
+ it "returns the binary value 127 if day is set as 'Mon, tue, wed, thu, fri, sat, sun'" do
+ new_resource.day "Mon, tue, wed, thu, fri, sat, sun"
+ expect(provider.send(:days_of_week)).to eq(127)
+ end
+ end
+
+ # REf: https://msdn.microsoft.com/en-us/library/windows/desktop/aa382064(v=vs.85).aspx
+ describe "#monts_of_year" do
+ it "returns the binary value 1 if day is set as 'Jan'" do
+ new_resource.months "Jan"
+ expect(provider.send(:months_of_year)).to eq(1)
+ end
+
+ it "returns the binary value 2 if day is set as 'Feb'" do
+ new_resource.months "Feb"
+ expect(provider.send(:months_of_year)).to eq(2)
+ end
+
+ it "returns the binary value 4 if day is set as 'Mar'" do
+ new_resource.months "Mar"
+ expect(provider.send(:months_of_year)).to eq(4)
+ end
+
+ it "returns the binary value 8 if day is set as 'Apr'" do
+ new_resource.months "Apr"
+ expect(provider.send(:months_of_year)).to eq(8)
+ end
+
+ it "returns the binary value 16 if day is set as 'May'" do
+ new_resource.months "May"
+ expect(provider.send(:months_of_year)).to eq(16)
+ end
+
+ it "returns the binary value 32 if day is set as 'Jun'" do
+ new_resource.months "Jun"
+ expect(provider.send(:months_of_year)).to eq(32)
+ end
+
+ it "returns the binary value 64 if day is set as 'Jul'" do
+ new_resource.months "Jul"
+ expect(provider.send(:months_of_year)).to eq(64)
+ end
+
+ it "returns the binary value 128 if day is set as 'Aug'" do
+ new_resource.months "Aug"
+ expect(provider.send(:months_of_year)).to eq(128)
+ end
+
+ it "returns the binary value 256 if day is set as 'Sep'" do
+ new_resource.months "Sep"
+ expect(provider.send(:months_of_year)).to eq(256)
+ end
+
+ it "returns the binary value 512 if day is set as 'Oct'" do
+ new_resource.months "Oct"
+ expect(provider.send(:months_of_year)).to eq(512)
+ end
+
+ it "returns the binary value 1024 if day is set as 'Nov'" do
+ new_resource.months "Nov"
+ expect(provider.send(:months_of_year)).to eq(1024)
+ end
+
+ it "returns the binary value 2048 if day is set as 'Dec'" do
+ new_resource.months "Dec"
+ expect(provider.send(:months_of_year)).to eq(2048)
+ end
+
+ it "returns the binary value 4095 if day is set as 'jan, Feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec'" do
+ new_resource.months "jan, Feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec"
+ expect(provider.send(:months_of_year)).to eq(4095)
+ end
+ end
+
+ describe "#run_level" do
+ it "return binary value 1 for run_level highest" do
+ new_resource.run_level :highest
+ expect(provider.send(:run_level)).to be(1)
+ end
+
+ it "return binary value 1 for run_level limited" do
+ new_resource.run_level :limited
+ expect(provider.send(:run_level)).to be(0)
+ end
+ end
+
+ describe "#logon_type" do
+ it "return logon_type bindary value as 5 as if password is nil" do
+ new_resource.password = nil
+ expect(provider.send(:logon_type)).to be(5)
+ end
+
+ it "return logon_type bindary value as 1 as if password is not nil" do
+ new_resource.password = "abc"
+ expect(provider.send(:logon_type)).to be(1)
+ end
+ end
+
+ describe "#get_day" do
+ it "return day if date is provided" do
+ expect(provider.send(:get_day, "01/02/2018")).to eq("TUE")
+ end
+ end
+end
diff --git a/spec/unit/provider/yum_repository_spec.rb b/spec/unit/provider/yum_repository_spec.rb
new file mode 100644
index 0000000000..5b019f7d3e
--- /dev/null
+++ b/spec/unit/provider/yum_repository_spec.rb
@@ -0,0 +1,35 @@
+#
+# Author:: Thom May (<thom@chef.io>)
+# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Provider::YumRepository do
+ let(:new_resource) { Chef::Resource::YumRepository.new("multiverse") }
+
+ let(:provider) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ Chef::Provider::YumRepository.new(new_resource, run_context)
+ end
+
+ it "responds to load_current_resource" do
+ expect(provider).to respond_to(:load_current_resource)
+ end
+
+end
diff --git a/spec/unit/provider/zypper_repository_spec.rb b/spec/unit/provider/zypper_repository_spec.rb
new file mode 100644
index 0000000000..2509452672
--- /dev/null
+++ b/spec/unit/provider/zypper_repository_spec.rb
@@ -0,0 +1,126 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: 2017, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+# Output of the command:
+# => rpm -qa gpg-pubkey*
+RPM_KEYS = <<-EOF
+gpg-pubkey-307e3d54-4be01a65
+gpg-pubkey-3dbdc284-53674dd4
+EOF
+
+# Output of the command:
+# => gpg --with-fingerprint [FILE]
+GPG_FINGER = <<-EOF
+pub 2048R/3DBDC284 2011-08-19 [expires: 2024-06-14]
+ Key fingerprint = 573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62
+uid nginx signing key <signing-key@nginx.com>
+EOF
+
+describe Chef::Provider::ZypperRepository do
+ let(:new_resource) { Chef::Resource::ZypperRepository.new("Nginx Repository") }
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
+ let(:provider) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ allow(run_context).to receive(:logger).and_return(logger)
+ Chef::Provider::ZypperRepository.new(new_resource, run_context)
+ end
+
+ let(:rpm_key_finger) do
+ double("shell_out_with_systems_locale", stdout: RPM_KEYS, exitstatus: 0, error?: false)
+ end
+
+ let(:gpg_finger) do
+ double("shell_out_with_systems_locale", stdout: GPG_FINGER, exitstatus: 0, error?: false)
+ end
+
+ it "responds to load_current_resource" do
+ expect(provider).to respond_to(:load_current_resource)
+ end
+
+ describe "#action_create" do
+ it "skips key import if gpgautoimportkeys is false" do
+ new_resource.gpgautoimportkeys(false)
+ expect(provider).to receive(:declare_resource)
+ expect(logger).to receive(:trace)
+ provider.run_action(:create)
+ end
+ end
+
+ describe "#escaped_repo_name" do
+ it "returns an escaped repo name" do
+ expect(provider.escaped_repo_name).to eq('Nginx\\ Repository')
+ end
+ end
+
+ describe "#cookbook_name" do
+ it "returns 'test' when the cookbook property is set" do
+ new_resource.cookbook("test")
+ expect(provider.cookbook_name).to eq("test")
+ end
+ end
+
+ describe "#key_type" do
+ it "returns :remote_file with an http URL" do
+ expect(provider.key_type("https://www.chef.io/key")).to eq(:remote_file)
+ end
+
+ it "returns :cookbook_file with a chef managed file" do
+ expect(provider).to receive(:has_cookbook_file?).and_return(true)
+ expect(provider.key_type("/foo/nginx.key")).to eq(:cookbook_file)
+ end
+
+ it "throws exception if an unknown file specified" do
+ expect(provider).to receive(:has_cookbook_file?).and_return(false)
+ expect { provider.key_type("/foo/nginx.key") }.to raise_error(Chef::Exceptions::FileNotFound)
+ end
+ end
+
+ describe "#key_installed?" do
+ before do
+ expect(provider).to receive(:shell_out).with("rpm -qa gpg-pubkey*").and_return(rpm_key_finger)
+ end
+
+ it "returns true if the key is installed" do
+ expect(provider).to receive(:key_fingerprint).and_return("3dbdc284")
+ expect(provider.key_installed?("/foo/nginx.key")).to be_truthy
+ end
+
+ it "returns false if the key is not installed" do
+ expect(provider).to receive(:key_fingerprint).and_return("BOGUS")
+ expect(provider.key_installed?("/foo/nginx.key")).to be_falsey
+ end
+ end
+
+ describe "#key_fingerprint" do
+ it "returns the key's fingerprint" do
+ expect(provider).to receive(:shell_out!).with("gpg --with-fingerprint /foo/nginx.key").and_return(gpg_finger)
+ expect(provider.key_fingerprint("/foo/nginx.key")).to eq("3dbdc284")
+ end
+ end
+
+ describe "#install_gpg_key" do
+ it "skips installing the key if a nil value for key is passed" do
+ expect(logger).to receive(:trace)
+ provider.install_gpg_key(nil)
+ end
+ end
+end
diff --git a/spec/unit/provider_resolver_spec.rb b/spec/unit/provider_resolver_spec.rb
index 5d4396ea4d..66b53b2f4a 100644
--- a/spec/unit/provider_resolver_spec.rb
+++ b/spec/unit/provider_resolver_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright 2014-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -135,6 +135,7 @@ describe Chef::ProviderResolver do
end
else
it "'#{name}' fails to resolve (since #{name.inspect} is unsupported on #{platform} #{platform_version})", *tags do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
expect(resolved_provider).to be_nil
end
end
@@ -191,34 +192,34 @@ describe Chef::ProviderResolver do
stub_service_providers(:debian, :invokercd, :upstart, :systemd)
end
- it "when only the SysV init script exists, it returns a Service::Debian provider" do
+ it "when both the SysV init and Systemd script exists, it returns a Service::Debian provider" do
stub_service_configs(:initd, :systemd)
expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
end
- it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do
+ it "when SysV, Upstart, and Systemd scripts exist, it returns a Service::Systemd provider" do
stub_service_configs(:initd, :upstart, :systemd)
expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
end
- it "when only the Upstart script exists, it returns a Service::Upstart provider" do
+ it "when both the Upstart and Systemd scripts exists, it returns a Service::Systemd provider" do
stub_service_configs(:upstart, :systemd)
expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
end
- it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do
+ it "when both do not exist, it calls the old style provider resolver and returns a Systemd Provider" do
stub_service_configs(:systemd)
expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
end
- it "when only the SysV init script exists, it returns a Service::Debian provider" do
+ it "when only the SysV init script exists, it returns a Service::Systemd provider" do
stub_service_configs(:initd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Debian)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
end
- it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do
+ it "when both SysV and Upstart scripts exist, it returns a Service::Systemd provider" do
stub_service_configs(:initd, :upstart)
- expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
end
it "when only the Upstart script exists, it returns a Service::Upstart provider" do
@@ -226,7 +227,7 @@ describe Chef::ProviderResolver do
expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
end
- it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do
+ it "when both do not exist, it calls the old style provider resolver and returns a Systemd Provider" do
stub_service_configs
expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
end
@@ -409,7 +410,7 @@ describe Chef::ProviderResolver do
stub_service_providers(:debian, :invokercd, :upstart, :systemd)
stub_service_configs(:initd, :upstart)
mock_shellout_command("/bin/systemctl list-unit-files", exitstatus: 1)
- expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
end
end
@@ -480,7 +481,41 @@ describe Chef::ProviderResolver do
end
end
- on_platform %w{freebsd netbsd}, platform_version: "3.1.4" do
+ on_platform "freebsd", os: "freebsd", platform_version: "10.3" do
+ it "returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do
+ stub_service_providers
+ stub_service_configs(:usr_local_etc_rcd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+ end
+
+ it "returns a Freebsd provider if it finds the /etc/rc.d initscript" do
+ stub_service_providers
+ stub_service_configs(:etc_rcd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+ end
+
+ it "always returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do
+ # should only care about :usr_local_etc_rcd stub in the service configs
+ stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
+ stub_service_configs(:usr_local_etc_rcd, :initd, :upstart, :xinetd, :systemd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+ end
+
+ it "always returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do
+ # should only care about :etc_rcd stub in the service configs
+ stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
+ stub_service_configs(:etc_rcd, :initd, :upstart, :xinetd, :systemd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+ end
+
+ it "always returns a freebsd provider by default?" do
+ stub_service_providers
+ stub_service_configs
+ expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+ end
+ end
+
+ on_platform "netbsd", os: "netbsd", platform_version: "7.0.1" do
it "returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do
stub_service_providers
stub_service_configs(:usr_local_etc_rcd)
@@ -507,7 +542,7 @@ describe Chef::ProviderResolver do
expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
end
- it "foo" do
+ it "always returns a freebsd provider by default?" do
stub_service_providers
stub_service_configs
expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
@@ -517,347 +552,342 @@ describe Chef::ProviderResolver do
end
PROVIDERS =
- {
- bash: [ Chef::Resource::Bash, Chef::Provider::Script ],
- breakpoint: [ Chef::Resource::Breakpoint, Chef::Provider::Breakpoint ],
- chef_gem: [ Chef::Resource::ChefGem, Chef::Provider::Package::Rubygems ],
- cookbook_file: [ Chef::Resource::CookbookFile, Chef::Provider::CookbookFile ],
- csh: [ Chef::Resource::Csh, Chef::Provider::Script ],
- deploy: [ Chef::Resource::Deploy, Chef::Provider::Deploy::Timestamped ],
- deploy_revision: [ Chef::Resource::DeployRevision, Chef::Provider::Deploy::Revision ],
- directory: [ Chef::Resource::Directory, Chef::Provider::Directory ],
- easy_install_package: [ Chef::Resource::EasyInstallPackage, Chef::Provider::Package::EasyInstall ],
- erl_call: [ Chef::Resource::ErlCall, Chef::Provider::ErlCall ],
- execute: [ Chef::Resource::Execute, Chef::Provider::Execute ],
- file: [ Chef::Resource::File, Chef::Provider::File ],
- gem_package: [ Chef::Resource::GemPackage, Chef::Provider::Package::Rubygems ],
- git: [ Chef::Resource::Git, Chef::Provider::Git ],
- group: [ Chef::Resource::Group, Chef::Provider::Group::Gpasswd ],
- homebrew_package: [ Chef::Resource::HomebrewPackage, Chef::Provider::Package::Homebrew ],
- http_request: [ Chef::Resource::HttpRequest, Chef::Provider::HttpRequest ],
- ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
- link: [ Chef::Resource::Link, Chef::Provider::Link ],
- log: [ Chef::Resource::Log, Chef::Provider::Log::ChefLog ],
- macports_package: [ Chef::Resource::MacportsPackage, Chef::Provider::Package::Macports ],
- mdadm: [ Chef::Resource::Mdadm, Chef::Provider::Mdadm ],
- mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Mount ],
- perl: [ Chef::Resource::Perl, Chef::Provider::Script ],
- portage_package: [ Chef::Resource::PortagePackage, Chef::Provider::Package::Portage ],
- python: [ Chef::Resource::Python, Chef::Provider::Script ],
- remote_directory: [ Chef::Resource::RemoteDirectory, Chef::Provider::RemoteDirectory ],
- route: [ Chef::Resource::Route, Chef::Provider::Route ],
- ruby: [ Chef::Resource::Ruby, Chef::Provider::Script ],
- ruby_block: [ Chef::Resource::RubyBlock, Chef::Provider::RubyBlock ],
- script: [ Chef::Resource::Script, Chef::Provider::Script ],
- subversion: [ Chef::Resource::Subversion, Chef::Provider::Subversion ],
- template: [ Chef::Resource::Template, Chef::Provider::Template ],
- timestamped_deploy: [ Chef::Resource::TimestampedDeploy, Chef::Provider::Deploy::Timestamped ],
- user: [ Chef::Resource::User, Chef::Provider::User::Useradd ],
- whyrun_safe_ruby_block: [ Chef::Resource::WhyrunSafeRubyBlock, Chef::Provider::WhyrunSafeRubyBlock ],
-
- # We want to check that these are unsupported:
- apt_package: nil,
- bff_package: nil,
- dpkg_package: nil,
- dsc_script: nil,
- ips_package: nil,
- pacman_package: nil,
- paludis_package: nil,
- rpm_package: nil,
- smartos_package: nil,
- solaris_package: nil,
- yum_package: nil,
- windows_package: nil,
- windows_service: nil,
-
- "linux" => {
- apt_package: [ Chef::Resource::AptPackage, Chef::Provider::Package::Apt ],
- dpkg_package: [ Chef::Resource::DpkgPackage, Chef::Provider::Package::Dpkg ],
- pacman_package: [ Chef::Resource::PacmanPackage, Chef::Provider::Package::Pacman ],
- paludis_package: [ Chef::Resource::PaludisPackage, Chef::Provider::Package::Paludis ],
- rpm_package: [ Chef::Resource::RpmPackage, Chef::Provider::Package::Rpm ],
- yum_package: [ Chef::Resource::YumPackage, Chef::Provider::Package::Yum ],
-
- "debian" => {
- ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Debian ],
- package: [ Chef::Resource::AptPackage, Chef::Provider::Package::Apt ],
- # service: [ Chef::Resource::DebianService, Chef::Provider::Service::Debian ],
-
+ {
+ aix_user: [ Chef::Resource::User::AixUser, Chef::Provider::User::Aix ],
+ apt_package: [ Chef::Resource::AptPackage, Chef::Provider::Package::Apt ],
+ bash: [ Chef::Resource::Bash, Chef::Provider::Script ],
+ bff_package: [ Chef::Resource::BffPackage, Chef::Provider::Package::Bff ],
+ breakpoint: [ Chef::Resource::Breakpoint, Chef::Resource::Breakpoint.action_class ],
+ chef_gem: [ Chef::Resource::ChefGem, Chef::Provider::Package::Rubygems ],
+ cookbook_file: [ Chef::Resource::CookbookFile, Chef::Provider::CookbookFile ],
+ csh: [ Chef::Resource::Csh, Chef::Provider::Script ],
+ directory: [ Chef::Resource::Directory, Chef::Provider::Directory ],
+ dpkg_package: [ Chef::Resource::DpkgPackage, Chef::Provider::Package::Dpkg ],
+ dsc_script: [ Chef::Resource::DscScript, Chef::Provider::DscScript ],
+ dscl_user: [ Chef::Resource::User::DsclUser, Chef::Provider::User::Dscl ],
+ execute: [ Chef::Resource::Execute, Chef::Provider::Execute ],
+ file: [ Chef::Resource::File, Chef::Provider::File ],
+ gem_package: [ Chef::Resource::GemPackage, Chef::Provider::Package::Rubygems ],
+ git: [ Chef::Resource::Git, Chef::Provider::Git ],
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Gpasswd ],
+ homebrew_package: [ Chef::Resource::HomebrewPackage, Chef::Provider::Package::Homebrew ],
+ http_request: [ Chef::Resource::HttpRequest, Chef::Provider::HttpRequest ],
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+ ips_package: [ Chef::Resource::IpsPackage, Chef::Provider::Package::Ips ],
+ link: [ Chef::Resource::Link, Chef::Provider::Link ],
+ linux_user: [ Chef::Resource::User::LinuxUser, Chef::Provider::User::Linux ],
+ log: [ Chef::Resource::Log, Chef::Provider::Log::ChefLog ],
+ macports_package: [ Chef::Resource::MacportsPackage, Chef::Provider::Package::Macports ],
+ mdadm: [ Chef::Resource::Mdadm, Chef::Provider::Mdadm ],
+ mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Mount ],
+ pacman_package: [ Chef::Resource::PacmanPackage, Chef::Provider::Package::Pacman ],
+ paludis_package: [ Chef::Resource::PaludisPackage, Chef::Provider::Package::Paludis ],
+ perl: [ Chef::Resource::Perl, Chef::Provider::Script ],
+ portage_package: [ Chef::Resource::PortagePackage, Chef::Provider::Package::Portage ],
+ pw_user: [ Chef::Resource::User::PwUser, Chef::Provider::User::Pw ],
+ python: [ Chef::Resource::Python, Chef::Provider::Script ],
+ remote_directory: [ Chef::Resource::RemoteDirectory, Chef::Provider::RemoteDirectory ],
+ route: [ Chef::Resource::Route, Chef::Provider::Route ],
+ rpm_package: [ Chef::Resource::Package::RpmPackage, Chef::Provider::Package::Rpm ],
+ ruby_block: [ Chef::Resource::RubyBlock, Chef::Provider::RubyBlock ],
+ ruby: [ Chef::Resource::Ruby, Chef::Provider::Script ],
+ script: [ Chef::Resource::Script, Chef::Provider::Script ],
+ smartos_package: [ Chef::Resource::SmartosPackage, Chef::Provider::Package::SmartOS ],
+ solaris_package: [ Chef::Resource::SolarisPackage, Chef::Provider::Package::Solaris ],
+ solaris_user: [ Chef::Resource::User::SolarisUser, Chef::Provider::User::Solaris ],
+ subversion: [ Chef::Resource::Subversion, Chef::Provider::Subversion ],
+ template: [ Chef::Resource::Template, Chef::Provider::Template ],
+ whyrun_safe_ruby_block: [ Chef::Resource::WhyrunSafeRubyBlock, Chef::Provider::WhyrunSafeRubyBlock ],
+ windows_package: [ Chef::Resource::WindowsPackage, Chef::Provider::Package::Windows ],
+ windows_service: [ Chef::Resource::WindowsService, Chef::Provider::Service::Windows ],
+ windows_user: [ Chef::Resource::User::WindowsUser, Chef::Provider::User::Windows ],
+ yum_package: [ Chef::Resource::YumPackage, Chef::Provider::Package::Yum ],
+
+ "linux" => {
"debian" => {
- "7.0" => {
- },
- "6.0" => {
- ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
- # service: [ Chef::Resource::InsservService, Chef::Provider::Service::Insserv ],
- },
- "5.0" => {
- ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
- },
- },
- "gcel" => {
- "3.1.4" => {
- ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
- },
- },
- "linaro" => {
- "3.1.4" => {
- ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
- },
- },
- "linuxmint" => {
- "3.1.4" => {
- ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
- # service: [ Chef::Resource::UpstartService, Chef::Provider::Service::Upstart ],
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Debian ],
+ package: [ Chef::Resource::AptPackage, Chef::Provider::Package::Apt ],
+ # service: [ Chef::Resource::DebianService, Chef::Provider::Service::Debian ],
+
+ "debian" => {
+ "7.0" => {
+ },
+ "6.0" => {
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+ # service: [ Chef::Resource::InsservService, Chef::Provider::Service::Insserv ],
+ },
+ "5.0" => {
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+ },
+ },
+ "gcel" => {
+ "3.1.4" => {
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+ },
+ },
+ "linaro" => {
+ "3.1.4" => {
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+ },
+ },
+ "linuxmint" => {
+ "3.1.4" => {
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+ # service: [ Chef::Resource::UpstartService, Chef::Provider::Service::Upstart ],
+ },
+ },
+ "raspbian" => {
+ "3.1.4" => {
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+ },
+ },
+ "ubuntu" => {
+ "11.10" => {
+ },
+ "10.04" => {
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+ },
},
},
- "raspbian" => {
- "3.1.4" => {
- ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
- },
- },
- "ubuntu" => {
- "11.10" => {
- },
- "10.04" => {
- ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
- },
- },
- },
-
- "arch" => {
- # TODO should be Chef::Resource::PacmanPackage
- package: [ Chef::Resource::Package, Chef::Provider::Package::Pacman ],
"arch" => {
- "3.1.4" => {
- },
- },
- },
+ # TODO should be Chef::Resource::PacmanPackage
+ package: [ Chef::Resource::Package, Chef::Provider::Package::Pacman ],
- "freebsd" => {
- group: [ Chef::Resource::Group, Chef::Provider::Group::Pw ],
- user: [ Chef::Resource::User, Chef::Provider::User::Pw ],
-
- "freebsd" => {
- "3.1.4" => {
+ "arch" => {
+ "3.1.4" => {
+ },
},
},
- },
- "suse" => {
- group: [ Chef::Resource::Group, Chef::Provider::Group::Gpasswd ],
+
"suse" => {
- "12.0" => {
- },
- %w{11.1 11.2 11.3} => {
- group: [ Chef::Resource::Group, Chef::Provider::Group::Suse ],
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Gpasswd ],
+ "suse" => {
+ "12.0" => {
+ },
+ %w{11.1 11.2 11.3} => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Suse ],
+ },
+ },
+ "opensuse" => {
+ # service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
+ package: [ Chef::Resource::ZypperPackage, Chef::Provider::Package::Zypper ],
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+ "12.3" => {
+ },
+ "12.2" => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Suse ],
+ },
},
},
- "opensuse" => {
- # service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
- package: [ Chef::Resource::ZypperPackage, Chef::Provider::Package::Zypper ],
- group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
- "12.3" => {
- },
- "12.2" => {
- group: [ Chef::Resource::Group, Chef::Provider::Group::Suse ],
+
+ "gentoo" => {
+ # TODO should be Chef::Resource::PortagePackage
+ package: [ Chef::Resource::Package, Chef::Provider::Package::Portage ],
+ portage_package: [ Chef::Resource::PortagePackage, Chef::Provider::Package::Portage ],
+ # service: [ Chef::Resource::GentooService, Chef::Provider::Service::Gentoo ],
+
+ "gentoo" => {
+ "3.1.4" => {
+ },
},
},
- },
-
- "gentoo" => {
- # TODO should be Chef::Resource::PortagePackage
- package: [ Chef::Resource::Package, Chef::Provider::Package::Portage ],
- portage_package: [ Chef::Resource::PortagePackage, Chef::Provider::Package::Portage ],
- # service: [ Chef::Resource::GentooService, Chef::Provider::Service::Gentoo ],
- "gentoo" => {
- "3.1.4" => {
+ "rhel" => {
+ # service: [ Chef::Resource::SystemdService, Chef::Provider::Service::Systemd ],
+ package: [ Chef::Resource::DnfPackage, Chef::Provider::Package::Dnf ],
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Redhat ],
+
+ %w{amazon xcp xenserver ibm_powerkvm cloudlinux parallels} => {
+ "3.1.4" => {
+ package: [ Chef::Resource::YumPackage, Chef::Provider::Package::Yum ],
+ # service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
+ },
+ },
+ %w{redhat centos scientific oracle} => {
+ "7.0" => {
+ package: [ Chef::Resource::YumPackage, Chef::Provider::Package::Yum ],
+ },
+ "6.0" => {
+ package: [ Chef::Resource::YumPackage, Chef::Provider::Package::Yum ],
+ # service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
+ },
+ },
+ "fedora" => {
+ "15.0" => {
+ },
+ "14.0" => {
+ # service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
+ },
},
},
+
},
- "rhel" => {
- # service: [ Chef::Resource::SystemdService, Chef::Provider::Service::Systemd ],
- package: [ Chef::Resource::YumPackage, Chef::Provider::Package::Yum ],
- ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Redhat ],
+ "freebsd" => {
+ "freebsd" => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Pw ],
+ user: [ Chef::Resource::User::PwUser, Chef::Provider::User::Pw ],
- %w{amazon xcp xenserver ibm_powerkvm cloudlinux parallels} => {
- "3.1.4" => {
- # service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
- },
- },
- %w{redhat centos scientific oracle} => {
- "7.0" => {
- },
- "6.0" => {
- # service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
- },
- },
- "fedora" => {
- "15.0" => {
- },
- "14.0" => {
- # service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
+ "freebsd" => {
+ "10.3" => {
+ },
},
},
},
- },
-
- "darwin" => {
- %w{mac_os_x mac_os_x_server} => {
- group: [ Chef::Resource::Group, Chef::Provider::Group::Dscl ],
- package: [ Chef::Resource::HomebrewPackage, Chef::Provider::Package::Homebrew ],
- osx_profile: [ Chef::Resource::OsxProfile, Chef::Provider::OsxProfile],
- user: [ Chef::Resource::User, Chef::Provider::User::Dscl ],
+ "darwin" => {
+ %w{mac_os_x mac_os_x_server} => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Dscl ],
+ package: [ Chef::Resource::HomebrewPackage, Chef::Provider::Package::Homebrew ],
+ osx_profile: [ Chef::Resource::OsxProfile, Chef::Provider::OsxProfile],
+ user: [ Chef::Resource::User::DsclUser, Chef::Provider::User::Dscl ],
- "mac_os_x" => {
- "10.9.2" => {
+ "mac_os_x" => {
+ "10.9.2" => {
+ },
},
},
},
- },
-
- "windows" => {
- batch: [ Chef::Resource::Batch, Chef::Provider::Batch ],
- dsc_script: [ Chef::Resource::DscScript, Chef::Provider::DscScript ],
- env: [ Chef::Resource::Env, Chef::Provider::Env::Windows ],
- group: [ Chef::Resource::Group, Chef::Provider::Group::Windows ],
- mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Windows ],
- package: [ Chef::Resource::WindowsPackage, Chef::Provider::Package::Windows ],
- powershell_script: [ Chef::Resource::PowershellScript, Chef::Provider::PowershellScript ],
- service: [ Chef::Resource::WindowsService, Chef::Provider::Service::Windows ],
- user: [ Chef::Resource::User, Chef::Provider::User::Windows ],
- windows_package: [ Chef::Resource::WindowsPackage, Chef::Provider::Package::Windows ],
- windows_service: [ Chef::Resource::WindowsService, Chef::Provider::Service::Windows ],
"windows" => {
- %w{mswin mingw32 windows} => {
- "10.9.2" => {
+ batch: [ Chef::Resource::Batch, Chef::Provider::Batch ],
+ dsc_script: [ Chef::Resource::DscScript, Chef::Provider::DscScript ],
+ windows_env: [ Chef::Resource::WindowsEnv, Chef::Provider::WindowsEnv ],
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Windows ],
+ mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Windows ],
+ package: [ Chef::Resource::WindowsPackage, Chef::Provider::Package::Windows ],
+ powershell_script: [ Chef::Resource::PowershellScript, Chef::Provider::PowershellScript ],
+ service: [ Chef::Resource::WindowsService, Chef::Provider::Service::Windows ],
+ user: [ Chef::Resource::User::WindowsUser, Chef::Provider::User::Windows ],
+ windows_package: [ Chef::Resource::WindowsPackage, Chef::Provider::Package::Windows ],
+ windows_service: [ Chef::Resource::WindowsService, Chef::Provider::Service::Windows ],
+
+ "windows" => {
+ %w{mswin mingw32 windows} => {
+ "10.9.2" => {
+ },
},
},
},
- },
-
- "aix" => {
- bff_package: [ Chef::Resource::BffPackage, Chef::Provider::Package::Aix ],
- cron: [ Chef::Resource::Cron, Chef::Provider::Cron::Aix ],
- group: [ Chef::Resource::Group, Chef::Provider::Group::Aix ],
- ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Aix ],
- mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Aix ],
- # TODO should be Chef::Resource::BffPackage
- package: [ Chef::Resource::Package, Chef::Provider::Package::Aix ],
- rpm_package: [ Chef::Resource::RpmPackage, Chef::Provider::Package::Rpm ],
- user: [ Chef::Resource::User, Chef::Provider::User::Aix ],
- # service: [ Chef::Resource::AixService, Chef::Provider::Service::Aix ],
"aix" => {
+ cron: [ Chef::Resource::Cron, Chef::Provider::Cron::Aix ],
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Aix ],
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Aix ],
+ mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Aix ],
+ package: [ Chef::Resource::Package, Chef::Provider::Package::Bff ],
+ rpm_package: [ Chef::Resource::RpmPackage, Chef::Provider::Package::Rpm ],
+ user: [ Chef::Resource::User::AixUser, Chef::Provider::User::Aix ],
+ # service: [ Chef::Resource::AixService, Chef::Provider::Service::Aix ],
+
"aix" => {
- "5.6" => {
+ "aix" => {
+ "5.6" => {
+ },
},
},
},
- },
- "hpux" => {
"hpux" => {
"hpux" => {
- "3.1.4" => {
- group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+ "hpux" => {
+ "3.1.4" => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+ },
},
},
},
- },
- "netbsd" => {
"netbsd" => {
"netbsd" => {
- "3.1.4" => {
- group: [ Chef::Resource::Group, Chef::Provider::Group::Groupmod ],
+ "netbsd" => {
+ "3.1.4" => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Groupmod ],
+ },
},
},
},
- },
-
- "openbsd" => {
- group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
- package: [ Chef::Resource::OpenbsdPackage, Chef::Provider::Package::Openbsd ],
"openbsd" => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+ package: [ Chef::Resource::OpenbsdPackage, Chef::Provider::Package::Openbsd ],
+
"openbsd" => {
- "3.1.4" => {
+ "openbsd" => {
+ "3.1.4" => {
+ },
},
},
},
- },
-
- "solaris2" => {
- group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
- ips_package: [ Chef::Resource::IpsPackage, Chef::Provider::Package::Ips ],
- package: [ Chef::Resource::SolarisPackage, Chef::Provider::Package::Solaris ],
- mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Solaris ],
- solaris_package: [ Chef::Resource::SolarisPackage, Chef::Provider::Package::Solaris ],
- "smartos" => {
- smartos_package: [ Chef::Resource::SmartosPackage, Chef::Provider::Package::SmartOS ],
- package: [ Chef::Resource::SmartosPackage, Chef::Provider::Package::SmartOS ],
+ "solaris2" => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+ ips_package: [ Chef::Resource::IpsPackage, Chef::Provider::Package::Ips ],
+ package: [ Chef::Resource::SolarisPackage, Chef::Provider::Package::Solaris ],
+ mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Solaris ],
+ solaris_package: [ Chef::Resource::SolarisPackage, Chef::Provider::Package::Solaris ],
"smartos" => {
- "3.1.4" => {
- },
- },
- },
+ smartos_package: [ Chef::Resource::SmartosPackage, Chef::Provider::Package::SmartOS ],
+ package: [ Chef::Resource::SmartosPackage, Chef::Provider::Package::SmartOS ],
- "solaris2" => {
- "nexentacore" => {
- "3.1.4" => {
- },
- },
- "omnios" => {
- "3.1.4" => {
- user: [ Chef::Resource::User, Chef::Provider::User::Solaris ],
- },
- },
- "openindiana" => {
- "3.1.4" => {
- },
- },
- "opensolaris" => {
- "3.1.4" => {
+ "smartos" => {
+ "3.1.4" => {
+ },
},
},
+
"solaris2" => {
- user: [ Chef::Resource::User, Chef::Provider::User::Solaris ],
- "5.11" => {
- package: [ Chef::Resource::IpsPackage, Chef::Provider::Package::Ips ],
- },
- "5.9" => {
+ "nexentacore" => {
+ "3.1.4" => {
+ },
+ },
+ "omnios" => {
+ "3.1.4" => {
+ user: [ Chef::Resource::User::SolarisUser, Chef::Provider::User::Solaris ],
+ },
+ },
+ "openindiana" => {
+ "3.1.4" => {
+ },
+ },
+ "opensolaris" => {
+ "3.1.4" => {
+ },
+ },
+ "solaris2" => {
+ user: [ Chef::Resource::User::SolarisUser, Chef::Provider::User::Solaris ],
+ "5.11" => {
+ package: [ Chef::Resource::IpsPackage, Chef::Provider::Package::Ips ],
+ },
+ "5.9" => {
+ },
},
},
- },
- },
+ },
- "solaris" => {
"solaris" => {
"solaris" => {
- "3.1.4" => {
+ "solaris" => {
+ "3.1.4" => {
+ },
},
},
},
- },
- "exherbo" => {
"exherbo" => {
"exherbo" => {
- "3.1.4" => {
- # TODO should be Chef::Resource::PaludisPackage
- package: [ Chef::Resource::Package, Chef::Provider::Package::Paludis ],
+ "exherbo" => {
+ "3.1.4" => {
+ # TODO should be Chef::Resource::PaludisPackage
+ package: [ Chef::Resource::Package, Chef::Provider::Package::Paludis ],
+ },
},
},
},
- },
- }
+ }
def self.create_provider_tests(providers, test, expected, filter)
expected = expected.merge(providers.select { |key, value| key.is_a?(Symbol) })
diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb
index 2bc2ae7c88..2565df8960 100644
--- a/spec/unit/provider_spec.rb
+++ b/spec/unit/provider_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -91,8 +91,8 @@ describe Chef::Provider do
expect(@provider.current_resource).to eql(nil)
end
- it "should not support whyrun by default" do
- expect(@provider.send(:whyrun_supported?)).to eql(false)
+ it "should support whyrun by default" do
+ expect(@provider.send(:whyrun_supported?)).to eql(true)
end
it "should do nothing for check_resource_semantics! by default" do
@@ -195,4 +195,11 @@ describe Chef::Provider do
end
end
+ context "when using use_inline_resources" do
+ it "should log a deprecation warning" do
+ pending Chef::VERSION.start_with?("14.1")
+ expect(Chef).to receive(:deprecated).with(:use_inline_resources, kind_of(String))
+ Class.new(described_class) { use_inline_resources }
+ end
+ end
end
diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb
index e5dbd42f70..93d03756a7 100644
--- a/spec/unit/recipe_spec.rb
+++ b/spec/unit/recipe_spec.rb
@@ -3,7 +3,7 @@
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Tim Hinderliter (<tim@chef.io>)
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -74,16 +74,10 @@ describe Chef::Recipe do
expect { recipe.not_home("not_home_resource") }.to raise_error(NameError)
end
- it "should require a name argument" do
- expect {
- recipe.cat
- }.to raise_error(ArgumentError)
- end
-
it "should allow regular errors (not NameErrors) to pass unchanged" do
- expect {
+ expect do
recipe.cat("felix") { raise ArgumentError, "You Suck" }
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should add our zen_master to the collection" do
@@ -147,16 +141,16 @@ describe Chef::Recipe do
Object.send(:remove_const, :TottenhamHotspur)
end
- it "selects the first one alphabetically" do
+ it "selects the last-writer wins" do
Sounders.provides :football, platform: "nbc_sports"
TottenhamHotspur.provides :football, platform: "nbc_sports"
res1 = recipe.football "club world cup"
expect(res1.name).to eql("club world cup")
- expect(res1).to be_a_kind_of(Sounders)
+ expect(res1).to be_a_kind_of(TottenhamHotspur)
end
- it "selects the first one alphabetically even if the declaration order is reversed" do
+ it "selects the last-writer wins even if the declaration order is reversed" do
TottenhamHotspur.provides :football2, platform: "nbc_sports"
Sounders.provides :football2, platform: "nbc_sports"
@@ -193,87 +187,36 @@ describe Chef::Recipe do
end
end
- describe "when cloning resources" do
- def expect_warning
- expect(Chef::Log).to receive(:warn).with(/3694/)
- expect(Chef::Log).to receive(:warn).with(/Previous/)
- expect(Chef::Log).to receive(:warn).with(/Current/)
- end
-
- it "should emit a 3694 warning when attributes change" do
- recipe.zen_master "klopp" do
- something "bvb"
- end
- expect_warning
- recipe.zen_master "klopp" do
- something "vbv"
- end
+ describe "when resource cloning is disabled" do
+ def not_expect_warning
+ expect(Chef::Log).not_to receive(:warn).with(/3694/)
+ expect(Chef::Log).not_to receive(:warn).with(/Previous/)
+ expect(Chef::Log).not_to receive(:warn).with(/Current/)
end
- it "should emit a 3694 warning when attributes change" do
- recipe.zen_master "klopp" do
- something "bvb"
- end
- expect_warning
- recipe.zen_master "klopp" do
- something "bvb"
- peace true
- end
+ before do
+ Chef::Config[:resource_cloning] = false
end
it "should emit a 3694 warning when attributes change" do
recipe.zen_master "klopp" do
something "bvb"
- peace true
end
- expect_warning
+ not_expect_warning
recipe.zen_master "klopp" do
- something "bvb"
+ something "vbv"
end
end
- it "should emit a 3694 warning for non-trivial attributes (unfortunately)" do
+ it "should not copy attributes from a prior resource" do
recipe.zen_master "klopp" do
something "bvb"
end
- expect_warning
- recipe.zen_master "klopp" do
- something "bvb"
- end
- end
-
- it "should not emit a 3694 warning for completely trivial resource cloning" do
- recipe.zen_master "klopp"
- expect(Chef::Log).to_not receive(:warn)
+ not_expect_warning
recipe.zen_master "klopp"
+ expect(run_context.resource_collection.first.something).to eql("bvb")
+ expect(run_context.resource_collection[1].something).to be nil
end
-
- it "should not emit a 3694 warning when attributes do not change and the first action is :nothing" do
- recipe.zen_master "klopp" do
- action :nothing
- end
- expect(Chef::Log).to_not receive(:warn)
- recipe.zen_master "klopp" do
- action :score
- end
- end
-
- it "should not emit a 3694 warning when attributes do not change and the second action is :nothing" do
- recipe.zen_master "klopp" do
- action :score
- end
- expect(Chef::Log).to_not receive(:warn)
- recipe.zen_master "klopp" do
- action :nothing
- end
- end
-
- it "validating resources via build_resource" do
- expect {recipe.build_resource(:remote_file, "klopp") do
- source Chef::DelayedEvaluator.new { "http://chef.io" }
- end}.to_not raise_error
- end
-
end
describe "creating resources via declare_resource" do
@@ -304,13 +247,6 @@ describe Chef::Recipe do
expect(run_context.resource_collection.count).to eql(2)
end
- it "does not insert two resources if create_if_missing is used" do
- zm_resource
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- recipe.declare_resource(:zen_master, "klopp", create_if_missing: true)
- expect(run_context.resource_collection.count).to eql(1)
- end
-
context "injecting a different run_context" do
let(:run_context2) do
events = Chef::EventDispatch::Dispatcher.new
@@ -365,7 +301,7 @@ describe Chef::Recipe do
it "gives a sane error message when using method_missing" do
expect do
recipe.no_such_resource("foo")
- end.to raise_error(NoMethodError, %q{No resource or method named `no_such_resource' for `Chef::Recipe "test"'})
+ end.to raise_error(NoMethodError, /undefined method `no_such_resource' for cookbook: hjk, recipe: test :Chef::Recipe/)
end
it "gives a sane error message when using method_missing 'bare'" do
@@ -374,7 +310,7 @@ describe Chef::Recipe do
# Giving an argument will change this from NameError to NoMethodError
no_such_resource
end
- end.to raise_error(NameError, %q{No resource, method, or local variable named `no_such_resource' for `Chef::Recipe "test"'})
+ end.to raise_error(NameError, /undefined local variable or method `no_such_resource' for cookbook: hjk, recipe: test :Chef::Recipe/)
end
it "gives a sane error message when using build_resource" do
@@ -400,53 +336,6 @@ describe Chef::Recipe do
end
- describe "resource cloning" do
-
- let(:second_recipe) do
- Chef::Recipe.new("second_cb", "second_recipe", run_context)
- end
-
- let(:original_resource) do
- recipe.zen_master("klopp") do
- something "bvb09"
- action :score
- end
- end
-
- let(:duplicated_resource) do
- original_resource
- second_recipe.zen_master("klopp") do
- # attrs should be cloned
- end
- end
-
- it "copies attributes from the first resource" do
- expect(duplicated_resource.something).to eq("bvb09")
- end
-
- it "does not copy the action from the first resource" do
- expect(original_resource.action).to eq([:score])
- expect(duplicated_resource.action).to eq([:nothing])
- end
-
- it "does not copy the source location of the first resource" do
- # sanity check source location:
- expect(original_resource.source_line).to include(__FILE__)
- expect(duplicated_resource.source_line).to include(__FILE__)
- # actual test:
- expect(original_resource.source_line).not_to eq(duplicated_resource.source_line)
- end
-
- it "sets the cookbook name on the cloned resource to that resource's cookbook" do
- expect(duplicated_resource.cookbook_name).to eq("second_cb")
- end
-
- it "sets the recipe name on the cloned resource to that resoure's recipe" do
- expect(duplicated_resource.recipe_name).to eq("second_recipe")
- end
-
- end
-
describe "resource definitions" do
it "should execute defined resources" do
crow_define = Chef::ResourceDefinition.new
diff --git a/spec/unit/resource/apt_package_spec.rb b/spec/unit/resource/apt_package_spec.rb
index 78eccfb444..3b399059a6 100644
--- a/spec/unit/resource/apt_package_spec.rb
+++ b/spec/unit/resource/apt_package_spec.rb
@@ -31,8 +31,12 @@ describe Chef::Resource::AptPackage, "initialize" do
let(:resource) { Chef::Resource::AptPackage.new("foo") }
- it "should support default_release" do
+ it "supports default_release" do
resource.default_release("lenny-backports")
expect(resource.default_release).to eql("lenny-backports")
end
+
+ it "should preserve configuration files by default" do
+ expect(resource.overwrite_config_files).to eql(false)
+ end
end
diff --git a/spec/unit/resource/apt_preference_spec.rb b/spec/unit/resource/apt_preference_spec.rb
new file mode 100644
index 0000000000..1d3be99f44
--- /dev/null
+++ b/spec/unit/resource/apt_preference_spec.rb
@@ -0,0 +1,44 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: 2016-2017, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::AptPreference do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::AptPreference.new("libmysqlclient16", run_context) }
+
+ it "resolves to a Noop class when on non-linux OS" do
+ node.automatic[:os] = "windows"
+ node.automatic[:platform_family] = "windows"
+ expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
+ end
+
+ it "resolves to a Noop class when on non-debian linux" do
+ node.automatic[:os] = "linux"
+ node.automatic[:platform_family] = "gentoo"
+ expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
+ end
+
+ it "resolves to a AptUpdate class when on a debian platform_family" do
+ node.automatic[:os] = "linux"
+ node.automatic[:platform_family] = "debian"
+ expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::AptPreference)
+ end
+end
diff --git a/spec/unit/resource/apt_repository_spec.rb b/spec/unit/resource/apt_repository_spec.rb
index cd55ce66cc..bbc80a95c1 100644
--- a/spec/unit/resource/apt_repository_spec.rb
+++ b/spec/unit/resource/apt_repository_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: 2016-2017, Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,20 +19,65 @@
require "spec_helper"
describe Chef::Resource::AptRepository do
+ let(:node) { Chef::Node.new }
+ let(:run_context) do
+ node.automatic[:lsb][:codename] = "superduper"
+ empty_events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, empty_events)
+ end
+ let(:resource) { Chef::Resource::AptRepository.new("multiverse", run_context) }
- let(:resource) { Chef::Resource::AptRepository.new("multiverse") }
+ it "keyserver defaults to keyserver.ubuntu.com" do
+ expect(resource.keyserver).to eql("keyserver.ubuntu.com")
+ end
- it "should create a new Chef::Resource::AptUpdate" do
- expect(resource).to be_a_kind_of(Chef::Resource)
- expect(resource).to be_a_kind_of(Chef::Resource::AptRepository)
+ it "repo_name is the name property" do
+ expect(resource.repo_name).to eql("multiverse")
end
- it "the default keyserver should be keyserver.ubuntu.com" do
- expect(resource.keyserver).to eql("keyserver.ubuntu.com")
+ it "distribution defaults to the distro codename" do
+ expect(resource.distribution).to eql("superduper")
+ end
+
+ it "allows setting key to an Array of keys and does not coerce it" do
+ resource.key = %w{key1 key2}
+ expect(resource.key).to eql(%w{key1 key2})
+ end
+
+ it "allows setting key to nil and does not coerce it" do
+ resource.key = nil
+ expect(resource.key).to be_nil
+ end
+
+ it "allows setting key to false and does not coerce it" do
+ resource.key = false
+ expect(resource.key).to be false
+ end
+
+ it "allows setting key to a String and coerces it to an Array" do
+ resource.key = "key1"
+ expect(resource.key).to eql(["key1"])
+ end
+
+ it "fails if the user provides a repo_name with a forward slash" do
+ expect { resource.repo_name "foo/bar" }.to raise_error(ArgumentError)
+ end
+
+ it "resolves to a Noop class when on non-linux OS" do
+ node.automatic[:os] = "windows"
+ node.automatic[:platform_family] = "windows"
+ expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
+ end
+
+ it "resolves to a Noop class when on non-debian linux" do
+ node.automatic[:os] = "linux"
+ node.automatic[:platform_family] = "gentoo"
+ expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
end
- it "the default distribution should be nillable" do
- expect(resource.distribution(nil)).to eql(nil)
- expect(resource.distribution).to eql(nil)
+ it "resolves to a AptUpdate class when on a debian platform_family" do
+ node.automatic[:os] = "linux"
+ node.automatic[:platform_family] = "debian"
+ expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::AptRepository)
end
end
diff --git a/spec/unit/resource/apt_update_spec.rb b/spec/unit/resource/apt_update_spec.rb
index 8015cb03b3..c872061140 100644
--- a/spec/unit/resource/apt_update_spec.rb
+++ b/spec/unit/resource/apt_update_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: 2016-2017, Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,20 +19,35 @@
require "spec_helper"
describe Chef::Resource::AptUpdate do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::AptUpdate.new("update", run_context) }
- let(:resource) { Chef::Resource::AptUpdate.new("update") }
-
- it "should create a new Chef::Resource::AptUpdate" do
- expect(resource).to be_a_kind_of(Chef::Resource)
- expect(resource).to be_a_kind_of(Chef::Resource::AptUpdate)
- end
-
- it "the default frequency should be 1 day" do
+ it "default frequency is set to be 1 day" do
expect(resource.frequency).to eql(86_400)
end
- it "the frequency should accept integers" do
+ it "frequency accepts integers" do
resource.frequency(400)
expect(resource.frequency).to eql(400)
end
+
+ it "resolves to a Noop class when on non-linux OS" do
+ node.automatic[:os] = "windows"
+ node.automatic[:platform_family] = "windows"
+ expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
+ end
+
+ it "resolves to a Noop class when on non-debian linux" do
+ node.automatic[:os] = "linux"
+ node.automatic[:platform_family] = "gentoo"
+ expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
+ end
+
+ it "resolves to a AptUpdate class when on a debian platform_family" do
+ node.automatic[:os] = "linux"
+ node.automatic[:platform_family] = "debian"
+ expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::AptUpdate)
+ end
end
diff --git a/spec/unit/resource/bash_spec.rb b/spec/unit/resource/bash_spec.rb
index 56c36df1ce..10482002db 100644
--- a/spec/unit/resource/bash_spec.rb
+++ b/spec/unit/resource/bash_spec.rb
@@ -20,21 +20,18 @@ require "spec_helper"
describe Chef::Resource::Bash do
- before(:each) do
- @resource = Chef::Resource::Bash.new("fakey_fakerton")
- end
+ let(:resource) { Chef::Resource::Bash.new("fakey_fakerton") }
- it "should create a new Chef::Resource::Bash" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Bash)
+ it "is a subclass of Chef::Resource::Script" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Script)
end
- it "should have a resource name of :bash" do
- expect(@resource.resource_name).to eql(:bash)
+ it "has a resource name of :bash" do
+ expect(resource.resource_name).to eql(:bash)
end
- it "should have an interpreter of bash" do
- expect(@resource.interpreter).to eql("bash")
+ it "has an interpreter of bash" do
+ expect(resource.interpreter).to eql("bash")
end
end
diff --git a/spec/unit/resource/batch_spec.rb b/spec/unit/resource/batch_spec.rb
index e19ea15585..c161428a13 100644
--- a/spec/unit/resource/batch_spec.rb
+++ b/spec/unit/resource/batch_spec.rb
@@ -19,21 +19,18 @@
require "spec_helper"
describe Chef::Resource::Batch do
+ let(:node) { Chef::Node.new }
before(:each) do
- node = Chef::Node.new
-
node.default["kernel"] = Hash.new
node.default["kernel"][:machine] = :x86_64.to_s
node.automatic[:os] = "windows"
run_context = Chef::RunContext.new(node, nil, nil)
-
@resource = Chef::Resource::Batch.new("batch_unit_test", run_context)
-
end
- it "should create a new Chef::Resource::Batch" do
+ it "creates a new Chef::Resource::Batch" do
expect(@resource).to be_a_kind_of(Chef::Resource::Batch)
end
diff --git a/spec/unit/resource/breakpoint_spec.rb b/spec/unit/resource/breakpoint_spec.rb
index a5b27bae16..ce0df676dd 100644
--- a/spec/unit/resource/breakpoint_spec.rb
+++ b/spec/unit/resource/breakpoint_spec.rb
@@ -17,31 +17,48 @@
#
require "spec_helper"
-require "support/shared/unit/resource/static_provider_resolution"
describe Chef::Resource::Breakpoint do
- static_provider_resolution(
- resource: Chef::Resource::Breakpoint,
- provider: Chef::Provider::Breakpoint,
- name: :breakpoint,
- action: :break
- )
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:collection) { double("resource collection") }
+ let(:resource) { Chef::Resource::Breakpoint.new("name", run_context) }
+ let(:provider) { resource.provider_for_action(:break) }
before do
- @breakpoint = Chef::Resource::Breakpoint.new
+ allow(run_context).to receive(:resource_collection).and_return(collection)
+ end
+
+ it "gets the iterator from @collection and pauses it" do
+ allow(Shell).to receive(:running?).and_return(true)
+ iterator = double("stepable_iterator")
+ allow(collection).to receive(:iterator).and_return(iterator)
+ expect(iterator).to receive(:pause)
+ provider.action_break
+ expect(resource).to be_updated
+ end
+
+ it "doesn't pause the iterator if chef-shell isn't running" do
+ allow(Shell).to receive(:running?).and_return(false)
+ iterator = double("stepable_iterator")
+ allow(collection).to receive(:iterator).and_return(iterator)
+ expect(iterator).not_to receive(:pause)
+ provider.action_break
end
it "allows the action :break" do
- expect(@breakpoint.allowed_actions).to include(:break)
+ expect(resource.allowed_actions).to include(:break)
end
it "defaults to the break action" do
- expect(@breakpoint.action).to eq([:break])
+ expect(resource.action).to eq([:break])
end
it "names itself after the line number of the file where it's created" do
- expect(@breakpoint.name).to match(/breakpoint_spec\.rb\:[\d]{2}\:in \`new\'$/)
+ resource = Chef::Resource::Breakpoint.new
+ expect(resource.name).to match(/breakpoint_spec\.rb\:[\d]{2}\:in \`new\'$/)
end
end
diff --git a/spec/unit/resource/deploy_revision_spec.rb b/spec/unit/resource/build_essential_spec.rb
index aa12b9595d..5c25d3e961 100644
--- a/spec/unit/resource/deploy_revision_spec.rb
+++ b/spec/unit/resource/build_essential_spec.rb
@@ -1,6 +1,5 @@
#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2009-2016, Daniel DeLeo
+# Copyright:: Copyright 2018, Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,26 +16,24 @@
#
require "spec_helper"
-require "support/shared/unit/resource/static_provider_resolution"
-describe Chef::Resource::DeployRevision do
+describe Chef::Resource::BuildEssential do
- static_provider_resolution(
- resource: Chef::Resource::DeployRevision,
- provider: Chef::Provider::Deploy::Revision,
- name: :deploy_revision,
- action: :deploy
- )
+ let(:resource) { Chef::Resource::BuildEssential.new("foo") }
-end
+ it "has a resource name of :build_essential" do
+ expect(resource.resource_name).to eql(:build_essential)
+ end
-describe Chef::Resource::DeployBranch do
+ it "has a default action of install" do
+ expect(resource.action).to eql([:install])
+ end
- static_provider_resolution(
- resource: Chef::Resource::DeployBranch,
- provider: Chef::Provider::Deploy::Revision,
- name: :deploy_branch,
- action: :deploy
- )
+ context "when not settting a resource name" do
+ let(:resource) { Chef::Resource::BuildEssential.new(nil) }
+ it "the name defaults to an empty string" do
+ expect(resource.name).to eql("")
+ end
+ end
end
diff --git a/spec/unit/resource/cab_package_spec.rb b/spec/unit/resource/cab_package_spec.rb
new file mode 100644
index 0000000000..87de446dd3
--- /dev/null
+++ b/spec/unit/resource/cab_package_spec.rb
@@ -0,0 +1,54 @@
+#
+# Author:: Vasundhara Jagdale (<vasundhara.jagdale@msystechnologies.com>)
+# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::CabPackage do
+
+ let(:resource) { Chef::Resource::CabPackage.new("test_pkg") }
+
+ it "is a subclass of Chef::Resource::Package" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Package)
+ end
+
+ it "sets resource name as :cab_package" do
+ expect(resource.resource_name).to eql(:cab_package)
+ end
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "coerces name property to package_name property" do
+ expect(resource.package_name).to eql("test_pkg")
+ end
+
+ it "coerces name property to a source property if source not provided" do
+ expect(resource.source).to end_with("test_pkg")
+ end
+
+ it "coerces name property to a source property if source not provided and package_name is" do
+ resource.package_name("package.cab")
+ expect(resource.source).to end_with("package.cab")
+ end
+
+ it "coerces source property if it does not looks like a path" do
+ resource.source("package.cab")
+ expect(resource.source).not_to eq("package.cab")
+ end
+end
diff --git a/spec/unit/resource/chef_gem_spec.rb b/spec/unit/resource/chef_gem_spec.rb
index c98b447582..0eac98c7d2 100644
--- a/spec/unit/resource/chef_gem_spec.rb
+++ b/spec/unit/resource/chef_gem_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,16 +34,16 @@ end
describe Chef::Resource::ChefGem, "gem_binary" do
let(:resource) { Chef::Resource::ChefGem.new("foo") }
- it "should raise an exception when gem_binary is set" do
+ it "raises an exception when gem_binary is set" do
expect { resource.gem_binary("/lol/cats/gem") }.to raise_error(ArgumentError)
end
- it "should set the gem_binary based on computing it from RbConfig" do
+ it "sets the gem_binary based on computing it from RbConfig" do
expect(resource.gem_binary).to eql("#{RbConfig::CONFIG['bindir']}/gem")
end
- it "should set the gem_binary based on computing it from RbConfig" do
- expect(resource.compile_time).to be nil
+ it "sets compile_time to false by default" do
+ expect(resource.compile_time).to be false
end
context "when building the resource" do
@@ -70,22 +70,13 @@ describe Chef::Resource::ChefGem, "gem_binary" do
expect(Chef::Resource::ChefGem).to receive(:new).and_return(resource)
end
- it "runs the install at compile-time by default", chef: "< 13" do
- expect(resource).to receive(:run_action).with(:install)
- expect(Chef::Log).to receive(:deprecation).at_least(:once)
- recipe.chef_gem "foo"
- end
-
- # the default behavior will change in Chef-13
- it "does not runs the install at compile-time by default", chef: ">= 13" do
+ it "does not runs the install at compile-time by default" do
expect(resource).not_to receive(:run_action).with(:install)
- expect(Chef::Log).not_to receive(:deprecation)
recipe.chef_gem "foo"
end
it "compile_time true installs at compile-time" do
expect(resource).to receive(:run_action).with(:install)
- expect(Chef::Log).not_to receive(:deprecation)
recipe.chef_gem "foo" do
compile_time true
end
@@ -93,64 +84,27 @@ describe Chef::Resource::ChefGem, "gem_binary" do
it "compile_time false does not install at compile-time" do
expect(resource).not_to receive(:run_action).with(:install)
- expect(Chef::Log).not_to receive(:deprecation)
recipe.chef_gem "foo" do
compile_time false
end
end
- describe "when Chef::Config[:chef_gem_compile_time] is explicitly true" do
- let(:chef_gem_compile_time) { true }
-
- before do
- expect(Chef::Log).not_to receive(:deprecation)
- end
-
- it "by default installs at compile-time" do
- expect(resource).to receive(:run_action).with(:install)
- recipe.chef_gem "foo"
- end
-
- it "compile_time true installs at compile-time" do
- expect(resource).to receive(:run_action).with(:install)
- recipe.chef_gem "foo" do
- compile_time true
- end
- end
-
- it "compile_time false does not install at compile-time" do
- expect(resource).not_to receive(:run_action).with(:install)
- recipe.chef_gem "foo" do
- compile_time false
- end
- end
+ it "by default does not install at compile-time" do
+ expect(resource).not_to receive(:run_action).with(:install)
+ recipe.chef_gem "foo"
end
- describe "when Chef::Config[:chef_gem_compile_time] is explicitly false" do
-
- let(:chef_gem_compile_time) { false }
-
- before do
- expect(Chef::Log).not_to receive(:deprecation)
- end
-
- it "by default does not install at compile-time" do
- expect(resource).not_to receive(:run_action).with(:install)
- recipe.chef_gem "foo"
- end
-
- it "compile_time true installs at compile-time" do
- expect(resource).to receive(:run_action).with(:install)
- recipe.chef_gem "foo" do
- compile_time true
- end
+ it "compile_time true installs at compile-time" do
+ expect(resource).to receive(:run_action).with(:install)
+ recipe.chef_gem "foo" do
+ compile_time true
end
+ end
- it "compile_time false does not install at compile-time" do
- expect(resource).not_to receive(:run_action).with(:install)
- recipe.chef_gem "foo" do
- compile_time false
- end
+ it "compile_time false does not install at compile-time" do
+ expect(resource).not_to receive(:run_action).with(:install)
+ recipe.chef_gem "foo" do
+ compile_time false
end
end
end
diff --git a/lib/chef/mixin/language_include_attribute.rb b/spec/unit/resource/chef_handler_spec.rb
index 7cb66dc272..7cba0c7fe3 100644
--- a/lib/chef/mixin/language_include_attribute.rb
+++ b/spec/unit/resource/chef_handler_spec.rb
@@ -1,6 +1,5 @@
#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2018, Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,19 +15,21 @@
# limitations under the License.
#
-require "chef/dsl/include_attribute"
-require "chef/mixin/deprecation"
+require "spec_helper"
-class Chef
- module Mixin
+describe Chef::Resource::ChefHandler do
- # DEPRECATED: This is just here for compatibility, use
- # Chef::DSL::IncludeAttribute instead.
+ let(:resource) { Chef::Resource::ChefHandler.new("foo") }
- deprecate_constant(:LanguageIncludeAttribute, Chef::DSL::IncludeAttribute, <<-EOM)
-Chef::Mixin::LanguageIncludeAttribute is deprecated. Use
-Chef::DSL::IncludeAttribute instead.
-EOM
+ it "has a resource name of :chef_handler" do
+ expect(resource.resource_name).to eql(:chef_handler)
+ end
+
+ it "has a default action of enable" do
+ expect(resource.action).to eql([:enable])
+ end
+ it "the class_name property is the name property" do
+ expect(resource.class_name).to eql("foo")
end
end
diff --git a/spec/unit/resource/chocolatey_package_spec.rb b/spec/unit/resource/chocolatey_package_spec.rb
index e43803a65f..452373b571 100644
--- a/spec/unit/resource/chocolatey_package_spec.rb
+++ b/spec/unit/resource/chocolatey_package_spec.rb
@@ -22,46 +22,56 @@ describe Chef::Resource::ChocolateyPackage do
let(:resource) { Chef::Resource::ChocolateyPackage.new("fakey_fakerton") }
- it "should create a new Chef::Resource::ChocolateyPackage" do
- expect(resource).to be_a_kind_of(Chef::Resource)
+ it "is a subclass of Chef::Resource::Package" do
expect(resource).to be_a_kind_of(Chef::Resource::Package)
- expect(resource).to be_a_instance_of(Chef::Resource::ChocolateyPackage)
end
- it "should have a resource name of :python" do
+ it "has a resource name of :python" do
expect(resource.resource_name).to eql(:chocolatey_package)
end
- it "should coerce its name to a package_name array" do
+ it "coerces its name to a package_name array" do
expect(resource.package_name).to eql(["fakey_fakerton"])
end
- it "the package_name setter should coerce to arrays" do
+ it "the package_name setter coerces to arrays" do
resource.package_name("git")
expect(resource.package_name).to eql(["git"])
end
- it "the package_name setter should accept arrays" do
+ it "the package_name setter accepts arrays" do
resource.package_name(%w{git unzip})
expect(resource.package_name).to eql(%w{git unzip})
end
- it "the name should accept arrays" do
+ it "the name accepts arrays" do
resource = Chef::Resource::ChocolateyPackage.new(%w{git unzip})
expect(resource.package_name).to eql(%w{git unzip})
end
- it "the default version should be nil" do
+ it "the default version is nil" do
expect(resource.version).to eql(nil)
end
- it "the version setter should coerce to arrays" do
+ it "the version setter coerces to arrays" do
resource.version("1.2.3")
expect(resource.version).to eql(["1.2.3"])
end
- it "the version setter should accept arrays" do
+ it "the version setter accepts arrays" do
resource.version(["1.2.3", "4.5.6"])
expect(resource.version).to eql(["1.2.3", "4.5.6"])
end
+
+ it "the default returns is 0" do
+ expect(resource.returns).to eql([0])
+ end
+
+ # Integer, Array
+ [ 0, [0, 48, 49] ].each do |val|
+ it "supports setting an alternate return value as a #{val.class}" do
+ resource.returns(val)
+ expect(resource.returns).to eql(val)
+ end
+ end
end
diff --git a/spec/unit/resource/conditional_spec.rb b/spec/unit/resource/conditional_spec.rb
index b34b4200e6..9a5f0a59bb 100644
--- a/spec/unit/resource/conditional_spec.rb
+++ b/spec/unit/resource/conditional_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright 2011-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,7 @@ describe Chef::Resource::Conditional do
allow_any_instance_of(Mixlib::ShellOut).to receive(:run_command).and_return(nil)
@status = OpenStruct.new(:success? => true)
allow_any_instance_of(Mixlib::ShellOut).to receive(:status).and_return(@status)
- @parent_resource = Chef::Resource.new(nil, Chef::Node.new)
+ @parent_resource = Chef::Resource.new("", Chef::Node.new)
end
it "raises an exception when neither a block or command is given" do
@@ -92,14 +92,14 @@ describe Chef::Resource::Conditional do
describe "after running a command which timed out" do
before do
@conditional = Chef::Resource::Conditional.only_if(@parent_resource, "false")
- allow_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).to receive(:shell_out).and_raise(Chef::Exceptions::CommandTimeout)
+ allow_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).to receive(:shell_out_with_systems_locale).and_raise(Chef::Exceptions::CommandTimeout)
end
it "indicates that resource convergence should not continue" do
expect(@conditional.continue?).to be_falsey
end
- it "should log a warning" do
+ it "logs a warning" do
expect(Chef::Log).to receive(:warn).with("Command 'false' timed out")
@conditional.continue?
end
@@ -124,6 +124,29 @@ describe Chef::Resource::Conditional do
expect(@conditional.continue?).to be_falsey
end
end
+
+ describe "after running a block that returns a string value" do
+ before do
+ @conditional = Chef::Resource::Conditional.only_if(@parent_resource) { "some command" }
+ end
+
+ it "logs a warning" do
+ expect(Chef::Log).to receive(:warn).with("only_if block for [] returned \"some command\", did you mean to run a command? If so use 'only_if \"some command\"' in your code.")
+ @conditional.evaluate
+ end
+ end
+
+ describe "after running a block that returns a string value on a sensitive resource" do
+ before do
+ @parent_resource.sensitive(true)
+ @conditional = Chef::Resource::Conditional.only_if(@parent_resource) { "some command" }
+ end
+
+ it "logs a warning" do
+ expect(Chef::Log).to receive(:warn).with("only_if block for [] returned a string, did you mean to run a command?")
+ @conditional.evaluate
+ end
+ end
end
describe "when created as a `not_if`" do
@@ -172,14 +195,14 @@ describe Chef::Resource::Conditional do
describe "after running a command which timed out" do
before do
@conditional = Chef::Resource::Conditional.not_if(@parent_resource, "false")
- allow_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).to receive(:shell_out).and_raise(Chef::Exceptions::CommandTimeout)
+ allow_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).to receive(:shell_out_with_systems_locale).and_raise(Chef::Exceptions::CommandTimeout)
end
it "indicates that resource convergence should continue" do
expect(@conditional.continue?).to be_truthy
end
- it "should log a warning" do
+ it "logs a warning" do
expect(Chef::Log).to receive(:warn).with("Command 'false' timed out")
@conditional.continue?
end
@@ -204,5 +227,28 @@ describe Chef::Resource::Conditional do
expect(@conditional.continue?).to be_truthy
end
end
+
+ describe "after running a block that returns a string value" do
+ before do
+ @conditional = Chef::Resource::Conditional.not_if(@parent_resource) { "some command" }
+ end
+
+ it "logs a warning" do
+ expect(Chef::Log).to receive(:warn).with("not_if block for [] returned \"some command\", did you mean to run a command? If so use 'not_if \"some command\"' in your code.")
+ @conditional.evaluate
+ end
+ end
+
+ describe "after running a block that returns a string value on a sensitive resource" do
+ before do
+ @parent_resource.sensitive(true)
+ @conditional = Chef::Resource::Conditional.not_if(@parent_resource) { "some command" }
+ end
+
+ it "logs a warning" do
+ expect(Chef::Log).to receive(:warn).with("not_if block for [] returned a string, did you mean to run a command?")
+ @conditional.evaluate
+ end
+ end
end
end
diff --git a/spec/unit/resource/cookbook_file_spec.rb b/spec/unit/resource/cookbook_file_spec.rb
index 6886ce1f31..c6fdab3a1f 100644
--- a/spec/unit/resource/cookbook_file_spec.rb
+++ b/spec/unit/resource/cookbook_file_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software, Inc.
+# Copyright:: Copyright 2010-2017, Chef Software Inc.
#p License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,51 +20,45 @@
require "spec_helper"
describe Chef::Resource::CookbookFile do
- before do
- @cookbook_file = Chef::Resource::CookbookFile.new("sourcecode_tarball.tgz")
- end
-
- it "uses the name parameter for the source parameter" do
- expect(@cookbook_file.name).to eq("sourcecode_tarball.tgz")
- end
+ let(:resource) { Chef::Resource::CookbookFile.new("/foo/bar/sourcecode_tarball.tgz") }
- it "has a source parameter" do
- @cookbook_file.name("config_file.conf")
- expect(@cookbook_file.name).to eq("config_file.conf")
+ it "uses the basepath of the resourc ename for the source property" do
+ expect(resource.source).to eq("sourcecode_tarball.tgz")
end
- it "defaults to a nil cookbook parameter (current cookbook will be used)" do
- expect(@cookbook_file.cookbook).to be_nil
+ it "source property accepts Strings" do
+ resource.name("config_file.conf")
+ expect(resource.source).to eq("config_file.conf")
end
- it "has a cookbook parameter" do
- @cookbook_file.cookbook("munin")
- expect(@cookbook_file.cookbook).to eq("munin")
+ it "cookbook property defaults to nil (current cookbook will be used)" do
+ expect(resource.cookbook).to be_nil
end
- it "sets the provider to Chef::Provider::CookbookFile" do
- expect(@cookbook_file.provider).to eq(Chef::Provider::CookbookFile)
+ it "has a cookbook property that accepts Strings" do
+ resource.cookbook("munin")
+ expect(resource.cookbook).to eq("munin")
end
describe "when it has a backup number, group, mode, owner, source, checksum, and cookbook on nix or path, rights, deny_rights, checksum on windows" do
before do
if Chef::Platform.windows?
- @cookbook_file.path("C:/temp/origin/file.txt")
- @cookbook_file.rights(:read, "Everyone")
- @cookbook_file.deny_rights(:full_control, "Clumsy_Sam")
+ resource.path("C:/temp/origin/file.txt")
+ resource.rights(:read, "Everyone")
+ resource.deny_rights(:full_control, "Clumsy_Sam")
else
- @cookbook_file.path("/tmp/origin/file.txt")
- @cookbook_file.group("wheel")
- @cookbook_file.mode("0664")
- @cookbook_file.owner("root")
- @cookbook_file.source("/tmp/foo.txt")
- @cookbook_file.cookbook("/tmp/cookbooks/cooked.rb")
+ resource.path("/tmp/origin/file.txt")
+ resource.group("wheel")
+ resource.mode("0664")
+ resource.owner("root")
+ resource.source("/tmp/foo.txt")
+ resource.cookbook("/tmp/cookbooks/cooked.rb")
end
- @cookbook_file.checksum("1" * 64)
+ resource.checksum("1" * 64)
end
it "describes the state" do
- state = @cookbook_file.state
+ state = resource.state_for_resource_reporter
if Chef::Platform.windows?
puts state
expect(state[:rights]).to eq([{ :permissions => :read, :principals => "Everyone" }])
@@ -79,9 +73,9 @@ describe Chef::Resource::CookbookFile do
it "returns the path as its identity" do
if Chef::Platform.windows?
- expect(@cookbook_file.identity).to eq("C:/temp/origin/file.txt")
+ expect(resource.identity).to eq("C:/temp/origin/file.txt")
else
- expect(@cookbook_file.identity).to eq("/tmp/origin/file.txt")
+ expect(resource.identity).to eq("/tmp/origin/file.txt")
end
end
end
diff --git a/spec/unit/resource/cron_spec.rb b/spec/unit/resource/cron_spec.rb
index 6e867b75e1..91e0b05908 100644
--- a/spec/unit/resource/cron_spec.rb
+++ b/spec/unit/resource/cron_spec.rb
@@ -20,157 +20,149 @@
require "spec_helper"
describe Chef::Resource::Cron do
+ let(:resource) { Chef::Resource::Cron.new("cronify") }
- before(:each) do
- @resource = Chef::Resource::Cron.new("cronify")
+ it "has a name property" do
+ expect(resource.name).to eql("cronify")
end
- it "should create a new Chef::Resource::Cron" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Cron)
+ it "has a default action of [:create]" do
+ expect(resource.action).to eql([:create])
end
- it "should have a name" do
- expect(@resource.name).to eql("cronify")
+ it "accepts create or delete for action" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :lolcat }.to raise_error(ArgumentError)
end
- it "should have a default action of 'create'" do
- expect(@resource.action).to eql([:create])
+ it "allows you to set a command" do
+ resource.command "/bin/true"
+ expect(resource.command).to eql("/bin/true")
end
- it "should accept create or delete for action" do
- expect { @resource.action :create }.not_to raise_error
- expect { @resource.action :delete }.not_to raise_error
- expect { @resource.action :lolcat }.to raise_error(ArgumentError)
+ it "allows you to set a user" do
+ resource.user "daemon"
+ expect(resource.user).to eql("daemon")
end
- it "should allow you to set a command" do
- @resource.command "/bin/true"
- expect(@resource.command).to eql("/bin/true")
+ it "allows you to specify the minute" do
+ resource.minute "30"
+ expect(resource.minute).to eql("30")
end
- it "should allow you to set a user" do
- @resource.user "daemon"
- expect(@resource.user).to eql("daemon")
+ it "allows you to specify the hour" do
+ resource.hour "6"
+ expect(resource.hour).to eql("6")
end
- it "should allow you to specify the minute" do
- @resource.minute "30"
- expect(@resource.minute).to eql("30")
+ it "allows you to specify the day" do
+ resource.day "10"
+ expect(resource.day).to eql("10")
end
- it "should allow you to specify the hour" do
- @resource.hour "6"
- expect(@resource.hour).to eql("6")
+ it "allows you to specify the month" do
+ resource.month "10"
+ expect(resource.month).to eql("10")
end
- it "should allow you to specify the day" do
- @resource.day "10"
- expect(@resource.day).to eql("10")
+ it "allows you to specify the weekday" do
+ resource.weekday "2"
+ expect(resource.weekday).to eql("2")
end
- it "should allow you to specify the month" do
- @resource.month "10"
- expect(@resource.month).to eql("10")
+ it "allows you to specify the mailto variable" do
+ resource.mailto "test@example.com"
+ expect(resource.mailto).to eql("test@example.com")
end
- it "should allow you to specify the weekday" do
- @resource.weekday "2"
- expect(@resource.weekday).to eql("2")
+ it "allows you to specify the path" do
+ resource.path "/usr/bin:/usr/sbin"
+ expect(resource.path).to eql("/usr/bin:/usr/sbin")
end
- it "should allow you to specify the mailto variable" do
- @resource.mailto "test@example.com"
- expect(@resource.mailto).to eql("test@example.com")
+ it "allows you to specify the home directory" do
+ resource.home "/root"
+ expect(resource.home).to eql("/root")
end
- it "should allow you to specify the path" do
- @resource.path "/usr/bin:/usr/sbin"
- expect(@resource.path).to eql("/usr/bin:/usr/sbin")
+ it "allows you to specify the shell to run the command with" do
+ resource.shell "/bin/zsh"
+ expect(resource.shell).to eql("/bin/zsh")
end
- it "should allow you to specify the home directory" do
- @resource.home "/root"
- expect(@resource.home).to eql("/root")
- end
-
- it "should allow you to specify the shell to run the command with" do
- @resource.shell "/bin/zsh"
- expect(@resource.shell).to eql("/bin/zsh")
- end
-
- it "should allow you to specify environment variables hash" do
+ it "allows you to specify environment variables hash" do
env = { "TEST" => "LOL" }
- @resource.environment env
- expect(@resource.environment).to eql(env)
+ resource.environment env
+ expect(resource.environment).to eql(env)
end
- it "should allow * for all time and date values" do
+ it "allows * for all time and date values" do
%w{minute hour day month weekday}.each do |x|
- expect(@resource.send(x, "*")).to eql("*")
+ expect(resource.send(x, "*")).to eql("*")
end
end
- it "should allow ranges for all time and date values" do
+ it "allows ranges for all time and date values" do
%w{minute hour day month weekday}.each do |x|
- expect(@resource.send(x, "1-2,5")).to eql("1-2,5")
+ expect(resource.send(x, "1-2,5")).to eql("1-2,5")
end
end
- it "should have a default value of * for all time and date values" do
+ it "has a default value of * for all time and date values" do
%w{minute hour day month weekday}.each do |x|
- expect(@resource.send(x)).to eql("*")
+ expect(resource.send(x)).to eql("*")
end
end
- it "should have a default value of root for the user" do
- expect(@resource.user).to eql("root")
+ it "has a default value of root for the user" do
+ expect(resource.user).to eql("root")
end
- it "should reject any minute over 59" do
- expect { @resource.minute "60" }.to raise_error(RangeError)
+ it "rejects any minute over 59" do
+ expect { resource.minute "60" }.to raise_error(RangeError)
end
- it "should reject any hour over 23" do
- expect { @resource.hour "24" }.to raise_error(RangeError)
+ it "rejects any hour over 23" do
+ expect { resource.hour "24" }.to raise_error(RangeError)
end
- it "should reject any day over 31" do
- expect { @resource.day "32" }.to raise_error(RangeError)
+ it "rejects any day over 31" do
+ expect { resource.day "32" }.to raise_error(RangeError)
end
- it "should reject any month over 12" do
- expect { @resource.month "13" }.to raise_error(RangeError)
+ it "rejects any month over 12" do
+ expect { resource.month "13" }.to raise_error(RangeError)
end
describe "weekday" do
- it "should reject any weekday over 7" do
- expect { @resource.weekday "8" }.to raise_error(RangeError)
+ it "rejects any weekday over 7" do
+ expect { resource.weekday "8" }.to raise_error(RangeError)
end
- it "should reject any symbols which don't represent day of week" do
- expect { @resource.weekday :foo }.to raise_error(RangeError)
+ it "rejects any symbols which don't represent day of week" do
+ expect { resource.weekday :foo }.to raise_error(RangeError)
end
end
- it "should convert integer schedule values to a string" do
+ it "converts integer schedule values to a string" do
%w{minute hour day month weekday}.each do |x|
- expect(@resource.send(x, 5)).to eql("5")
+ expect(resource.send(x, 5)).to eql("5")
end
end
describe "when it has a time (minute, hour, day, month, weeekend) and user" do
before do
- @resource.command("tackle")
- @resource.minute("1")
- @resource.hour("2")
- @resource.day("3")
- @resource.month("4")
- @resource.weekday("5")
- @resource.user("root")
+ resource.command("tackle")
+ resource.minute("1")
+ resource.hour("2")
+ resource.day("3")
+ resource.month("4")
+ resource.weekday("5")
+ resource.user("root")
end
it "describes the state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:minute]).to eq("1")
expect(state[:hour]).to eq("2")
expect(state[:day]).to eq("3")
@@ -180,7 +172,7 @@ describe Chef::Resource::Cron do
end
it "returns the command as its identity" do
- expect(@resource.identity).to eq("tackle")
+ expect(resource.identity).to eq("tackle")
end
end
end
diff --git a/spec/unit/resource/csh_spec.rb b/spec/unit/resource/csh_spec.rb
index 864175fc85..3391bf84cf 100644
--- a/spec/unit/resource/csh_spec.rb
+++ b/spec/unit/resource/csh_spec.rb
@@ -20,21 +20,18 @@ require "spec_helper"
describe Chef::Resource::Csh do
- before(:each) do
- @resource = Chef::Resource::Csh.new("fakey_fakerton")
- end
+ let(:resource) { Chef::Resource::Csh.new("fakey_fakerton") }
- it "should create a new Chef::Resource::Csh" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Csh)
+ it "is a subclass of Chef::Resource::Script" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Script)
end
- it "should have a resource name of :csh" do
- expect(@resource.resource_name).to eql(:csh)
+ it "has a resource name of :csh" do
+ expect(resource.resource_name).to eql(:csh)
end
- it "should have an interpreter of csh" do
- expect(@resource.interpreter).to eql("csh")
+ it "has an interpreter of csh" do
+ expect(resource.interpreter).to eql("csh")
end
end
diff --git a/spec/unit/resource/deploy_spec.rb b/spec/unit/resource/deploy_spec.rb
deleted file mode 100644
index 33f16b4a89..0000000000
--- a/spec/unit/resource/deploy_spec.rb
+++ /dev/null
@@ -1,283 +0,0 @@
-#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-require "support/shared/unit/resource/static_provider_resolution"
-
-describe Chef::Resource::Deploy do
-
- static_provider_resolution(
- resource: Chef::Resource::Deploy,
- provider: Chef::Provider::Deploy::Timestamped,
- name: :deploy,
- action: :deploy
- )
-
- class << self
- def resource_has_a_string_attribute(attr_name)
- it "has a String attribute for #{attr_name}" do
- @resource.send(attr_name, "this is a string")
- expect(@resource.send(attr_name)).to eql("this is a string")
- expect { @resource.send(attr_name, 8675309) }.to raise_error(ArgumentError)
- end
- end
-
- def resource_has_a_boolean_attribute(attr_name, opts = { :defaults_to => false })
- it "has a Boolean attribute for #{attr_name}" do
- expect(@resource.send(attr_name)).to eql(opts[:defaults_to])
- @resource.send(attr_name, !opts[:defaults_to])
- expect(@resource.send(attr_name)).to eql( !opts[:defaults_to] )
- end
- end
-
- def resource_has_a_callback_attribute(attr_name)
- it "has a Callback attribute #{attr_name}" do
- callback_block = lambda { :noop }
- expect { @resource.send(attr_name, &callback_block) }.not_to raise_error
- expect(@resource.send(attr_name)).to eq(callback_block)
- callback_file = "path/to/callback.rb"
- expect { @resource.send(attr_name, callback_file) }.not_to raise_error
- expect(@resource.send(attr_name)).to eq(callback_file)
- expect { @resource.send(attr_name, :this_is_fail) }.to raise_error(ArgumentError)
- end
- end
- end
-
- before do
- @resource = Chef::Resource::Deploy.new("/my/deploy/dir")
- end
-
- resource_has_a_string_attribute(:repo)
- resource_has_a_string_attribute(:deploy_to)
- resource_has_a_string_attribute(:role)
- resource_has_a_string_attribute(:restart_command)
- resource_has_a_string_attribute(:migration_command)
- resource_has_a_string_attribute(:user)
- resource_has_a_string_attribute(:group)
- resource_has_a_string_attribute(:repository_cache)
- resource_has_a_string_attribute(:copy_exclude)
- resource_has_a_string_attribute(:revision)
- resource_has_a_string_attribute(:remote)
- resource_has_a_string_attribute(:git_ssh_wrapper)
- resource_has_a_string_attribute(:svn_username)
- resource_has_a_string_attribute(:svn_password)
- resource_has_a_string_attribute(:svn_arguments)
- resource_has_a_string_attribute(:svn_info_args)
-
- resource_has_a_boolean_attribute(:migrate, :defaults_to => false)
- resource_has_a_boolean_attribute(:enable_submodules, :defaults_to => false)
- resource_has_a_boolean_attribute(:shallow_clone, :defaults_to => false)
-
- it "uses the first argument as the deploy directory" do
- expect(@resource.deploy_to).to eql("/my/deploy/dir")
- end
-
- # For git, any revision, branch, tag, whatever is resolved to a SHA1 ref.
- # For svn, the branch is included in the repo URL.
- # Therefore, revision and branch ARE NOT SEPARATE THINGS
- it "aliases #revision as #branch" do
- @resource.branch "stable"
- expect(@resource.revision).to eql("stable")
- end
-
- it "takes the SCM resource to use as a constant, and defaults to git" do
- expect(@resource.scm_provider).to eql(Chef::Provider::Git)
- @resource.scm_provider Chef::Provider::Subversion
- expect(@resource.scm_provider).to eql(Chef::Provider::Subversion)
- end
-
- it "allows scm providers to be set via symbol" do
- expect(@resource.scm_provider).to eq(Chef::Provider::Git)
- @resource.scm_provider :subversion
- expect(@resource.scm_provider).to eq(Chef::Provider::Subversion)
- end
-
- it "allows scm providers to be set via string" do
- expect(@resource.scm_provider).to eq(Chef::Provider::Git)
- @resource.scm_provider "subversion"
- expect(@resource.scm_provider).to eq(Chef::Provider::Subversion)
- end
-
- it "has a boolean attribute for svn_force_export defaulting to false" do
- expect(@resource.svn_force_export).to be_falsey
- @resource.svn_force_export true
- expect(@resource.svn_force_export).to be_truthy
- expect { @resource.svn_force_export(10053) }.to raise_error(ArgumentError)
- end
-
- it "takes arbitrary environment variables in a hash" do
- @resource.environment "RAILS_ENV" => "production"
- expect(@resource.environment).to eq({ "RAILS_ENV" => "production" })
- end
-
- it "takes string arguments to environment for backwards compat, setting RAILS_ENV, RACK_ENV, and MERB_ENV" do
- @resource.environment "production"
- expect(@resource.environment).to eq({ "RAILS_ENV" => "production", "RACK_ENV" => "production", "MERB_ENV" => "production" })
- end
-
- it "sets destination to $deploy_to/shared/$repository_cache" do
- expect(@resource.destination).to eql("/my/deploy/dir/shared/cached-copy")
- end
-
- it "sets shared_path to $deploy_to/shared" do
- expect(@resource.shared_path).to eql("/my/deploy/dir/shared")
- end
-
- it "sets current_path to $deploy_to/current" do
- expect(@resource.current_path).to eql("/my/deploy/dir/current")
- end
-
- it "gets the current_path correct even if the shared_path is set (regression test)" do
- @resource.shared_path
- expect(@resource.current_path).to eql("/my/deploy/dir/current")
- end
-
- it "allows depth to be set via integer" do
- expect(@resource.depth).to be_nil
- @resource.depth 1
- expect(@resource.depth).to eql(1)
- end
-
- it "gives #depth as 5 if shallow clone is true, nil otherwise" do
- expect(@resource.depth).to be_nil
- @resource.shallow_clone true
- expect(@resource.depth).to eql(5)
- end
-
- it "aliases repo as repository" do
- @resource.repository "git@github.com/opcode/cookbooks.git"
- expect(@resource.repo).to eql("git@github.com/opcode/cookbooks.git")
- end
-
- it "aliases git_ssh_wrapper as ssh_wrapper" do
- @resource.ssh_wrapper "git_my_repo.sh"
- expect(@resource.git_ssh_wrapper).to eql("git_my_repo.sh")
- end
-
- it "has an Array attribute purge_before_symlink, default: log, tmp/pids, public/system" do
- expect(@resource.purge_before_symlink).to eq(%w{ log tmp/pids public/system })
- @resource.purge_before_symlink %w{foo bar baz}
- expect(@resource.purge_before_symlink).to eq(%w{foo bar baz})
- end
-
- it "has an Array attribute create_dirs_before_symlink, default: tmp, public, config" do
- expect(@resource.create_dirs_before_symlink).to eq(%w{tmp public config})
- @resource.create_dirs_before_symlink %w{foo bar baz}
- expect(@resource.create_dirs_before_symlink).to eq(%w{foo bar baz})
- end
-
- it 'has a Hash attribute symlinks, default: {"system" => "public/system", "pids" => "tmp/pids", "log" => "log"}' do
- default = { "system" => "public/system", "pids" => "tmp/pids", "log" => "log" }
- expect(@resource.symlinks).to eq(default)
- @resource.symlinks "foo" => "bar/baz"
- expect(@resource.symlinks).to eq({ "foo" => "bar/baz" })
- end
-
- it 'has a Hash attribute symlink_before_migrate, default "config/database.yml" => "config/database.yml"' do
- expect(@resource.symlink_before_migrate).to eq({ "config/database.yml" => "config/database.yml" })
- @resource.symlink_before_migrate "wtf?" => "wtf is going on"
- expect(@resource.symlink_before_migrate).to eq({ "wtf?" => "wtf is going on" })
- end
-
- resource_has_a_callback_attribute :before_migrate
- resource_has_a_callback_attribute :before_symlink
- resource_has_a_callback_attribute :before_restart
- resource_has_a_callback_attribute :after_restart
-
- it "aliases restart_command as restart" do
- @resource.restart "foobaz"
- expect(@resource.restart_command).to eq("foobaz")
- end
-
- it "takes a block for the restart parameter" do
- restart_like_this = lambda { p :noop }
- @resource.restart(&restart_like_this)
- expect(@resource.restart).to eq(restart_like_this)
- end
-
- it "allows providers to be set with a full class name" do
- @resource.provider Chef::Provider::Deploy::Timestamped
- expect(@resource.provider).to eq(Chef::Provider::Deploy::Timestamped)
- end
-
- it "allows deploy providers to be set via symbol" do
- @resource.provider :revision
- expect(@resource.provider).to eq(Chef::Provider::Deploy::Revision)
- end
-
- it "allows deploy providers to be set via string" do
- @resource.provider "revision"
- expect(@resource.provider).to eq(Chef::Provider::Deploy::Revision)
- end
-
- it "defaults keep_releases to 5" do
- expect(@resource.keep_releases).to eq(5)
- end
-
- it "allows keep_releases to be set via integer" do
- @resource.keep_releases 10
- expect(@resource.keep_releases).to eq(10)
- end
-
- it "enforces a minimum keep_releases of 1" do
- @resource.keep_releases 0
- expect(@resource.keep_releases).to eq(1)
- end
-
- describe "when it has a timeout attribute" do
- let(:ten_seconds) { 10 }
- before { @resource.timeout(ten_seconds) }
- it "stores this timeout" do
- expect(@resource.timeout).to eq(ten_seconds)
- end
- end
-
- describe "when it has no timeout attribute" do
- it "should have no default timeout" do
- expect(@resource.timeout).to be_nil
- end
- end
-
- describe "when it has meta application root, revision, user, group,
- scm provider, repository cache, environment, simlinks and migrate" do
- before do
- @resource.repository("http://uri.org")
- @resource.deploy_to("/")
- @resource.revision("1.2.3")
- @resource.user("root")
- @resource.group("pokemon")
- @resource.scm_provider(Chef::Provider::Git)
- @resource.repository_cache("cached-copy")
- @resource.environment({ "SUDO" => "TRUE" })
- @resource.symlinks({ "system" => "public/system" })
- @resource.migrate(false)
-
- end
-
- it "describes its state" do
- state = @resource.state
- expect(state[:deploy_to]).to eq("/")
- expect(state[:revision]).to eq("1.2.3")
- end
-
- it "returns the repository URI as its identity" do
- expect(@resource.identity).to eq("http://uri.org")
- end
- end
-
-end
diff --git a/spec/unit/resource/directory_spec.rb b/spec/unit/resource/directory_spec.rb
index cfb3ade135..778e54ccb6 100644
--- a/spec/unit/resource/directory_spec.rb
+++ b/spec/unit/resource/directory_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,63 +20,55 @@
require "spec_helper"
describe Chef::Resource::Directory do
+ let(:resource) { Chef::Resource::Directory.new("fakey_fakerton") }
- before(:each) do
- @resource = Chef::Resource::Directory.new("fakey_fakerton")
+ it "has a name property" do
+ expect(resource.name).to eql("fakey_fakerton")
end
- it "should create a new Chef::Resource::Directory" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Directory)
+ it "has a default action of 'create'" do
+ expect(resource.action).to eql([:create])
end
- it "should have a name" do
- expect(@resource.name).to eql("fakey_fakerton")
+ it "accepts create or delete for action" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :blues }.to raise_error(ArgumentError)
end
- it "should have a default action of 'create'" do
- expect(@resource.action).to eql([:create])
+ it "uses the object name as the path by default" do
+ expect(resource.path).to eql("fakey_fakerton")
end
- it "should accept create or delete for action" do
- expect { @resource.action :create }.not_to raise_error
- expect { @resource.action :delete }.not_to raise_error
- expect { @resource.action :blues }.to raise_error(ArgumentError)
+ it "accepts a string as the path" do
+ expect { resource.path "/tmp" }.not_to raise_error
+ expect(resource.path).to eql("/tmp")
+ expect { resource.path Hash.new }.to raise_error(ArgumentError)
end
- it "should use the object name as the path by default" do
- expect(@resource.path).to eql("fakey_fakerton")
- end
-
- it "should accept a string as the path" do
- expect { @resource.path "/tmp" }.not_to raise_error
- expect(@resource.path).to eql("/tmp")
- expect { @resource.path Hash.new }.to raise_error(ArgumentError)
- end
-
- it "should allow you to have specify whether the action is recursive with true/false" do
- expect { @resource.recursive true }.not_to raise_error
- expect { @resource.recursive false }.not_to raise_error
- expect { @resource.recursive "monkey" }.to raise_error(ArgumentError)
+ it "allows you to have specify whether the action is recursive with true/false" do
+ expect { resource.recursive true }.not_to raise_error
+ expect { resource.recursive false }.not_to raise_error
+ expect { resource.recursive "monkey" }.to raise_error(ArgumentError)
end
describe "when it has group, mode, and owner" do
before do
- @resource.path("/tmp/foo/bar/")
- @resource.group("wheel")
- @resource.mode("0664")
- @resource.owner("root")
+ resource.path("/tmp/foo/bar/")
+ resource.group("wheel")
+ resource.mode("0664")
+ resource.owner("root")
end
it "describes its state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:group]).to eq("wheel")
expect(state[:mode]).to eq("0664")
expect(state[:owner]).to eq("root")
end
it "returns the directory path as its identity" do
- expect(@resource.identity).to eq("/tmp/foo/bar/")
+ expect(resource.identity).to eq("/tmp/foo/bar/")
end
end
end
diff --git a/spec/unit/resource/dmg_package_spec.rb b/spec/unit/resource/dmg_package_spec.rb
new file mode 100644
index 0000000000..d1d8a71188
--- /dev/null
+++ b/spec/unit/resource/dmg_package_spec.rb
@@ -0,0 +1,35 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::DmgPackage do
+
+ let(:resource) { Chef::Resource::DmgPackage.new("myapp") }
+
+ it "has a resource name of :dmg_package" do
+ expect(resource.resource_name).to eql(:dmg_package)
+ end
+
+ it "has a default action of install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "the app property is the name property" do
+ expect(resource.app).to eql("myapp")
+ end
+end
diff --git a/spec/unit/resource/dnf_package_spec.rb b/spec/unit/resource/dnf_package_spec.rb
new file mode 100644
index 0000000000..21979b05c3
--- /dev/null
+++ b/spec/unit/resource/dnf_package_spec.rb
@@ -0,0 +1,93 @@
+#
+# Copyright:: Copyright 2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "support/shared/unit/resource/static_provider_resolution"
+
+describe Chef::Resource::DnfPackage, "initialize" do
+
+ static_provider_resolution(
+ resource: Chef::Resource::DnfPackage,
+ provider: Chef::Provider::Package::Dnf,
+ name: :dnf_package,
+ action: :install,
+ os: "linux",
+ platform_family: "rhel"
+ )
+
+end
+
+describe Chef::Resource::DnfPackage, "arch" do
+ let(:resource) { Chef::Resource::DnfPackage.new("foo") }
+
+ it "sets the arch variable to whatever is passed in" do
+ resource.arch("i386")
+ expect(resource.arch).to eql(["i386"])
+ end
+end
+
+describe Chef::Resource::DnfPackage, "flush_cache" do
+ let(:resource) { Chef::Resource::DnfPackage.new("foo") }
+
+ it "defaults the flush timing to false" do
+ flush_hash = { :before => false, :after => false }
+ expect(resource.flush_cache).to eq(flush_hash)
+ end
+
+ it "allows you to set the flush timing with an array" do
+ flush_array = [ :before, :after ]
+ flush_hash = { :before => true, :after => true }
+ resource.flush_cache(flush_array)
+ expect(resource.flush_cache).to eq(flush_hash)
+ end
+
+ it "allows you to set the flush timing with a hash" do
+ flush_hash = { :before => true, :after => true }
+ resource.flush_cache(flush_hash)
+ expect(resource.flush_cache).to eq(flush_hash)
+ end
+
+ it "allows 'true' for flush_cache" do
+ resource.flush_cache(true)
+ expect(resource.flush_cache).to eq({ before: true, after: true })
+ end
+
+ it "allows 'false' for flush_cache" do
+ resource.flush_cache(false)
+ expect(resource.flush_cache).to eq({ before: false, after: false })
+ end
+
+ it "allows ':before' for flush_cache" do
+ resource.flush_cache(:before)
+ expect(resource.flush_cache).to eq({ before: true, after: false })
+ end
+
+ it "allows ':after' for flush_cache" do
+ resource.flush_cache(:after)
+ expect(resource.flush_cache).to eq({ before: false, after: true })
+ end
+end
+
+describe Chef::Resource::DnfPackage, "allow_downgrade" do
+ let(:resource) { Chef::Resource::DnfPackage.new("foo") }
+
+ it "allows you to specify whether allow_downgrade is true or false" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ expect { resource.allow_downgrade true }.not_to raise_error
+ expect { resource.allow_downgrade false }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/dsc_resource_spec.rb b/spec/unit/resource/dsc_resource_spec.rb
index 00e667c9de..b687811392 100644
--- a/spec/unit/resource/dsc_resource_spec.rb
+++ b/spec/unit/resource/dsc_resource_spec.rb
@@ -18,22 +18,23 @@
require "spec_helper"
describe Chef::Resource::DscResource do
let(:dsc_test_resource_name) { "DSCTest" }
+ let(:dsc_test_resource_module_version) { "2.7.2" }
let(:dsc_test_property_name) { :DSCTestProperty }
let(:dsc_test_property_value) { "DSCTestValue" }
let(:dsc_test_reboot_action) { :reboot_now }
let(:dsc_test_timeout) { 101 }
context "when Powershell supports Dsc" do
- let(:dsc_test_run_context) {
+ let(:dsc_test_run_context) do
node = Chef::Node.new
node.automatic[:languages][:powershell][:version] = "5.0.10018.0"
empty_events = Chef::EventDispatch::Dispatcher.new
Chef::RunContext.new(node, {}, empty_events)
- }
+ end
- let(:dsc_test_resource) {
+ let(:dsc_test_resource) do
Chef::Resource::DscResource.new(dsc_test_resource_name, dsc_test_run_context)
- }
+ end
it "has a default action of `:run`" do
expect(dsc_test_resource.action).to eq([:run])
@@ -53,6 +54,11 @@ describe Chef::Resource::DscResource do
expect(dsc_test_resource.module_name).to eq(dsc_test_resource_name)
end
+ it "allows the module_version attribute to be set" do
+ dsc_test_resource.module_version(dsc_test_resource_module_version)
+ expect(dsc_test_resource.module_version).to eq(dsc_test_resource_module_version)
+ end
+
it "allows the reboot_action attribute to be set" do
dsc_test_resource.reboot_action(dsc_test_reboot_action)
expect(dsc_test_resource.reboot_action).to eq(dsc_test_reboot_action)
@@ -71,16 +77,16 @@ describe Chef::Resource::DscResource do
end
it "raises a TypeError if property_name is not a symbol" do
- expect {
+ expect do
dsc_test_resource.property("Foo", dsc_test_property_value)
- }.to raise_error(TypeError)
+ end.to raise_error(TypeError)
end
context "when using DelayedEvaluators" do
it "allows setting a dsc property with a property name of type Symbol" do
- dsc_test_resource.property(dsc_test_property_name, Chef::DelayedEvaluator.new {
+ dsc_test_resource.property(dsc_test_property_name, Chef::DelayedEvaluator.new do
dsc_test_property_value
- })
+ end)
expect(dsc_test_resource.property(dsc_test_property_name)).to eq(dsc_test_property_value)
expect(dsc_test_resource.properties[dsc_test_property_name]).to eq(dsc_test_property_value)
end
diff --git a/spec/unit/resource/dsc_script_spec.rb b/spec/unit/resource/dsc_script_spec.rb
index 4892049884..f0c81e43b5 100644
--- a/spec/unit/resource/dsc_script_spec.rb
+++ b/spec/unit/resource/dsc_script_spec.rb
@@ -22,15 +22,15 @@ describe Chef::Resource::DscScript do
let(:dsc_test_resource_name) { "DSCTest" }
context "when Powershell supports Dsc" do
- let(:dsc_test_run_context) {
+ let(:dsc_test_run_context) do
node = Chef::Node.new
node.automatic[:languages][:powershell][:version] = "4.0"
empty_events = Chef::EventDispatch::Dispatcher.new
Chef::RunContext.new(node, {}, empty_events)
- }
- let(:dsc_test_resource) {
+ end
+ let(:dsc_test_resource) do
Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context)
- }
+ end
let(:configuration_code) { 'echo "This is supposed to create a configuration document."' }
let(:configuration_path) { "c:/myconfigs/formatc.ps1" }
let(:configuration_name) { "formatme" }
diff --git a/spec/unit/resource/easy_install_package_spec.rb b/spec/unit/resource/easy_install_package_spec.rb
deleted file mode 100644
index ce8e6d8bf6..0000000000
--- a/spec/unit/resource/easy_install_package_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# Author:: Joe Williams (<joe@joetify.com>)
-# Copyright:: Copyright 2009-2016, Joe Williams
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-require "support/shared/unit/resource/static_provider_resolution"
-
-describe Chef::Resource::EasyInstallPackage, "initialize" do
-
- static_provider_resolution(
- resource: Chef::Resource::EasyInstallPackage,
- provider: Chef::Provider::Package::EasyInstall,
- name: :easy_install_package,
- action: :install
- )
-
- before(:each) do
- @resource = Chef::Resource::EasyInstallPackage.new("foo")
- end
-
- it "should allow you to set the easy_install_binary attribute" do
- @resource.easy_install_binary "/opt/local/bin/easy_install"
- expect(@resource.easy_install_binary).to eql("/opt/local/bin/easy_install")
- end
-end
diff --git a/spec/unit/resource/erl_call_spec.rb b/spec/unit/resource/erl_call_spec.rb
deleted file mode 100644
index 6d1f45ec63..0000000000
--- a/spec/unit/resource/erl_call_spec.rb
+++ /dev/null
@@ -1,81 +0,0 @@
-#
-# Author:: Joe Williams (<joe@joetify.com>)
-# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2009-2016, Joe Williams
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-
-describe Chef::Resource::ErlCall do
-
- before(:each) do
- @resource = Chef::Resource::ErlCall.new("fakey_fakerton")
- end
-
- it "should create a new Chef::Resource::ErlCall" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::ErlCall)
- end
-
- it "should have a resource name of :erl_call" do
- expect(@resource.resource_name).to eql(:erl_call)
- end
-
- it "should have a default action of run" do
- expect(@resource.action).to eql([:run])
- end
-
- it "should accept run as an action" do
- expect { @resource.action :run }.not_to raise_error
- end
-
- it "should allow you to set the code attribute" do
- @resource.code "q()."
- expect(@resource.code).to eql("q().")
- end
-
- it "should allow you to set the cookie attribute" do
- @resource.cookie "nomnomnom"
- expect(@resource.cookie).to eql("nomnomnom")
- end
-
- it "should allow you to set the distributed attribute" do
- @resource.distributed true
- expect(@resource.distributed).to eql(true)
- end
-
- it "should allow you to set the name_type attribute" do
- @resource.name_type "sname"
- expect(@resource.name_type).to eql("sname")
- end
-
- it "should allow you to set the node_name attribute" do
- @resource.node_name "chef@erlang"
- expect(@resource.node_name).to eql("chef@erlang")
- end
-
- describe "when it has cookie and node_name" do
- before do
- @resource.code("erl-call:function()")
- @resource.cookie("cookie")
- @resource.node_name("raster")
- end
-
- it "returns the code as its identity" do
- expect(@resource.identity).to eq("erl-call:function()")
- end
- end
-end
diff --git a/spec/unit/resource/execute_spec.rb b/spec/unit/resource/execute_spec.rb
index 70824e9f5b..c99e87b351 100644
--- a/spec/unit/resource/execute_spec.rb
+++ b/spec/unit/resource/execute_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,7 @@ describe Chef::Resource::Execute do
let(:execute_resource) { Chef::Resource::Execute.new(resource_instance_name) }
it_behaves_like "an execute resource"
- it "default guard interpreter should be :execute interpreter" do
+ it "default guard interpreter is :execute interpreter" do
expect(execute_resource.guard_interpreter).to be(:execute)
end
@@ -32,4 +32,238 @@ describe Chef::Resource::Execute do
expect(execute_resource.is_guard_interpreter).to eq(false)
end
+ describe "#qualify_user" do
+ let(:password) { "password" }
+ let(:domain) { nil }
+
+ context "when username is passed as user@domain" do
+ let(:username) { "user@domain" }
+
+ it "correctly parses the user and domain" do
+ identity = execute_resource.qualify_user(username, password, domain)
+ expect(identity[:domain]).to eq("domain")
+ expect(identity[:user]).to eq("user")
+ end
+ end
+
+ context "when username is passed as domain\\user" do
+ let(:username) { "domain\\user" }
+
+ it "correctly parses the user and domain" do
+ identity = execute_resource.qualify_user(username, password, domain)
+ expect(identity[:domain]).to eq("domain")
+ expect(identity[:user]).to eq("user")
+ end
+ end
+ end
+
+ shared_examples_for "it received valid credentials" do
+ describe "the validation method" do
+ it "does not raise an error" do
+ expect { execute_resource.validate_identity_platform(username, password, domain) }.not_to raise_error
+ end
+ end
+
+ describe "the name qualification method" do
+ it "correctly translates the user and domain" do
+ identity = nil
+ expect { identity = execute_resource.qualify_user(username, password, domain) }.not_to raise_error
+ expect(identity[:domain]).to eq(domain)
+ expect(identity[:user]).to eq(username)
+ end
+ end
+ end
+
+ shared_examples_for "it received invalid credentials" do
+ describe "the validation method" do
+ it "raises an error" do
+ expect { execute_resource.validate_identity_platform(username, password, domain, elevated) }.to raise_error(ArgumentError)
+ end
+ end
+ end
+
+ shared_examples_for "it received invalid username and domain" do
+ describe "the validation method" do
+ it "raises an error" do
+ expect { execute_resource.qualify_user(username, password, domain) }.to raise_error(ArgumentError)
+ end
+ end
+ end
+
+ shared_examples_for "it received credentials that are not valid on the platform" do
+ describe "the validation method" do
+ it "raises an error" do
+ expect { execute_resource.validate_identity_platform(username, password, domain) }.to raise_error(Chef::Exceptions::UnsupportedPlatform)
+ end
+ end
+ end
+
+ shared_examples_for "a consumer of the Execute resource" do
+ context "when running on Windows" do
+ before do
+ allow(execute_resource).to receive(:node).and_return({ :platform_family => "windows" })
+ end
+
+ context "when no user, domain, or password is specified" do
+ let(:username) { nil }
+ let(:domain) { nil }
+ let(:password) { nil }
+ it_behaves_like "it received valid credentials"
+ end
+
+ context "when a valid username is specified" do
+ let(:username) { "starchild" }
+ let(:elevated) { false }
+ context "when a valid domain is specified" do
+ let(:domain) { "mothership" }
+
+ context "when the password is not specified" do
+ let(:password) { nil }
+ it_behaves_like "it received invalid credentials"
+ end
+
+ context "when the password is specified" do
+ let(:password) { "we.funk!" }
+ it_behaves_like "it received valid credentials"
+ end
+ end
+
+ context "when the domain is not specified" do
+ let(:domain) { nil }
+ let(:elevated) { false }
+
+ context "when the password is not specified" do
+ let(:password) { nil }
+ it_behaves_like "it received invalid credentials"
+ end
+
+ context "when the password is specified" do
+ let(:password) { "we.funk!" }
+ it_behaves_like "it received valid credentials"
+ end
+ end
+
+ context "when username is not specified" do
+ let(:username) { nil }
+
+ context "when domain is specified" do
+ let(:domain) { "mothership" }
+ let(:password) { nil }
+ it_behaves_like "it received invalid username and domain"
+ end
+
+ context "when password is specified" do
+ let(:domain) { nil }
+ let(:password) { "we.funk!" }
+ it_behaves_like "it received invalid username and domain"
+ end
+ end
+ end
+
+ context "when invalid username is specified" do
+ let(:username) { "user@domain@domain" }
+ let(:domain) { nil }
+ let(:password) { "we.funk!" }
+ it_behaves_like "it received invalid username and domain"
+ end
+
+ context "when the domain is provided in both username and domain" do
+ let(:domain) { "some_domain" }
+ let(:password) { "we.funk!" }
+
+ context "when username is in the form domain\\user" do
+ let(:username) { "mothership\\starchild" }
+ it_behaves_like "it received invalid username and domain"
+ end
+
+ context "when username is in the form user@domain" do
+ let(:username) { "starchild@mothership" }
+ it_behaves_like "it received invalid username and domain"
+ end
+ end
+
+ context "when elevated is passed" do
+ let(:elevated) { true }
+
+ context "when username and password are not passed" do
+ let(:username) { nil }
+ let(:domain) { nil }
+ let(:password) { nil }
+ it_behaves_like "it received invalid credentials"
+ end
+
+ context "when username and password are passed" do
+ let(:username) { "user" }
+ let(:domain) { nil }
+ let(:password) { "we.funk!" }
+ it_behaves_like "it received valid credentials"
+ end
+ end
+ end
+
+ context "when not running on Windows" do
+ before do
+ allow(execute_resource).to receive(:node).and_return({ :platform_family => "ubuntu" })
+ end
+
+ context "when no user, domain, or password is specified" do
+ let(:username) { nil }
+ let(:domain) { nil }
+ let(:password) { nil }
+ it_behaves_like "it received valid credentials"
+ end
+
+ context "when the user is specified and the domain and password are not" do
+ let(:username) { "starchild" }
+ let(:domain) { nil }
+ let(:password) { nil }
+ it_behaves_like "it received valid credentials"
+
+ context "when the password is specified and the domain is not" do
+ let(:password) { "we.funk!" }
+ let(:domain) { nil }
+ it_behaves_like "it received credentials that are not valid on the platform"
+ end
+
+ context "when the domain is specified and the password is not" do
+ let(:domain) { "mothership" }
+ let(:password) { nil }
+ it_behaves_like "it received credentials that are not valid on the platform"
+ end
+
+ context "when the domain and password are specified" do
+ let(:domain) { "mothership" }
+ let(:password) { "we.funk!" }
+ it_behaves_like "it received credentials that are not valid on the platform"
+ end
+ end
+
+ context "when the user is not specified" do
+ let(:username) { nil }
+ context "when the domain is specified" do
+ let(:domain) { "mothership" }
+ context "when the password is specified" do
+ let(:password) { "we.funk!" }
+ it_behaves_like "it received credentials that are not valid on the platform"
+ end
+
+ context "when password is not specified" do
+ let(:password) { nil }
+ it_behaves_like "it received credentials that are not valid on the platform"
+ end
+ end
+
+ context "when the domain is not specified" do
+ let(:domain) { nil }
+ context "when the password is specified" do
+ let(:password) { "we.funk!" }
+ it_behaves_like "it received credentials that are not valid on the platform"
+ end
+ end
+ end
+ end
+ end
+
+ it_behaves_like "a consumer of the Execute resource"
+
end
diff --git a/spec/unit/resource/file/verification/systemd_unit_spec.rb b/spec/unit/resource/file/verification/systemd_unit_spec.rb
new file mode 100644
index 0000000000..8a3d849582
--- /dev/null
+++ b/spec/unit/resource/file/verification/systemd_unit_spec.rb
@@ -0,0 +1,103 @@
+#
+# Author:: Mal Graty (<mal.graty@googlemail.com>)
+# Copyright:: Copyright 2014-2017, Chef Software, Inc
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::File::Verification::SystemdUnit do
+ let(:command) { "#{systemd_analyze_path} verify %{path}" }
+ let(:opts) { { :future => true } }
+ let(:parent_resource) { Chef::Resource.new("llama") }
+ let(:systemd_analyze_path) { "/usr/bin/systemd-analyze" }
+ let(:systemd_dir) { "/etc/systemd/system" }
+ let(:temp_path) { "/tmp" }
+ let(:unit_name) { "sysstat-collect.timer" }
+ let(:unit_path) { "#{systemd_dir}/#{unit_name}" }
+ let(:unit_temp_path) { "#{systemd_dir}/.chef-#{unit_name}" }
+ let(:unit_test_path) { "#{temp_path}/#{unit_name}" }
+
+ describe "verification registration" do
+ it "registers itself for later use" do
+ expect(Chef::Resource::File::Verification.lookup(:systemd_unit)).to eq(Chef::Resource::File::Verification::SystemdUnit)
+ end
+ end
+
+ describe "#initialize" do
+ before(:each) do
+ allow_any_instance_of(Chef::Resource::File::Verification::SystemdUnit).to receive(:which)
+ .with("systemd-analyze")
+ .and_return(systemd_analyze_path)
+ end
+
+ it "overwrites the @command variable with the verification command" do
+ v = Chef::Resource::File::Verification::SystemdUnit.new(parent_resource, :systemd_unit, {})
+ expect(v.instance_variable_get(:@command)).to eql(command)
+ end
+ end
+
+ describe "#verify" do
+ context "with the systemd-analyze binary available" do
+ before(:each) do
+ allow_any_instance_of(Chef::Resource::File::Verification::SystemdUnit).to receive(:which)
+ .with("systemd-analyze")
+ .and_return(systemd_analyze_path)
+
+ allow(parent_resource).to receive(:path)
+ .and_return(unit_path)
+ allow(Dir).to receive(:mktmpdir)
+ .with("chef-systemd-unit") { |&b| b.call temp_path }
+ allow(FileUtils).to receive(:cp)
+ .with(unit_temp_path, unit_test_path)
+ end
+
+ it "copies the temp file to secondary location under correct name" do
+ v = Chef::Resource::File::Verification::SystemdUnit.new(parent_resource, :systemd_unit, {})
+
+ expect(FileUtils).to receive(:cp).with(unit_temp_path, unit_test_path)
+ expect(v).to receive(:verify_command).with(unit_test_path, opts)
+
+ v.verify(unit_temp_path, opts)
+ end
+
+ it "returns the value given by #verify_command" do
+ v = Chef::Resource::File::Verification::SystemdUnit.new(parent_resource, :systemd_unit, {})
+
+ expect(v).to receive(:verify_command)
+ .with(unit_test_path, opts)
+ .and_return("foo")
+
+ expect(v.verify(unit_temp_path, opts)).to eql("foo")
+ end
+ end
+
+ context "with the systemd-analyze binary unavailable" do
+ before(:each) do
+ allow_any_instance_of(Chef::Resource::File::Verification::SystemdUnit).to receive(:which)
+ .with("systemd-analyze")
+ .and_return(false)
+ end
+
+ it "skips verification" do
+ v = Chef::Resource::File::Verification::SystemdUnit.new(parent_resource, :systemd_unit, {})
+
+ expect(v).to_not receive(:verify_command)
+
+ expect(v.verify(unit_temp_path)).to eq(true)
+ end
+ end
+ end
+end
diff --git a/spec/unit/resource/file/verification_spec.rb b/spec/unit/resource/file/verification_spec.rb
index bc51eccaef..e46040268a 100644
--- a/spec/unit/resource/file/verification_spec.rb
+++ b/spec/unit/resource/file/verification_spec.rb
@@ -66,6 +66,11 @@ describe Chef::Resource::File::Verification do
v = Chef::Resource::File::Verification.new(parent_resource, nil, {}, &f_block)
expect(v.verify(temp_path)).to eq(false)
end
+
+ it "responds to to_s" do
+ v = Chef::Resource::File::Verification.new(parent_resource, nil, {}) {}
+ expect(v.to_s).to eq("<Proc>")
+ end
end
context "with a verification command(String)" do
@@ -81,24 +86,18 @@ describe Chef::Resource::File::Verification do
end
end
- it "substitutes \%{file} with the path" do
- test_command = platform_specific_verify_command("file")
- v = Chef::Resource::File::Verification.new(parent_resource, test_command, {})
- expect(v.verify(temp_path)).to eq(true)
- end
-
- it "warns about deprecation when \%{file} is used" do
- expect(Chef::Log).to receive(:deprecation).with(/%{file} is deprecated/, /verification_spec\.rb/)
+ it "raises an error when \%{file} is used" do
test_command = platform_specific_verify_command("file")
- Chef::Resource::File::Verification.new(parent_resource, test_command, {})
- .verify(temp_path)
+ expect do
+ Chef::Resource::File::Verification.new(parent_resource, test_command, {}).verify(temp_path)
+ end.to raise_error(ArgumentError)
end
- it "does not warn about deprecation when \%{file} is not used" do
- expect(Chef::Log).to_not receive(:deprecation)
+ it "does not raise an error when \%{file} is not used" do
test_command = platform_specific_verify_command("path")
- Chef::Resource::File::Verification.new(parent_resource, test_command, {})
- .verify(temp_path)
+ expect do
+ Chef::Resource::File::Verification.new(parent_resource, test_command, {}).verify(temp_path)
+ end.to_not raise_error
end
it "substitutes \%{path} with the path" do
@@ -116,23 +115,32 @@ describe Chef::Resource::File::Verification do
v = Chef::Resource::File::Verification.new(parent_resource, "true", {})
expect(v.verify(temp_path)).to eq(true)
end
+
+ it "responds to to_s" do
+ v = Chef::Resource::File::Verification.new(parent_resource, "some command --here", {})
+ expect(v.to_s).to eq("some command --here")
+ end
end
context "with a named verification(Symbol)" do
+ let(:registered_verification) { double("registered_verification") }
+ subject { described_class.new(parent_resource, :cats, {}) }
before(:each) do
class Chef::Resource::File::Verification::Turtle < Chef::Resource::File::Verification
provides :cats
def verify(path, opts)
end
end
+ allow(Chef::Resource::File::Verification::Turtle).to receive(:new).and_return(registered_verification)
end
it "delegates to the registered verification" do
- registered_verification = double()
- allow(Chef::Resource::File::Verification::Turtle).to receive(:new).and_return(registered_verification)
- v = Chef::Resource::File::Verification.new(parent_resource, :cats, {})
expect(registered_verification).to receive(:verify).with(temp_path, {})
- v.verify(temp_path, {})
+ subject.verify(temp_path, {})
+ end
+
+ it "responds to to_s" do
+ expect(subject.to_s).to eq(":cats (Chef::Resource::File::Verification::Turtle)")
end
end
end
diff --git a/spec/unit/resource/file_spec.rb b/spec/unit/resource/file_spec.rb
index 19304cb6b8..f2dea066ad 100644
--- a/spec/unit/resource/file_spec.rb
+++ b/spec/unit/resource/file_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,88 +20,86 @@ require "spec_helper"
describe Chef::Resource::File do
- before(:each) do
- @resource = Chef::Resource::File.new("fakey_fakerton")
- end
+ let(:resource) { Chef::Resource::File.new("fakey_fakerton") }
- it "should have a name" do
- expect(@resource.name).to eql("fakey_fakerton")
+ it "has a name property" do
+ expect(resource.name).to eql("fakey_fakerton")
end
- it "should have a default action of 'create'" do
- expect(@resource.action).to eql([:create])
+ it "has a default action of 'create'" do
+ expect(resource.action).to eql([:create])
end
- it "should have a default content of nil" do
- expect(@resource.content).to be_nil
+ it "has a default content of nil" do
+ expect(resource.content).to be_nil
end
- it "should be set to back up 5 files by default" do
- expect(@resource.backup).to eql(5)
+ it "is set to back up 5 files by default" do
+ expect(resource.backup).to eql(5)
end
- it "should only accept strings for content" do
- expect { @resource.content 5 }.to raise_error(ArgumentError)
- expect { @resource.content :foo }.to raise_error(ArgumentError)
- expect { @resource.content "hello" => "there" }.to raise_error(ArgumentError)
- expect { @resource.content "hi" }.not_to raise_error
+ it "only accept strings for content" do
+ expect { resource.content 5 }.to raise_error(ArgumentError)
+ expect { resource.content :foo }.to raise_error(ArgumentError)
+ expect { resource.content "hello" => "there" }.to raise_error(ArgumentError)
+ expect { resource.content "hi" }.not_to raise_error
end
- it "should only accept false or a number for backup" do
- expect { @resource.backup true }.to raise_error(ArgumentError)
- expect { @resource.backup false }.not_to raise_error
- expect { @resource.backup 10 }.not_to raise_error
- expect { @resource.backup "blues" }.to raise_error(ArgumentError)
+ it "only accept false or a number for backup" do
+ expect { resource.backup true }.to raise_error(ArgumentError)
+ expect { resource.backup false }.not_to raise_error
+ expect { resource.backup 10 }.not_to raise_error
+ expect { resource.backup "blues" }.to raise_error(ArgumentError)
end
- it "should accept a sha256 for checksum" do
- expect { @resource.checksum "0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa" }.not_to raise_error
- expect { @resource.checksum "monkey!" }.to raise_error(ArgumentError)
+ it "accepts a sha256 for checksum" do
+ expect { resource.checksum "0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa" }.not_to raise_error
+ expect { resource.checksum "monkey!" }.to raise_error(ArgumentError)
end
- it "should accept create, delete or touch for action" do
- expect { @resource.action :create }.not_to raise_error
- expect { @resource.action :delete }.not_to raise_error
- expect { @resource.action :touch }.not_to raise_error
- expect { @resource.action :blues }.to raise_error(ArgumentError)
+ it "accepts create, delete or touch for action" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :touch }.not_to raise_error
+ expect { resource.action :blues }.to raise_error(ArgumentError)
end
- it "should accept a block, symbol, or string for verify" do
- expect { @resource.verify {} }.not_to raise_error
- expect { @resource.verify "" }.not_to raise_error
- expect { @resource.verify :json }.not_to raise_error
- expect { @resource.verify true }.to raise_error(ArgumentError)
- expect { @resource.verify false }.to raise_error(ArgumentError)
+ it "accepts a block, symbol, or string for verify" do
+ expect { resource.verify {} }.not_to raise_error
+ expect { resource.verify "" }.not_to raise_error
+ expect { resource.verify :json }.not_to raise_error
+ expect { resource.verify true }.to raise_error(ArgumentError)
+ expect { resource.verify false }.to raise_error(ArgumentError)
end
- it "should accept multiple verify statements" do
- @resource.verify "foo"
- @resource.verify "bar"
- @resource.verify.length == 2
+ it "accepts multiple verify statements" do
+ resource.verify "foo"
+ resource.verify "bar"
+ resource.verify.length == 2
end
- it "should use the object name as the path by default" do
- expect(@resource.path).to eql("fakey_fakerton")
+ it "uses the object name as the path by default" do
+ expect(resource.path).to eql("fakey_fakerton")
end
- it "should accept a string as the path" do
- expect { @resource.path "/tmp" }.not_to raise_error
- expect(@resource.path).to eql("/tmp")
- expect { @resource.path Hash.new }.to raise_error(ArgumentError)
+ it "accepts a string as the path" do
+ expect { resource.path "/tmp" }.not_to raise_error
+ expect(resource.path).to eql("/tmp")
+ expect { resource.path Hash.new }.to raise_error(ArgumentError)
end
describe "when it has a path, owner, group, mode, and checksum" do
before do
- @resource.path("/tmp/foo.txt")
- @resource.owner("root")
- @resource.group("wheel")
- @resource.mode("0644")
- @resource.checksum("1" * 64)
+ resource.path("/tmp/foo.txt")
+ resource.owner("root")
+ resource.group("wheel")
+ resource.mode("0644")
+ resource.checksum("1" * 64)
end
context "on unix", :unix_only do
it "describes its state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:owner]).to eq("root")
expect(state[:group]).to eq("wheel")
expect(state[:mode]).to eq("0644")
@@ -110,21 +108,20 @@ describe Chef::Resource::File do
end
it "returns the file path as its identity" do
- expect(@resource.identity).to eq("/tmp/foo.txt")
+ expect(resource.identity).to eq("/tmp/foo.txt")
end
end
describe "when access controls are set on windows", :windows_only => true do
before do
- @resource.rights :read, "Everyone"
- @resource.rights :full_control, "DOMAIN\User"
+ resource.rights :read, "Everyone"
+ resource.rights :full_control, "DOMAIN\User"
end
it "describes its state including windows ACL attributes" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:rights]).to eq([ { :permissions => :read, :principals => "Everyone" },
{ :permissions => :full_control, :principals => "DOMAIN\User" } ])
end
end
-
end
diff --git a/spec/unit/resource/freebsd_package_spec.rb b/spec/unit/resource/freebsd_package_spec.rb
index 0842114c47..4a223eb507 100644
--- a/spec/unit/resource/freebsd_package_spec.rb
+++ b/spec/unit/resource/freebsd_package_spec.rb
@@ -22,64 +22,63 @@ require "spec_helper"
require "ostruct"
describe Chef::Resource::FreebsdPackage do
- before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @resource = Chef::Resource::FreebsdPackage.new("foo", @run_context)
- end
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::FreebsdPackage.new("foo", run_context) }
describe "Initialization" do
- it "should return a Chef::Resource::FreebsdPackage" do
- expect(@resource).to be_a_kind_of(Chef::Resource::FreebsdPackage)
+
+ it "is a subclass of Chef::Resource::Package" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Package)
end
- it "should set the resource_name to :freebsd_package" do
- expect(@resource.resource_name).to eql(:freebsd_package)
+ it "sets the resource_name to :freebsd_package" do
+ expect(resource.resource_name).to eql(:freebsd_package)
end
- it "should not set the provider" do
- expect(@resource.provider).to be_nil
+ it "does not set the provider" do
+ expect(resource.provider).to be_nil
end
end
describe "Assigning provider after creation" do
describe "if ports specified as source" do
- it "should be Freebsd::Port" do
- @resource.source("ports")
- @resource.after_created
- expect(@resource.provider).to eq(Chef::Provider::Package::Freebsd::Port)
+ it "is Freebsd::Port" do
+ resource.source("ports")
+ resource.after_created
+ expect(resource.provider).to eq(Chef::Provider::Package::Freebsd::Port)
end
end
- describe "if __Freebsd_version is greater than or equal to 1000017" do
- it "should be Freebsd::Pkgng" do
- [1000017, 1000018, 1000500, 1001001, 1100000].each do |__freebsd_version|
- @node.automatic_attrs[:os_version] = __freebsd_version
- @resource.after_created
- expect(@resource.provider).to eq(Chef::Provider::Package::Freebsd::Pkgng)
+ describe "if freebsd_version is greater than or equal to 1000017" do
+ it "is Freebsd::Pkgng" do
+ [1000017, 1000018, 1000500, 1001001, 1100000].each do |freebsd_version|
+ node.automatic_attrs[:os_version] = freebsd_version
+ resource.after_created
+ expect(resource.provider).to eq(Chef::Provider::Package::Freebsd::Pkgng)
end
end
end
describe "if pkgng enabled" do
- it "should be Freebsd::Pkgng" do
+ it "is Freebsd::Pkgng" do
pkg_enabled = OpenStruct.new(:stdout => "yes\n")
- allow(@resource).to receive(:shell_out!).with("make -V WITH_PKGNG", :env => nil).and_return(pkg_enabled)
- @resource.after_created
- expect(@resource.provider).to eq(Chef::Provider::Package::Freebsd::Pkgng)
+ allow(resource).to receive(:shell_out!).with("make", "-V", "WITH_PKGNG", :env => nil).and_return(pkg_enabled)
+ resource.after_created
+ expect(resource.provider).to eq(Chef::Provider::Package::Freebsd::Pkgng)
end
end
- describe "if __Freebsd_version is less than 1000017 and pkgng not enabled" do
- it "should be Freebsd::Pkg" do
+ describe "if freebsd_version is less than 1000017 and pkgng not enabled" do
+ it "is Freebsd::Pkg" do
pkg_enabled = OpenStruct.new(:stdout => "\n")
- allow(@resource).to receive(:shell_out!).with("make -V WITH_PKGNG", :env => nil).and_return(pkg_enabled)
+ allow(resource).to receive(:shell_out!).with("make", "-V", "WITH_PKGNG", :env => nil).and_return(pkg_enabled)
- [1000016, 1000000, 901503, 902506, 802511].each do |__freebsd_version|
- @node.automatic_attrs[:os_version] = __freebsd_version
- @resource.after_created
- expect(@resource.provider).to eq(Chef::Provider::Package::Freebsd::Pkg)
+ [1000016, 1000000, 901503, 902506, 802511].each do |freebsd_version|
+ node.automatic_attrs[:os_version] = freebsd_version
+ resource.after_created
+ expect(resource.provider).to eq(Chef::Provider::Package::Freebsd::Pkg)
end
end
end
diff --git a/spec/unit/resource/gem_package_spec.rb b/spec/unit/resource/gem_package_spec.rb
index a1571ab9bb..bee7ba9de4 100644
--- a/spec/unit/resource/gem_package_spec.rb
+++ b/spec/unit/resource/gem_package_spec.rb
@@ -31,12 +31,10 @@ describe Chef::Resource::GemPackage, "initialize" do
end
describe Chef::Resource::GemPackage, "gem_binary" do
- before(:each) do
- @resource = Chef::Resource::GemPackage.new("foo")
- end
+ let(:resource) { Chef::Resource::GemPackage.new("foo") }
- it "should set the gem_binary variable to whatever is passed in" do
- @resource.gem_binary("/opt/local/bin/gem")
- expect(@resource.gem_binary).to eql("/opt/local/bin/gem")
+ it "sets the gem_binary variable to whatever is passed in" do
+ resource.gem_binary("/opt/local/bin/gem")
+ expect(resource.gem_binary).to eql("/opt/local/bin/gem")
end
end
diff --git a/spec/unit/resource/git_spec.rb b/spec/unit/resource/git_spec.rb
index 15c1e54f25..1883595617 100644
--- a/spec/unit/resource/git_spec.rb
+++ b/spec/unit/resource/git_spec.rb
@@ -28,23 +28,20 @@ describe Chef::Resource::Git do
action: :sync
)
- before(:each) do
- @git = Chef::Resource::Git.new("my awesome webapp")
- end
+ let(:resource) { Chef::Resource::Git.new("my awesome webapp") }
- it "is a kind of Scm Resource" do
- expect(@git).to be_a_kind_of(Chef::Resource::Scm)
- expect(@git).to be_an_instance_of(Chef::Resource::Git)
+ it "is a subclass of Chef::Resource::Scm" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Scm)
end
it "uses aliases revision as branch" do
- @git.branch "HEAD"
- expect(@git.revision).to eql("HEAD")
+ resource.branch "HEAD"
+ expect(resource.revision).to eql("HEAD")
end
it "aliases revision as reference" do
- @git.reference "v1.0 tag"
- expect(@git.revision).to eql("v1.0 tag")
+ resource.reference "v1.0 tag"
+ expect(resource.revision).to eql("v1.0 tag")
end
end
diff --git a/spec/unit/resource/group_spec.rb b/spec/unit/resource/group_spec.rb
index 9d9b5c1111..e850361beb 100644
--- a/spec/unit/resource/group_spec.rb
+++ b/spec/unit/resource/group_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: AJ Christensen (<aj@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>);
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,143 +20,128 @@
require "spec_helper"
describe Chef::Resource::Group, "initialize" do
- before(:each) do
- @resource = Chef::Resource::Group.new("admin")
- end
-
- it "should create a new Chef::Resource::Group" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Group)
- end
+ let(:resource) { Chef::Resource::Group.new("admin") }
- it "should set the resource_name to :group" do
- expect(@resource.resource_name).to eql(:group)
+ it "sets the resource_name to :group" do
+ expect(resource.resource_name).to eql(:group)
end
- it "should set the group_name equal to the argument to initialize" do
- expect(@resource.group_name).to eql("admin")
+ it "sets the group_name equal to the argument to initialize" do
+ expect(resource.group_name).to eql("admin")
end
- it "should default gid to nil" do
- expect(@resource.gid).to eql(nil)
+ it "defaults gid to nil" do
+ expect(resource.gid).to eql(nil)
end
- it "should default members to an empty array" do
- expect(@resource.members).to eql([])
+ it "defaults members to an empty array" do
+ expect(resource.members).to eql([])
end
- it "should alias users to members, also an empty array" do
- expect(@resource.users).to eql([])
+ it "aliases users to members, also an empty array" do
+ expect(resource.users).to eql([])
end
- it "should set action to :create" do
- expect(@resource.action).to eql([:create])
+ it "sets action to :create" do
+ expect(resource.action).to eql([:create])
end
%w{create remove modify manage}.each do |action|
- it "should allow action #{action}" do
- expect(@resource.allowed_actions.detect { |a| a == action.to_sym }).to eql(action.to_sym)
+ it "allows action #{action}" do
+ expect(resource.allowed_actions.detect { |a| a == action.to_sym }).to eql(action.to_sym)
end
end
- it "should accept domain groups (@ or \ separator) on non-windows" do
- expect { @resource.group_name "domain\@group" }.not_to raise_error
- expect(@resource.group_name).to eq("domain\@group")
- expect { @resource.group_name "domain\\group" }.not_to raise_error
- expect(@resource.group_name).to eq("domain\\group")
- expect { @resource.group_name "domain\\group^name" }.not_to raise_error
- expect(@resource.group_name).to eq("domain\\group^name")
+ it "accepts domain groups (@ or \ separator) on non-windows" do
+ expect { resource.group_name "domain\@group" }.not_to raise_error
+ expect(resource.group_name).to eq("domain\@group")
+ expect { resource.group_name "domain\\group" }.not_to raise_error
+ expect(resource.group_name).to eq("domain\\group")
+ expect { resource.group_name "domain\\group^name" }.not_to raise_error
+ expect(resource.group_name).to eq("domain\\group^name")
end
end
describe Chef::Resource::Group, "group_name" do
- before(:each) do
- @resource = Chef::Resource::Group.new("admin")
- end
+ let(:resource) { Chef::Resource::Group.new("admin") }
- it "should allow a string" do
- @resource.group_name "pirates"
- expect(@resource.group_name).to eql("pirates")
+ it "allows a string" do
+ resource.group_name "pirates"
+ expect(resource.group_name).to eql("pirates")
end
- it "should not allow a hash" do
- expect { @resource.send(:group_name, { :aj => "is freakin awesome" }) }.to raise_error(ArgumentError)
+ it "does not allow a hash" do
+ expect { resource.send(:group_name, { :aj => "is freakin awesome" }) }.to raise_error(ArgumentError)
end
end
describe Chef::Resource::Group, "gid" do
- before(:each) do
- @resource = Chef::Resource::Group.new("admin")
- end
+ let(:resource) { Chef::Resource::Group.new("admin") }
- it "should allow an integer" do
- @resource.gid 100
- expect(@resource.gid).to eql(100)
+ it "allows an integer" do
+ resource.gid 100
+ expect(resource.gid).to eql(100)
end
- it "should not allow a hash" do
- expect { @resource.send(:gid, { :aj => "is freakin awesome" }) }.to raise_error(ArgumentError)
+ it "does not allow a hash" do
+ expect { resource.send(:gid, { :aj => "is freakin awesome" }) }.to raise_error(ArgumentError)
end
end
describe Chef::Resource::Group, "members" do
- before(:each) do
- @resource = Chef::Resource::Group.new("admin")
- end
+ let(:resource) { Chef::Resource::Group.new("admin") }
[ :users, :members].each do |method|
- it "(#{method}) should allow and convert a string" do
- @resource.send(method, "aj")
- expect(@resource.send(method)).to eql(["aj"])
+ it "(#{method}) allows and convert a string" do
+ resource.send(method, "aj")
+ expect(resource.send(method)).to eql(["aj"])
end
it "(#{method}) should split a string on commas" do
- @resource.send(method, "aj,adam")
- expect(@resource.send(method)).to eql( %w{aj adam} )
+ resource.send(method, "aj,adam")
+ expect(resource.send(method)).to eql( %w{aj adam} )
end
- it "(#{method}) should allow an array" do
- @resource.send(method, %w{aj adam})
- expect(@resource.send(method)).to eql( %w{aj adam} )
+ it "(#{method}) allows an array" do
+ resource.send(method, %w{aj adam})
+ expect(resource.send(method)).to eql( %w{aj adam} )
end
- it "(#{method}) should not allow a hash" do
- expect { @resource.send(method, { :aj => "is freakin awesome" }) }.to raise_error(ArgumentError)
+ it "(#{method}) does not allow a hash" do
+ expect { resource.send(method, { :aj => "is freakin awesome" }) }.to raise_error(ArgumentError)
end
end
end
describe Chef::Resource::Group, "append" do
- before(:each) do
- @resource = Chef::Resource::Group.new("admin")
- end
+ let(:resource) { Chef::Resource::Group.new("admin") }
- it "should default to false" do
- expect(@resource.append).to eql(false)
+ it "defaults to false" do
+ expect(resource.append).to eql(false)
end
- it "should allow a boolean" do
- @resource.append true
- expect(@resource.append).to eql(true)
+ it "allows a boolean" do
+ resource.append true
+ expect(resource.append).to eql(true)
end
- it "should not allow a hash" do
- expect { @resource.send(:gid, { :aj => "is freakin awesome" }) }.to raise_error(ArgumentError)
+ it "does not allow a hash" do
+ expect { resource.send(:gid, { :aj => "is freakin awesome" }) }.to raise_error(ArgumentError)
end
describe "when it has members" do
before do
- @resource.group_name("pokemon")
- @resource.members(%w{blastoise pikachu})
+ resource.group_name("pokemon")
+ resource.members(%w{blastoise pikachu})
end
it "describes its state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:members]).to eql(%w{blastoise pikachu})
end
it "returns the group name as its identity" do
- expect(@resource.identity).to eq("pokemon")
+ expect(resource.identity).to eq("pokemon")
end
end
end
diff --git a/spec/unit/resource/homebrew_cask_spec.rb b/spec/unit/resource/homebrew_cask_spec.rb
new file mode 100644
index 0000000000..9b04a0328d
--- /dev/null
+++ b/spec/unit/resource/homebrew_cask_spec.rb
@@ -0,0 +1,35 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::HomebrewCask do
+
+ let(:resource) { Chef::Resource::HomebrewCask.new("myapp") }
+
+ it "has a resource name of :homebrew_cask" do
+ expect(resource.resource_name).to eql(:homebrew_cask)
+ end
+
+ it "has a default action of install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "the cask_name property is the name property" do
+ expect(resource.cask_name).to eql("myapp")
+ end
+end
diff --git a/spec/unit/resource/homebrew_package_spec.rb b/spec/unit/resource/homebrew_package_spec.rb
index cfcfcd9c3a..c3383a5abc 100644
--- a/spec/unit/resource/homebrew_package_spec.rb
+++ b/spec/unit/resource/homebrew_package_spec.rb
@@ -30,6 +30,10 @@ describe Chef::Resource::HomebrewPackage, "initialize" do
let(:resource) { Chef::Resource::HomebrewPackage.new("emacs") }
+ it "is a subclass of Chef::Resource::Package" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Package)
+ end
+
shared_examples "home_brew user set and returned" do
it "returns the configured homebrew_user" do
resource.homebrew_user user
diff --git a/spec/unit/resource/homebrew_tap_spec.rb b/spec/unit/resource/homebrew_tap_spec.rb
new file mode 100644
index 0000000000..2b93c11c28
--- /dev/null
+++ b/spec/unit/resource/homebrew_tap_spec.rb
@@ -0,0 +1,39 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::HomebrewTap do
+
+ let(:resource) { Chef::Resource::HomebrewTap.new("user/mytap") }
+
+ it "has a resource name of :homebrew_tap" do
+ expect(resource.resource_name).to eql(:homebrew_tap)
+ end
+
+ it "has a default action of tap" do
+ expect(resource.action).to eql([:tap])
+ end
+
+ it "the tap_name property is the name property" do
+ expect(resource.tap_name).to eql("user/mytap")
+ end
+
+ it "fails if tap_name isn't in the USER/TAP format" do
+ expect { resource.tap_name "mytap" }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/hostname_spec.rb b/spec/unit/resource/hostname_spec.rb
new file mode 100644
index 0000000000..33f944dbc9
--- /dev/null
+++ b/spec/unit/resource/hostname_spec.rb
@@ -0,0 +1,43 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::Hostname do
+
+ let(:resource) { Chef::Resource::Hostname.new("foo") }
+
+ it "has a resource name of :hostname" do
+ expect(resource.resource_name).to eql(:hostname)
+ end
+
+ it "has a default action of set" do
+ expect(resource.action).to eql([:set])
+ end
+
+ it "runs at compile_time by default" do
+ expect(resource.compile_time).to eql(true)
+ end
+
+ it "reboots windows nodes by default" do
+ expect(resource.windows_reboot).to eql(true)
+ end
+
+ it "the hostname property is the name property" do
+ expect(resource.hostname).to eql("foo")
+ end
+end
diff --git a/spec/unit/resource/http_request_spec.rb b/spec/unit/resource/http_request_spec.rb
index 318a154b88..92c84962d7 100644
--- a/spec/unit/resource/http_request_spec.rb
+++ b/spec/unit/resource/http_request_spec.rb
@@ -20,39 +20,31 @@
require "spec_helper"
describe Chef::Resource::HttpRequest do
+ let(:resource) { Chef::Resource::HttpRequest.new("fakey_fakerton") }
- before(:each) do
- @resource = Chef::Resource::HttpRequest.new("fakey_fakerton")
+ it "sets url to a string" do
+ resource.url "http://slashdot.org"
+ expect(resource.url).to eql("http://slashdot.org")
end
- it "should create a new Chef::Resource::HttpRequest" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::HttpRequest)
+ it "sets the message to the name by default" do
+ expect(resource.message).to eql("fakey_fakerton")
end
- it "should set url to a string" do
- @resource.url "http://slashdot.org"
- expect(@resource.url).to eql("http://slashdot.org")
- end
-
- it "should set the message to the name by default" do
- expect(@resource.message).to eql("fakey_fakerton")
- end
-
- it "should set message to a string" do
- @resource.message "monkeybars"
- expect(@resource.message).to eql("monkeybars")
+ it "sets message to a string" do
+ resource.message "monkeybars"
+ expect(resource.message).to eql("monkeybars")
end
describe "when it has a message and headers" do
before do
- @resource.url("http://www.trololol.net")
- @resource.message("Get sum post brah.")
- @resource.headers({ "head" => "tail" })
+ resource.url("http://www.trololol.net")
+ resource.message("Get sum post brah.")
+ resource.headers({ "head" => "tail" })
end
it "returns the url as its identity" do
- expect(@resource.identity).to eq("http://www.trololol.net")
+ expect(resource.identity).to eq("http://www.trololol.net")
end
end
diff --git a/spec/unit/resource/ifconfig_spec.rb b/spec/unit/resource/ifconfig_spec.rb
index eceba0c319..3221d0b864 100644
--- a/spec/unit/resource/ifconfig_spec.rb
+++ b/spec/unit/resource/ifconfig_spec.rb
@@ -37,7 +37,7 @@ describe Chef::Resource::Ifconfig do
end
it "describes its state" do
- state = @resource.state
+ state = @resource.state_for_resource_reporter
expect(state[:inet_addr]).to eq("434.2343.23")
expect(state[:mask]).to eq("255.255.545")
end
@@ -55,7 +55,7 @@ describe Chef::Resource::Ifconfig do
@node.automatic_attrs[:platform_version] = version
end
- it "should use an ordinary Provider::Ifconfig as a provider for #{platform} #{version}" do
+ it "uses an ordinary Provider::Ifconfig as a provider for #{platform} #{version}" do
expect(@resource.provider_for_action(:add).class).to eq(Chef::Provider::Ifconfig)
end
end
@@ -68,7 +68,7 @@ describe Chef::Resource::Ifconfig do
@node.automatic_attrs[:platform_version] = version
end
- it "should use an Provider::Ifconfig::Redhat as a provider for #{platform} #{version}" do
+ it "uses an Provider::Ifconfig::Redhat as a provider for #{platform} #{version}" do
expect(@resource.provider_for_action(:add)).to be_a_kind_of(Chef::Provider::Ifconfig::Redhat)
end
end
@@ -81,7 +81,7 @@ describe Chef::Resource::Ifconfig do
@node.automatic_attrs[:platform_version] = version
end
- it "should use an Ifconfig::Debian as a provider for #{platform} #{version}" do
+ it "uses an Ifconfig::Debian as a provider for #{platform} #{version}" do
expect(@resource.provider_for_action(:add)).to be_a_kind_of(Chef::Provider::Ifconfig::Debian)
end
end
diff --git a/spec/unit/resource/ips_package_spec.rb b/spec/unit/resource/ips_package_spec.rb
index fd1fe2840c..b19f6bdf58 100644
--- a/spec/unit/resource/ips_package_spec.rb
+++ b/spec/unit/resource/ips_package_spec.rb
@@ -29,12 +29,14 @@ describe Chef::Resource::IpsPackage, "initialize" do
os: "solaris2"
)
- before(:each) do
- @resource = Chef::Resource::IpsPackage.new("crypto/gnupg")
+ let(:resource) { Chef::Resource::IpsPackage.new("crypto/gnupg") }
+
+ it "is a subclass of Chef::Resource::Package" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Package)
end
it "should support accept_license" do
- @resource.accept_license(true)
- expect(@resource.accept_license).to eql(true)
+ resource.accept_license(true)
+ expect(resource.accept_license).to eql(true)
end
end
diff --git a/spec/unit/resource/ksh_spec.rb b/spec/unit/resource/ksh_spec.rb
index 6c3ba291b4..7b651abacc 100644
--- a/spec/unit/resource/ksh_spec.rb
+++ b/spec/unit/resource/ksh_spec.rb
@@ -20,21 +20,18 @@ require "spec_helper"
describe Chef::Resource::Ksh do
- before(:each) do
- @resource = Chef::Resource::Ksh.new("fakey_fakerton")
- end
+ let(:resource) { Chef::Resource::Ksh.new("fakey_fakerton") }
- it "should create a new Chef::Resource::Ksh" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Ksh)
+ it "is a subclass of Chef::Resource::Script" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Script)
end
- it "should have a resource name of :ksh" do
- expect(@resource.resource_name).to eql(:ksh)
+ it "has a resource name of :ksh" do
+ expect(resource.resource_name).to eql(:ksh)
end
- it "should have an interpreter of ksh" do
- expect(@resource.interpreter).to eql("ksh")
+ it "has an interpreter of ksh" do
+ expect(resource.interpreter).to eql("ksh")
end
end
diff --git a/spec/unit/resource/launchd_spec.rb b/spec/unit/resource/launchd_spec.rb
index 95febc47cf..1fcfc25e3b 100644
--- a/spec/unit/resource/launchd_spec.rb
+++ b/spec/unit/resource/launchd_spec.rb
@@ -1,28 +1,31 @@
#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
require "spec_helper"
describe Chef::Resource::Launchd do
- @launchd = Chef::Resource::Launchd.new("io.chef.chef-client")
- let(:resource) { Chef::Resource::Launchd.new(
- "io.chef.chef-client",
- run_context
- )}
-
- it "should create a new Chef::Resource::Launchd" do
- expect(resource).to be_a_kind_of(Chef::Resource)
- expect(resource).to be_a_kind_of(Chef::Resource::Launchd)
- end
+ let(:resource) { Chef::Resource::Launchd.new("io.chef.chef-client" ) }
- it "should have a resource name of Launchd" do
+ it "has a resource name of Launchd" do
expect(resource.resource_name).to eql(:launchd)
end
- it "should have a default action of create" do
+ it "has a default action of create" do
expect(resource.action).to eql([:create])
end
- it "should accept enable, disable, create, and delete as actions" do
+ it "accepts enable, disable, create, and delete as actions" do
expect { resource.action :enable }.not_to raise_error
expect { resource.action :disable }.not_to raise_error
expect { resource.action :create }.not_to raise_error
diff --git a/spec/unit/resource/link_spec.rb b/spec/unit/resource/link_spec.rb
index bd0976d8ea..4f4f8c7c9f 100644
--- a/spec/unit/resource/link_spec.rb
+++ b/spec/unit/resource/link_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,115 +20,110 @@
require "spec_helper"
describe Chef::Resource::Link do
+ let(:resource) { Chef::Resource::Link.new("fakey_fakerton") }
before(:each) do
expect_any_instance_of(Chef::Resource::Link).to receive(:verify_links_supported!).and_return(true)
- @resource = Chef::Resource::Link.new("fakey_fakerton")
end
- it "should create a new Chef::Resource::Link" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Link)
+ it "has a name property" do
+ expect(resource.name).to eql("fakey_fakerton")
end
- it "should have a name" do
- expect(@resource.name).to eql("fakey_fakerton")
- end
-
- it "should have a default action of 'create'" do
- expect(@resource.action).to eql([:create])
+ it "has a default action of 'create'" do
+ expect(resource.action).to eql([:create])
end
{ :create => false, :delete => false, :blues => true }.each do |action, bad_value|
it "should #{bad_value ? 'not' : ''} accept #{action}" do
if bad_value
- expect { @resource.action action }.to raise_error(ArgumentError)
+ expect { resource.action action }.to raise_error(ArgumentError)
else
- expect { @resource.action action }.not_to raise_error
+ expect { resource.action action }.not_to raise_error
end
end
end
- it "should use the object name as the target_file by default" do
- expect(@resource.target_file).to eql("fakey_fakerton")
+ it "uses the object name as the target_file by default" do
+ expect(resource.target_file).to eql("fakey_fakerton")
end
- it "should accept a delayed evaluator as the target path" do
- @resource.target_file Chef::DelayedEvaluator.new { "my_lazy_name" }
- expect(@resource.target_file).to eql("my_lazy_name")
+ it "accepts a delayed evaluator as the target path" do
+ resource.target_file Chef::DelayedEvaluator.new { "my_lazy_name" }
+ expect(resource.target_file).to eql("my_lazy_name")
end
- it "should accept a delayed evaluator when accessing via 'path'" do
- @resource.target_file Chef::DelayedEvaluator.new { "my_lazy_name" }
- expect(@resource.path).to eql("my_lazy_name")
+ it "accepts a delayed evaluator when accessing via 'path'" do
+ resource.target_file Chef::DelayedEvaluator.new { "my_lazy_name" }
+ expect(resource.path).to eql("my_lazy_name")
end
- it "should accept a delayed evaluator via 'to'" do
- @resource.to Chef::DelayedEvaluator.new { "my_lazy_name" }
- expect(@resource.to).to eql("my_lazy_name")
+ it "accepts a delayed evaluator via 'to'" do
+ resource.to Chef::DelayedEvaluator.new { "my_lazy_name" }
+ expect(resource.to).to eql("my_lazy_name")
end
- it "should accept a string as the link source via 'to'" do
- expect { @resource.to "/tmp" }.not_to raise_error
+ it "accepts a string as the link source via 'to'" do
+ expect { resource.to "/tmp" }.not_to raise_error
end
- it "should not accept a Hash for the link source via 'to'" do
- expect { @resource.to Hash.new }.to raise_error(ArgumentError)
+ it "does not accept a Hash for the link source via 'to'" do
+ expect { resource.to Hash.new }.to raise_error(ArgumentError)
end
- it "should allow you to set a link source via 'to'" do
- @resource.to "/tmp/foo"
- expect(@resource.to).to eql("/tmp/foo")
+ it "allows you to set a link source via 'to'" do
+ resource.to "/tmp/foo"
+ expect(resource.to).to eql("/tmp/foo")
end
- it "should allow you to specify the link type" do
- @resource.link_type "symbolic"
- expect(@resource.link_type).to eql(:symbolic)
+ it "allows you to specify the link type" do
+ resource.link_type "symbolic"
+ expect(resource.link_type).to eql(:symbolic)
end
- it "should default to a symbolic link" do
- expect(@resource.link_type).to eql(:symbolic)
+ it "defaults to a symbolic link" do
+ expect(resource.link_type).to eql(:symbolic)
end
- it "should accept a hard link_type" do
- @resource.link_type :hard
- expect(@resource.link_type).to eql(:hard)
+ it "accepts a hard link_type" do
+ resource.link_type :hard
+ expect(resource.link_type).to eql(:hard)
end
- it "should reject any other link_type but :hard and :symbolic" do
- expect { @resource.link_type "x-men" }.to raise_error(ArgumentError)
+ it "rejects any other link_type but :hard and :symbolic" do
+ expect { resource.link_type "x-men" }.to raise_error(ArgumentError)
end
- it "should accept a group name or id for group" do
- expect { @resource.group "root" }.not_to raise_error
- expect { @resource.group 123 }.not_to raise_error
- expect { @resource.group "root:goo" }.to raise_error(ArgumentError)
+ it "accepts a group name or id for group" do
+ expect { resource.group "root" }.not_to raise_error
+ expect { resource.group 123 }.not_to raise_error
+ expect { resource.group "root:goo" }.to raise_error(ArgumentError)
end
- it "should accept a user name or id for owner" do
- expect { @resource.owner "root" }.not_to raise_error
- expect { @resource.owner 123 }.not_to raise_error
- expect { @resource.owner "root:goo" }.to raise_error(ArgumentError)
+ it "accepts a user name or id for owner" do
+ expect { resource.owner "root" }.not_to raise_error
+ expect { resource.owner 123 }.not_to raise_error
+ expect { resource.owner "root:goo" }.to raise_error(ArgumentError)
end
describe "when it has to, link_type, owner, and group" do
before do
- @resource.target_file("/var/target.tar")
- @resource.to("/to/dir/file.tar")
- @resource.link_type(:symbolic)
- @resource.owner("root")
- @resource.group("0664")
+ resource.target_file("/var/target.tar")
+ resource.to("/to/dir/file.tar")
+ resource.link_type(:symbolic)
+ resource.owner("root")
+ resource.group("0664")
end
it "describes its state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:to]).to eq("/to/dir/file.tar")
expect(state[:owner]).to eq("root")
expect(state[:group]).to eq("0664")
end
it "returns the target file as its identity" do
- expect(@resource.identity).to eq("/var/target.tar")
+ expect(resource.identity).to eq("/var/target.tar")
end
end
end
diff --git a/spec/unit/resource/log_spec.rb b/spec/unit/resource/log_spec.rb
index 18a1eb65bf..9646b23b53 100644
--- a/spec/unit/resource/log_spec.rb
+++ b/spec/unit/resource/log_spec.rb
@@ -21,53 +21,44 @@ require "spec_helper"
describe Chef::Resource::Log do
- before(:each) do
- @log_str = "this is my string to log"
- @resource = Chef::Resource::Log.new(@log_str)
- end
-
- it "should create a new Chef::Resource::Log" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Log)
- end
+ let(:log_str) { "this is my string to log" }
+ let(:resource) { Chef::Resource::Log.new(log_str) }
it "supports the :write actions" do
- expect(@resource.allowed_actions).to include(:write)
+ expect(resource.allowed_actions).to include(:write)
end
- it "should have a name of log" do
- expect(@resource.resource_name).to eq(:log)
+ it "has a name of log" do
+ expect(resource.resource_name).to eq(:log)
end
- it "should allow you to set a log string" do
- expect(@resource.name).to eq(@log_str)
+ it "allows you to set a log string" do
+ expect(resource.name).to eq(log_str)
end
- it "should set the message to the first argument to new" do
- expect(@resource.message).to eq(@log_str)
+ it "sets the message to the first argument to new" do
+ expect(resource.message).to eq(log_str)
end
- it "should accept a string for the log message" do
- @resource.message "this is different"
- expect(@resource.message).to eq("this is different")
+ it "accepts a string for the log message" do
+ resource.message "this is different"
+ expect(resource.message).to eq("this is different")
end
- it "should accept a vaild level option" do
- @resource.level :debug
- @resource.level :info
- @resource.level :warn
- @resource.level :error
- @resource.level :fatal
- expect { @resource.level :unsupported }.to raise_error(ArgumentError)
+ it "accepts a vaild level option" do
+ resource.level :debug
+ resource.level :info
+ resource.level :warn
+ resource.level :error
+ resource.level :fatal
+ expect { resource.level :unsupported }.to raise_error(ArgumentError)
end
describe "when the identity is defined" do
- before do
- @resource = Chef::Resource::Log.new("ery day I'm loggin-in")
- end
+ let(:resource) { Chef::Resource::Log.new("ery day I'm loggin-in") }
it "returns the log string as its identity" do
- expect(@resource.identity).to eq("ery day I'm loggin-in")
+ expect(resource.identity).to eq("ery day I'm loggin-in")
end
end
end
diff --git a/spec/unit/resource/timestamped_deploy_spec.rb b/spec/unit/resource/macos_user_defaults_spec.rb
index 5a2dc8ae02..8787cd0b9a 100644
--- a/spec/unit/resource/timestamped_deploy_spec.rb
+++ b/spec/unit/resource/macos_user_defaults_spec.rb
@@ -1,6 +1,5 @@
#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2009-2016, Daniel DeLeo
+# Copyright:: Copyright 2018, Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,15 +17,15 @@
require "spec_helper"
-describe Chef::Resource::TimestampedDeploy, "initialize" do
+describe Chef::Resource::MacosUserDefaults do
- static_provider_resolution(
- resource: Chef::Resource::TimestampedDeploy,
- provider: Chef::Provider::Deploy::Timestamped,
- name: :timestamped_deploy,
- action: :deploy,
- os: "linux",
- platform_family: "rhel"
- )
+ let(:resource) { Chef::Resource::MacosUserDefaults.new("foo") }
+ it "has a resource name of :macos_userdefaults" do
+ expect(resource.resource_name).to eql(:macos_userdefaults)
+ end
+
+ it "has a default action of install" do
+ expect(resource.action).to eql([:write])
+ end
end
diff --git a/spec/unit/resource/mdadm_spec.rb b/spec/unit/resource/mdadm_spec.rb
index fe9acf807b..6d595dc0f8 100644
--- a/spec/unit/resource/mdadm_spec.rb
+++ b/spec/unit/resource/mdadm_spec.rb
@@ -21,86 +21,79 @@ require "spec_helper"
describe Chef::Resource::Mdadm do
- before(:each) do
- @resource = Chef::Resource::Mdadm.new("fakey_fakerton")
- end
-
- it "should create a new Chef::Resource::Mdadm" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Mdadm)
- end
+ let(:resource) { Chef::Resource::Mdadm.new("fakey_fakerton") }
- it "should have a resource name of :mdadm" do
- expect(@resource.resource_name).to eql(:mdadm)
+ it "has a resource name of :mdadm" do
+ expect(resource.resource_name).to eql(:mdadm)
end
- it "should have a default action of create" do
- expect(@resource.action).to eql([:create])
+ it "has a default action of create" do
+ expect(resource.action).to eql([:create])
end
- it "should accept create, assemble, stop as actions" do
- expect { @resource.action :create }.not_to raise_error
- expect { @resource.action :assemble }.not_to raise_error
- expect { @resource.action :stop }.not_to raise_error
+ it "accepts create, assemble, stop as actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :assemble }.not_to raise_error
+ expect { resource.action :stop }.not_to raise_error
end
- it "should allow you to set the raid_device attribute" do
- @resource.raid_device "/dev/md3"
- expect(@resource.raid_device).to eql("/dev/md3")
+ it "allows you to set the raid_device attribute" do
+ resource.raid_device "/dev/md3"
+ expect(resource.raid_device).to eql("/dev/md3")
end
- it "should allow you to set the chunk attribute" do
- @resource.chunk 256
- expect(@resource.chunk).to eql(256)
+ it "allows you to set the chunk attribute" do
+ resource.chunk 256
+ expect(resource.chunk).to eql(256)
end
- it "should allow you to set the level attribute" do
- @resource.level 1
- expect(@resource.level).to eql(1)
+ it "allows you to set the level attribute" do
+ resource.level 1
+ expect(resource.level).to eql(1)
end
- it "should allow you to set the metadata attribute" do
- @resource.metadata "1.2"
- expect(@resource.metadata).to eql("1.2")
+ it "allows you to set the metadata attribute" do
+ resource.metadata "1.2"
+ expect(resource.metadata).to eql("1.2")
end
- it "should allow you to set the bitmap attribute" do
- @resource.bitmap "internal"
- expect(@resource.bitmap).to eql("internal")
+ it "allows you to set the bitmap attribute" do
+ resource.bitmap "internal"
+ expect(resource.bitmap).to eql("internal")
end
- it "should allow you to set the layout attribute" do
- @resource.layout "f2"
- expect(@resource.layout).to eql("f2")
+ it "allows you to set the layout attribute" do
+ resource.layout "f2"
+ expect(resource.layout).to eql("f2")
end
- it "should allow you to set the devices attribute" do
- @resource.devices ["/dev/sda", "/dev/sdb"]
- expect(@resource.devices).to eql(["/dev/sda", "/dev/sdb"])
+ it "allows you to set the devices attribute" do
+ resource.devices ["/dev/sda", "/dev/sdb"]
+ expect(resource.devices).to eql(["/dev/sda", "/dev/sdb"])
end
- it "should allow you to set the exists attribute" do
- @resource.exists true
- expect(@resource.exists).to eql(true)
+ it "allows you to set the exists attribute" do
+ resource.exists true
+ expect(resource.exists).to eql(true)
end
describe "when it has devices, level, and chunk" do
before do
- @resource.raid_device("raider")
- @resource.devices(%w{device1 device2})
- @resource.level(1)
- @resource.chunk(42)
+ resource.raid_device("raider")
+ resource.devices(%w{device1 device2})
+ resource.level(1)
+ resource.chunk(42)
end
it "describes its state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:devices]).to eql(%w{device1 device2})
expect(state[:level]).to eq(1)
expect(state[:chunk]).to eq(42)
end
it "returns the raid device as its identity" do
- expect(@resource.identity).to eq("raider")
+ expect(resource.identity).to eq("raider")
end
end
diff --git a/spec/unit/resource/mount_spec.rb b/spec/unit/resource/mount_spec.rb
index 188eaa67d6..2fdad43f3f 100644
--- a/spec/unit/resource/mount_spec.rb
+++ b/spec/unit/resource/mount_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Joshua Timberman (<joshua@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,195 +20,152 @@
require "spec_helper"
describe Chef::Resource::Mount do
- before(:each) do
- @resource = Chef::Resource::Mount.new("filesystem")
- end
+ let(:resource) { Chef::Resource::Mount.new("filesystem") }
- it "should create a new Chef::Resource::Mount" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Mount)
+ it "has a name property" do
+ expect(resource.name).to eql("filesystem")
end
- it "should have a name" do
- expect(@resource.name).to eql("filesystem")
+ it "sets mount_point to the name" do
+ expect(resource.mount_point).to eql("filesystem")
end
- it "should set mount_point to the name" do
- expect(@resource.mount_point).to eql("filesystem")
+ it "has a default action of mount" do
+ expect(resource.action).to eql([:mount])
end
- it "should have a default action of mount" do
- expect(@resource.action).to eql([:mount])
+ it "accepts mount, umount, unmount and remount as actions" do
+ expect { resource.action :mount }.not_to raise_error
+ expect { resource.action :umount }.not_to raise_error
+ expect { resource.action :unmount }.not_to raise_error
+ expect { resource.action :remount }.not_to raise_error
+ expect { resource.action :brooklyn }.to raise_error(ArgumentError)
end
- it "should accept mount, umount and remount as actions" do
- expect { @resource.action :mount }.not_to raise_error
- expect { @resource.action :umount }.not_to raise_error
- expect { @resource.action :remount }.not_to raise_error
- expect { @resource.action :brooklyn }.to raise_error(ArgumentError)
+ it "allows you to set the device attribute" do
+ resource.device "/dev/sdb3"
+ expect(resource.device).to eql("/dev/sdb3")
end
- it "should allow you to set the device attribute" do
- @resource.device "/dev/sdb3"
- expect(@resource.device).to eql("/dev/sdb3")
+ it "sets fsck_device to '-' by default" do
+ expect(resource.fsck_device).to eql("-")
end
- it "should set fsck_device to '-' by default" do
- expect(@resource.fsck_device).to eql("-")
+ it "allows you to set the fsck_device attribute" do
+ resource.fsck_device "/dev/rdsk/sdb3"
+ expect(resource.fsck_device).to eql("/dev/rdsk/sdb3")
end
- it "should allow you to set the fsck_device attribute" do
- @resource.fsck_device "/dev/rdsk/sdb3"
- expect(@resource.fsck_device).to eql("/dev/rdsk/sdb3")
+ it "allows you to set the fstype attribute" do
+ resource.fstype "nfs"
+ expect(resource.fstype).to eql("nfs")
end
- it "should allow you to set the fstype attribute" do
- @resource.fstype "nfs"
- expect(@resource.fstype).to eql("nfs")
+ it "sets fstype to 'auto' by default" do
+ expect(resource.fstype).to eql("auto")
end
- it "should allow you to set the dump attribute" do
- @resource.dump 1
- expect(@resource.dump).to eql(1)
+ it "allows you to set the dump attribute" do
+ resource.dump 1
+ expect(resource.dump).to eql(1)
end
- it "should allow you to set the pass attribute" do
- @resource.pass 1
- expect(@resource.pass).to eql(1)
+ it "allows you to set the pass attribute" do
+ resource.pass 1
+ expect(resource.pass).to eql(1)
end
- it "should set the options attribute to defaults" do
- expect(@resource.options).to eql(["defaults"])
+ it "sets the options attribute to defaults" do
+ expect(resource.options).to eql(["defaults"])
end
- it "should allow options to be sent as a string, and convert to array" do
- @resource.options "rw,noexec"
- expect(@resource.options).to be_a_kind_of(Array)
+ it "allows options to be sent as a string, and convert to array" do
+ resource.options "rw,noexec"
+ expect(resource.options).to be_a_kind_of(Array)
end
- it "should allow options attribute as an array" do
- @resource.options %w{ro nosuid}
- expect(@resource.options).to be_a_kind_of(Array)
+ it "allows options attribute as an array" do
+ resource.options %w{ro nosuid}
+ expect(resource.options).to be_a_kind_of(Array)
end
- it "should allow options to be sent as a delayed evaluator" do
- @resource.options Chef::DelayedEvaluator.new { %w{rw noexec} }
- expect(@resource.options).to eql(%w{rw noexec})
+ it "allows options to be sent as a delayed evaluator" do
+ resource.options Chef::DelayedEvaluator.new { %w{rw noexec} }
+ expect(resource.options).to eql(%w{rw noexec})
end
- it "should allow options to be sent as a delayed evaluator, and convert to array" do
- @resource.options Chef::DelayedEvaluator.new { "rw,noexec" }
- expect(@resource.options).to be_a_kind_of(Array)
- expect(@resource.options).to eql(%w{rw noexec})
+ it "allows options to be sent as a delayed evaluator, and convert to array" do
+ resource.options Chef::DelayedEvaluator.new { "rw,noexec" }
+ expect(resource.options).to be_a_kind_of(Array)
+ expect(resource.options).to eql(%w{rw noexec})
end
- it "should accept true for mounted" do
- @resource.mounted(true)
- expect(@resource.mounted).to eql(true)
+ it "accepts true for mounted" do
+ resource.mounted(true)
+ expect(resource.mounted).to eql(true)
end
- it "should accept false for mounted" do
- @resource.mounted(false)
- expect(@resource.mounted).to eql(false)
+ it "accepts false for mounted" do
+ resource.mounted(false)
+ expect(resource.mounted).to eql(false)
end
- it "should set mounted to false by default" do
- expect(@resource.mounted).to eql(false)
+ it "sets mounted to false by default" do
+ expect(resource.mounted).to eql(false)
end
- it "should not accept a string for mounted" do
- expect { @resource.mounted("poop") }.to raise_error(ArgumentError)
+ it "does not accept a string for mounted" do
+ expect { resource.mounted("poop") }.to raise_error(ArgumentError)
end
- it "should accept true for enabled" do
- @resource.enabled(true)
- expect(@resource.enabled).to eql(true)
+ it "accepts true for enabled" do
+ resource.enabled(true)
+ expect(resource.enabled).to eql(true)
end
- it "should accept false for enabled" do
- @resource.enabled(false)
- expect(@resource.enabled).to eql(false)
+ it "accepts false for enabled" do
+ resource.enabled(false)
+ expect(resource.enabled).to eql(false)
end
- it "should set enabled to false by default" do
- expect(@resource.enabled).to eql(false)
+ it "sets enabled to false by default" do
+ expect(resource.enabled).to eql(false)
end
- it "should not accept a string for enabled" do
- expect { @resource.enabled("poop") }.to raise_error(ArgumentError)
+ it "does not accept a string for enabled" do
+ expect { resource.enabled("poop") }.to raise_error(ArgumentError)
end
- it "should default all feature support to false" do
+ it "defaults all feature support to false" do
support_hash = { :remount => false }
- expect(@resource.supports).to eq(support_hash)
+ expect(resource.supports).to eq(support_hash)
end
- it "should allow you to set feature support as an array" do
+ it "allows you to set feature support as an array" do
support_array = [ :remount ]
support_hash = { :remount => true }
- @resource.supports(support_array)
- expect(@resource.supports).to eq(support_hash)
+ resource.supports(support_array)
+ expect(resource.supports).to eq(support_hash)
end
- it "should allow you to set feature support as a hash" do
+ it "allows you to set feature support as a hash" do
support_hash = { :remount => true }
- @resource.supports(support_hash)
- expect(@resource.supports).to eq(support_hash)
- end
-
- it "should allow you to set username" do
- @resource.username("Administrator")
- expect(@resource.username).to eq("Administrator")
+ resource.supports(support_hash)
+ expect(resource.supports).to eq(support_hash)
end
- it "should allow you to set password" do
- @resource.password("Jetstream123!")
- expect(@resource.password).to eq("Jetstream123!")
+ it "allows you to set username" do
+ resource.username("Administrator")
+ expect(resource.username).to eq("Administrator")
end
- it "should allow you to set domain" do
- @resource.domain("TEST_DOMAIN")
- expect(@resource.domain).to eq("TEST_DOMAIN")
+ it "allows you to set password" do
+ resource.password("Jetstream123!")
+ expect(resource.password).to eq("Jetstream123!")
end
- describe "when it has mount point, device type, and fstype" do
- before do
- @resource.device("charmander")
- @resource.mount_point("123.456")
- @resource.device_type(:device)
- @resource.fstype("ranked")
- end
-
- it "describes its state" do
- state = @resource.state
- expect(state[:mount_point]).to eq("123.456")
- expect(state[:device_type]).to eql(:device)
- expect(state[:fstype]).to eq("ranked")
- end
-
- it "returns the device as its identity" do
- expect(@resource.identity).to eq("charmander")
- end
- end
-
- describe "when it has username, password and domain" do
- before do
- @resource.mount_point("T:")
- @resource.device("charmander")
- @resource.username("Administrator")
- @resource.password("Jetstream123!")
- @resource.domain("TEST_DOMAIN")
- end
-
- it "describes its state" do
- state = @resource.state
- expect(state[:mount_point]).to eq("T:")
- expect(state[:username]).to eq("Administrator")
- expect(state[:password]).to eq("Jetstream123!")
- expect(state[:domain]).to eq("TEST_DOMAIN")
- expect(state[:device_type]).to eql(:device)
- expect(state[:fstype]).to eq("auto")
- end
-
+ it "allows you to set domain" do
+ resource.domain("TEST_DOMAIN")
+ expect(resource.domain).to eq("TEST_DOMAIN")
end
end
diff --git a/spec/unit/resource/msu_package_spec.rb b/spec/unit/resource/msu_package_spec.rb
new file mode 100644
index 0000000000..bc2529df9c
--- /dev/null
+++ b/spec/unit/resource/msu_package_spec.rb
@@ -0,0 +1,53 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::MsuPackage do
+ let(:resource) { Chef::Resource::MsuPackage.new("test_pkg") }
+
+ it "is a subclass of Chef::Resource::Package" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Package)
+ end
+
+ it "sets resource name as :msu_package" do
+ expect(resource.resource_name).to eql(:msu_package)
+ end
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "coerces name property to package_name property" do
+ expect(resource.package_name).to eql("test_pkg")
+ end
+
+ it "coerces name property to a source property if source not provided" do
+ expect(resource.source).to end_with("test_pkg")
+ end
+
+ it "coerces name property to a source property if source not provided and package_name is" do
+ resource.package_name("package.msu")
+ expect(resource.source).to end_with("package.msu")
+ end
+
+ it "coerces source property if it does not looks like a path" do
+ resource.source("package.msu")
+ expect(resource.source).not_to eq("package.msu")
+ end
+end
diff --git a/spec/unit/resource/ohai_hint_spec.rb b/spec/unit/resource/ohai_hint_spec.rb
new file mode 100644
index 0000000000..1a4f6637fa
--- /dev/null
+++ b/spec/unit/resource/ohai_hint_spec.rb
@@ -0,0 +1,43 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::OhaiHint do
+
+ let(:resource) { Chef::Resource::OhaiHint.new("foo") }
+
+ it "has a resource name of :ohai_hint" do
+ expect(resource.resource_name).to eql(:ohai_hint)
+ end
+
+ it "has a default action of create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports the :nothing, :create and :delete actions" do
+ expect(resource.allowed_actions).to include(:nothing, :create, :delete)
+ end
+
+ it "runs at compile_time by default" do
+ expect(resource.compile_time).to eql(true)
+ end
+
+ it "the hint_name property is the name property" do
+ expect(resource.hint_name).to eql("foo")
+ end
+end
diff --git a/spec/unit/resource/ohai_spec.rb b/spec/unit/resource/ohai_spec.rb
index 9669ef193d..eb569aa3b7 100644
--- a/spec/unit/resource/ohai_spec.rb
+++ b/spec/unit/resource/ohai_spec.rb
@@ -20,41 +20,34 @@ require "spec_helper"
describe Chef::Resource::Ohai do
- before(:each) do
- @resource = Chef::Resource::Ohai.new("ohai_reload")
- end
-
- it "should create a new Chef::Resource::Ohai" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Ohai)
- end
+ let(:resource) { Chef::Resource::Ohai.new("ohai_reload") }
- it "should have a resource name of :ohai" do
- expect(@resource.resource_name).to eql(:ohai)
+ it "has a resource name of :ohai" do
+ expect(resource.resource_name).to eql(:ohai)
end
- it "should have a default action of create" do
- expect(@resource.action).to eql([:reload])
+ it "has a default action of reload" do
+ expect(resource.action).to eql([:reload])
end
- it "should allow you to set the plugin attribute" do
- @resource.plugin "passwd"
- expect(@resource.plugin).to eql("passwd")
+ it "allows you to set the plugin attribute" do
+ resource.plugin "passwd"
+ expect(resource.plugin).to eql("passwd")
end
describe "when it has a plugin value" do
before do
- @resource.name("test")
- @resource.plugin("passwd")
+ resource.name("test")
+ resource.plugin("passwd")
end
it "describes its state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:plugin]).to eq("passwd")
end
it "returns the name as its identity" do
- expect(@resource.identity).to eq("test")
+ expect(resource.identity).to eq("test")
end
end
diff --git a/spec/unit/resource/openbsd_package_spec.rb b/spec/unit/resource/openbsd_package_spec.rb
index 9bdc823576..17dc9f09ff 100644
--- a/spec/unit/resource/openbsd_package_spec.rb
+++ b/spec/unit/resource/openbsd_package_spec.rb
@@ -20,28 +20,24 @@
#
require "spec_helper"
-require "ostruct"
describe Chef::Resource::OpenbsdPackage do
-
- before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @resource = Chef::Resource::OpenbsdPackage.new("foo", @run_context)
- end
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::OpenbsdPackage.new("foo", run_context) }
describe "Initialization" do
- it "should return a Chef::Resource::OpenbsdPackage" do
- expect(@resource).to be_a_kind_of(Chef::Resource::OpenbsdPackage)
+ it "is a subclass of Chef::Resource::Package" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Package)
end
- it "should set the resource_name to :openbsd_package" do
- expect(@resource.resource_name).to eql(:openbsd_package)
+ it "sets the resource_name to :openbsd_package" do
+ expect(resource.resource_name).to eql(:openbsd_package)
end
- it "should not set the provider" do
- expect(@resource.provider).to be_nil
+ it "does not set the provider" do
+ expect(resource.provider).to be_nil
end
end
diff --git a/spec/unit/resource/openssl_dhparam.rb b/spec/unit/resource/openssl_dhparam.rb
new file mode 100644
index 0000000000..d1021d4f7e
--- /dev/null
+++ b/spec/unit/resource/openssl_dhparam.rb
@@ -0,0 +1,51 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::OpensslDhparam do
+
+ let(:resource) { Chef::Resource::OpensslDhparam.new("dhparam") }
+
+ it "has a resource name of :openssl_dhparam" do
+ expect(resource.resource_name).to eql(:openssl_dhparam)
+ end
+
+ it "has a default action of create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "has a default mode of '0640'" do
+ expect(resource.mode).to eql("0640")
+ end
+
+ it "has a default generator of 2" do
+ expect(resource.generator).to eql(2)
+ end
+
+ it "has a default key_length of 2048" do
+ expect(resource.key_length).to eql(2048)
+ end
+
+ it "only accepts valid key length" do
+ expect { resource.key_length 1234 }.to raise_error(ArgumentError)
+ end
+
+ it "the path property is the name property" do
+ expect(resource.path).to eql("dhparam")
+ end
+end
diff --git a/spec/unit/resource/openssl_rsa_private_key_spec.rb b/spec/unit/resource/openssl_rsa_private_key_spec.rb
new file mode 100644
index 0000000000..67bebf17fe
--- /dev/null
+++ b/spec/unit/resource/openssl_rsa_private_key_spec.rb
@@ -0,0 +1,59 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::OpensslRsaPrivateKey do
+
+ let(:resource) { Chef::Resource::OpensslRsaPrivateKey.new("key") }
+
+ it "has a resource name of :openssl_rsa_private_key" do
+ expect(resource.resource_name).to eql(:openssl_rsa_private_key)
+ end
+
+ it "has a default action of create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "has a default mode of '0600'" do
+ expect(resource.mode).to eql("0600")
+ end
+
+ it "has a default key_cipher of 'des3'" do
+ expect(resource.key_cipher).to eql("des3")
+ end
+
+ it "only accepts valid key_cipher values" do
+ expect { resource.key_cipher "fako" }.to raise_error(ArgumentError)
+ end
+
+ it "has a default key_length of 2048" do
+ expect(resource.key_length).to eql(2048)
+ end
+
+ it "only accepts valid key length" do
+ expect { resource.key_length 1234 }.to raise_error(ArgumentError)
+ end
+
+ it "has a default force value of of false" do
+ expect(resource.force).to eql(false)
+ end
+
+ it "the path property is the name property" do
+ expect(resource.path).to eql("key")
+ end
+end
diff --git a/lib/chef/knife/cookbook_site_vendor.rb b/spec/unit/resource/openssl_rsa_public_key_spec.rb
index 291715cc0b..d624e10338 100644
--- a/lib/chef/knife/cookbook_site_vendor.rb
+++ b/spec/unit/resource/openssl_rsa_public_key_spec.rb
@@ -1,6 +1,5 @@
#
-# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright 2018, Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,31 +15,25 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/cookbook_site_install"
+require "spec_helper"
-class Chef::Knife::CookbookSiteVendor < Chef::Knife::CookbookSiteInstall
+describe Chef::Resource::OpensslRsaPublicKey do
- def self.load_deps
- superclass.load_deps
- end
+ let(:resource) { Chef::Resource::OpensslRsaPublicKey.new("key") }
- def self.options=(new_opts)
- superclass.options = new_opts
+ it "has a resource name of :openssl_rsa_public_key" do
+ expect(resource.resource_name).to eql(:openssl_rsa_public_key)
end
- def self.options
- superclass.options
+ it "has a default action of create" do
+ expect(resource.action).to eql([:create])
end
- banner(<<-B)
-*************************************************
-DEPRECATED: please use knife cookbook site install
-*************************************************
-
-#{superclass.banner}
-B
-
- category "deprecated"
+ it "has a default mode of '0640'" do
+ expect(resource.mode).to eql("0640")
+ end
+ it "the path property is the name property" do
+ expect(resource.path).to eql("key")
+ end
end
diff --git a/spec/unit/resource/osx_profile_spec.rb b/spec/unit/resource/osx_profile_spec.rb
index 7bd504d414..291b205466 100644
--- a/spec/unit/resource/osx_profile_spec.rb
+++ b/spec/unit/resource/osx_profile_spec.rb
@@ -19,41 +19,37 @@
require "spec_helper"
describe Chef::Resource::OsxProfile do
- let(:resource) { Chef::Resource::OsxProfile.new(
+ let(:resource) do
+ Chef::Resource::OsxProfile.new(
"Test Profile Resource",
run_context)
- }
-
- it "should create a new Chef::Resource::OsxProfile" do
- expect(resource).to be_a_kind_of(Chef::Resource)
- expect(resource).to be_a_kind_of(Chef::Resource::OsxProfile)
end
- it "should have a resource name of profile" do
+ it "has a resource name of profile" do
expect(resource.resource_name).to eql(:osx_profile)
end
- it "should have a default action of install" do
+ it "has a default action of install" do
expect(resource.action).to eql([:install])
end
- it "should accept install and remove as actions" do
+ it "accepts install and remove as actions" do
expect { resource.action :install }.not_to raise_error
expect { resource.action :remove }.not_to raise_error
end
- it "should allow you to set the profile attribute" do
+ it "allows you to set the profile attribute" do
resource.profile "com.testprofile.screensaver"
expect(resource.profile).to eql("com.testprofile.screensaver")
end
- it "should allow you to set the profile attribute to a string" do
+ it "allows you to set the profile attribute to a string" do
resource.profile "com.testprofile.screensaver"
expect(resource.profile).to be_a(String)
expect(resource.profile).to eql("com.testprofile.screensaver")
end
- it "should allow you to set the profile attribute to a hash" do
+ it "allows you to set the profile attribute to a hash" do
test_profile = { "profile" => false }
resource.profile test_profile
expect(resource.profile).to be_a(Hash)
diff --git a/spec/unit/resource/package_spec.rb b/spec/unit/resource/package_spec.rb
index dbd76d2eba..38d0bfce96 100644
--- a/spec/unit/resource/package_spec.rb
+++ b/spec/unit/resource/package_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,74 +20,75 @@
require "spec_helper"
describe Chef::Resource::Package do
+ let(:resource) { Chef::Resource::Package.new("emacs") }
- before(:each) do
- @resource = Chef::Resource::Package.new("emacs")
+ it "sets the package_name to the first argument to new" do
+ expect(resource.package_name).to eql("emacs")
end
- it "should create a new Chef::Resource::Package" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Package)
+ it "accepts a string for the package name" do
+ resource.package_name "something"
+ expect(resource.package_name).to eql("something")
end
- it "should set the package_name to the first argument to new" do
- expect(@resource.package_name).to eql("emacs")
+ it "accepts a string for the version" do
+ resource.version "something"
+ expect(resource.version).to eql("something")
end
- it "should accept a string for the package name" do
- @resource.package_name "something"
- expect(@resource.package_name).to eql("something")
+ it "accepts a string for the response file" do
+ resource.response_file "something"
+ expect(resource.response_file).to eql("something")
end
- it "should accept a string for the version" do
- @resource.version "something"
- expect(@resource.version).to eql("something")
+ it "accepts a hash for response file template variables" do
+ resource.response_file_variables({ :variables => true })
+ expect(resource.response_file_variables).to eql({ :variables => true })
end
- it "should accept a string for the response file" do
- @resource.response_file "something"
- expect(@resource.response_file).to eql("something")
+ it "accepts a string for the source" do
+ resource.source "something"
+ expect(resource.source).to eql("something")
end
- it "should accept a hash for response file template variables" do
- @resource.response_file_variables({ :variables => true })
- expect(@resource.response_file_variables).to eql({ :variables => true })
+ it "accepts a string for the options" do
+ resource.options "something"
+ expect(resource.options).to eql(["something"])
end
- it "should accept a string for the source" do
- @resource.source "something"
- expect(@resource.source).to eql("something")
- end
-
- it "should accept a string for the options" do
- @resource.options "something"
- expect(@resource.options).to eql("something")
+ it "splits options" do
+ resource.options "-a -b 'arg with spaces' -b \"and quotes\""
+ expect(resource.options).to eql(["-a", "-b", "arg with spaces", "-b", "and quotes"])
end
describe "when it has a package_name and version" do
before do
- @resource.package_name("tomcat")
- @resource.version("10.9.8")
- @resource.options("-al")
+ resource.package_name("tomcat")
+ resource.version("10.9.8")
+ resource.options("-al")
end
it "describes its state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:version]).to eq("10.9.8")
- expect(state[:options]).to eq("-al")
+ expect(state[:options]).to eq(["-al"])
end
it "returns the file path as its identity" do
- expect(@resource.identity).to eq("tomcat")
+ expect(resource.identity).to eq("tomcat")
+ end
+
+ it "takes options as an array" do
+ resource.options [ "-a", "-l" ]
+ expect(resource.options).to eq(["-a", "-l" ])
end
end
# String, Integer
[ "600", 600 ].each do |val|
it "supports setting a timeout as a #{val.class}" do
- @resource.timeout(val)
- expect(@resource.timeout).to eql(val)
+ resource.timeout(val)
+ expect(resource.timeout).to eql(val)
end
end
-
end
diff --git a/spec/unit/resource/perl_spec.rb b/spec/unit/resource/perl_spec.rb
index 417d74a8c2..f8e11c465c 100644
--- a/spec/unit/resource/perl_spec.rb
+++ b/spec/unit/resource/perl_spec.rb
@@ -20,21 +20,17 @@ require "spec_helper"
describe Chef::Resource::Perl do
- before(:each) do
- @resource = Chef::Resource::Perl.new("fakey_fakerton")
- end
+ let(:resource) { Chef::Resource::Perl.new("fakey_fakerton") }
- it "should create a new Chef::Resource::Perl" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Perl)
+ it "is a subclass of Chef::Resource::Script" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Script)
end
- it "should have a resource name of :perl" do
- expect(@resource.resource_name).to eql(:perl)
+ it "has a resource name of :perl" do
+ expect(resource.resource_name).to eql(:perl)
end
- it "should have an interpreter of perl" do
- expect(@resource.interpreter).to eql("perl")
+ it "has an interpreter of perl" do
+ expect(resource.interpreter).to eql("perl")
end
-
end
diff --git a/spec/unit/resource/portage_package_spec.rb b/spec/unit/resource/portage_package_spec.rb
index d2336744bf..a37cfd6e41 100644
--- a/spec/unit/resource/portage_package_spec.rb
+++ b/spec/unit/resource/portage_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +16,17 @@
# limitations under the License.
#
-require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
+require "spec_helper"
describe Chef::Resource::PortagePackage, "initialize" do
- before(:each) do
- @resource = Chef::Resource::PortagePackage.new("foo")
- end
-
- it "should return a Chef::Resource::PortagePackage" do
- expect(@resource).to be_a_kind_of(Chef::Resource::PortagePackage)
- end
+ let(:resource) { Chef::Resource::PortagePackage.new("foo") }
- it "should set the resource_name to :portage_package" do
- expect(@resource.resource_name).to eql(:portage_package)
+ it "is a subclass of Chef::Resource::Package" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Package)
end
- it "should set the provider to Chef::Provider::Package::Portage" do
- expect(@resource.provider).to eql(Chef::Provider::Package::Portage)
+ it "sets the resource_name to :portage_package" do
+ expect(resource.resource_name).to eql(:portage_package)
end
end
diff --git a/spec/unit/resource/powershell_package_spec.rb b/spec/unit/resource/powershell_package_spec.rb
new file mode 100644
index 0000000000..a448d58d5a
--- /dev/null
+++ b/spec/unit/resource/powershell_package_spec.rb
@@ -0,0 +1,75 @@
+#
+# Author:: Dheeraj Dubey(<dheeraj.dubey@msystechnologies.com>)
+# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::PowershellPackage do
+
+ let(:resource) { Chef::Resource::PowershellPackage.new("test_package") }
+
+ it "is a subclass of Chef::Resource::Package" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Package)
+ end
+
+ #to check the value of resource.resource_name
+ it "has a resource name of :powershell_package" do
+ expect(resource.resource_name).to eql(:powershell_package)
+ end
+
+ it "coerces its name to a package_name array" do
+ expect(resource.package_name).to eql(["test_package"])
+ end
+
+ it "the package_name setter coerces to arrays" do
+ resource.package_name("git")
+ expect(resource.package_name).to eql(["git"])
+ end
+
+ it "the package_name setter accepts arrays" do
+ resource.package_name(%w{git unzip})
+ expect(resource.package_name).to eql(%w{git unzip})
+ end
+
+ it "the name accepts arrays" do
+ resource = Chef::Resource::PowershellPackage.new(%w{git unzip})
+ expect(resource.package_name).to eql(%w{git unzip})
+ end
+
+ it "the default version is nil" do
+ expect(resource.version).to eql(nil)
+ end
+
+ it "the version setter coerces to arrays" do
+ resource.version("1.2.3")
+ expect(resource.version).to eql(["1.2.3"])
+ end
+
+ it "the version setter accepts arrays" do
+ resource.version(["1.2.3", "4.5.6"])
+ expect(resource.version).to eql(["1.2.3", "4.5.6"])
+ end
+
+ it "the default source is nil" do
+ expect(resource.source).to eql(nil)
+ end
+
+ it "the source setter accepts strings" do
+ resource.source("MyGallery")
+ expect(resource.source).to eql("MyGallery")
+ end
+end
diff --git a/spec/unit/resource/powershell_script_spec.rb b/spec/unit/resource/powershell_script_spec.rb
index 6457090608..9cdb6fdbee 100644
--- a/spec/unit/resource/powershell_script_spec.rb
+++ b/spec/unit/resource/powershell_script_spec.rb
@@ -59,9 +59,9 @@ describe Chef::Resource::PowershellScript do
allow(resource).to receive(:updated).and_return(true)
end
- it "inherits exactly the :cwd, :environment, :group, :path, :user, :umask, and :architecture attributes from a parent resource class" do
+ it "inherits exactly the :cwd, :environment, :group, :path, :user, :umask, :architecture, :elevated attributes from a parent resource class" do
inherited_difference = Chef::Resource::PowershellScript.guard_inherited_attributes -
- [:cwd, :environment, :group, :path, :user, :umask, :architecture ]
+ [:cwd, :environment, :group, :path, :user, :umask, :architecture, :elevated ]
expect(inherited_difference).to eq([])
end
diff --git a/spec/unit/resource/python_spec.rb b/spec/unit/resource/python_spec.rb
index aba84c4000..5a4dc8eca8 100644
--- a/spec/unit/resource/python_spec.rb
+++ b/spec/unit/resource/python_spec.rb
@@ -20,21 +20,13 @@ require "spec_helper"
describe Chef::Resource::Python do
- before(:each) do
- @resource = Chef::Resource::Python.new("fakey_fakerton")
- end
-
- it "should create a new Chef::Resource::Python" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Python)
- end
+ let(:resource) { Chef::Resource::Python.new("fakey_fakerton") }
- it "should have a resource name of :python" do
- expect(@resource.resource_name).to eql(:python)
+ it "has a resource name of :python" do
+ expect(resource.resource_name).to eql(:python)
end
- it "should have an interpreter of python" do
- expect(@resource.interpreter).to eql("python")
+ it "has an interpreter of python" do
+ expect(resource.interpreter).to eql("python")
end
-
end
diff --git a/spec/unit/resource/reboot_spec.rb b/spec/unit/resource/reboot_spec.rb
new file mode 100644
index 0000000000..adcfb9e6a4
--- /dev/null
+++ b/spec/unit/resource/reboot_spec.rb
@@ -0,0 +1,45 @@
+#
+# Copyright:: Copyright 2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::Reboot do
+
+ let(:resource) { Chef::Resource::Reboot.new("reboot me!") }
+
+ it "has a default action of :nothing" do
+ expect(resource.action).to eql([:nothing])
+ end
+
+ it "supports the :nothing, :request_reboot, :reboot_now, and :cancel actions" do
+ expect(resource.allowed_actions).to include(:nothing, :request_reboot, :reboot_now, :cancel)
+ end
+
+ it "has a resource_name of :reboot" do
+ expect(resource.resource_name).to eq(:reboot)
+ end
+
+ it "accepts a String for the reboot reason" do
+ resource.reason "reasons"
+ expect(resource.reason).to eq("reasons")
+ end
+
+ it "accepts an Integer for delay_mins" do
+ resource.delay_mins 100
+ expect { resource.delay_mins "100" }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/registry_key_spec.rb b/spec/unit/resource/registry_key_spec.rb
index 472c511857..1f32a00210 100644
--- a/spec/unit/resource/registry_key_spec.rb
+++ b/spec/unit/resource/registry_key_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright 2012-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,181 +19,182 @@
require "spec_helper"
describe Chef::Resource::RegistryKey, "initialize" do
- before(:each) do
- @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius')
- end
-
- it "should create a new Chef::Resource::RegistryKey" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::RegistryKey)
- end
+ let(:resource) { Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius') }
- it "should set the resource_name to :registry_key" do
- expect(@resource.resource_name).to eql(:registry_key)
+ it "sets the resource_name to :registry_key" do
+ expect(resource.resource_name).to eql(:registry_key)
end
- it "should set the key equal to the argument to initialize" do
- expect(@resource.key).to eql('HKCU\Software\Raxicoricofallapatorius')
+ it "sets the key property to the resource name" do
+ expect(resource.key).to eql('HKCU\Software\Raxicoricofallapatorius')
end
- it "should default recursive to false" do
- expect(@resource.recursive).to eql(false)
+ it "defaults recursive to false" do
+ expect(resource.recursive).to eql(false)
end
- it "should default architecture to :machine" do
- expect(@resource.architecture).to eql(:machine)
+ it "defaults architecture to :machine" do
+ expect(resource.architecture).to eql(:machine)
end
- it "should set action to :create" do
- expect(@resource.action).to eql([:create])
+ it "sets action to :create" do
+ expect(resource.action).to eql([:create])
end
%w{create create_if_missing delete delete_key}.each do |action|
- it "should allow action #{action}" do
- expect(@resource.allowed_actions.detect { |a| a == action.to_sym }).to eql(action.to_sym)
+ it "allows action #{action}" do
+ expect(resource.allowed_actions.detect { |a| a == action.to_sym }).to eql(action.to_sym)
end
end
end
describe Chef::Resource::RegistryKey, "key" do
- before(:each) do
- @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius')
- end
+ let(:resource) { Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius') }
- it "should allow a string" do
- @resource.key 'HKCU\Software\Poosh'
- expect(@resource.key).to eql('HKCU\Software\Poosh')
+ it "allows a string" do
+ resource.key 'HKCU\Software\Poosh'
+ expect(resource.key).to eql('HKCU\Software\Poosh')
end
- it "should not allow an integer" do
- expect { @resource.send(:key, 100) }.to raise_error(ArgumentError)
+ it "does not allow an integer" do
+ expect { resource.send(:key, 100) }.to raise_error(ArgumentError)
end
- it "should not allow a hash" do
- expect { @resource.send(:key, { :sonic => "screwdriver" }) }.to raise_error(ArgumentError)
+ it "does not allow a hash" do
+ expect { resource.send(:key, { :sonic => "screwdriver" }) }.to raise_error(ArgumentError)
end
end
describe Chef::Resource::RegistryKey, "values" do
- before(:each) do
- @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius')
+ let(:resource) { Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius') }
+
+ it "allows a single proper hash of registry values" do
+ resource.values( { :name => "poosh", :type => :string, :data => "carmen" } )
+ expect(resource.values).to eql([ { :name => "poosh", :type => :string, :data => "carmen" } ])
end
- it "should allow a single proper hash of registry values" do
- @resource.values( { :name => "poosh", :type => :string, :data => "carmen" } )
- expect(@resource.values).to eql([ { :name => "poosh", :type => :string, :data => "carmen" } ])
+ it "allows an array of proper hashes of registry values" do
+ resource.values [ { :name => "poosh", :type => :string, :data => "carmen" } ]
+ expect(resource.values).to eql([ { :name => "poosh", :type => :string, :data => "carmen" } ])
end
- it "should allow an array of proper hashes of registry values" do
- @resource.values [ { :name => "poosh", :type => :string, :data => "carmen" } ]
- expect(@resource.values).to eql([ { :name => "poosh", :type => :string, :data => "carmen" } ])
+ it "returns checksummed data if the type is unsafe" do
+ resource.values( { :name => "poosh", :type => :binary, :data => 255.chr * 1 })
+ expect(resource.values).to eql([ { :name => "poosh", :type => :binary, :data => "a8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89" } ])
end
- it "should return checksummed data if the type is unsafe" do
- @resource.values( { :name => "poosh", :type => :binary, :data => 255.chr * 1 })
- expect(@resource.values).to eql([ { :name => "poosh", :type => :binary, :data => "a8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89" } ])
+ it "raises an exception if the name field is missing" do
+ expect { resource.values [ { :type => :string, :data => "carmen" } ] }.to raise_error(ArgumentError)
end
- it "should throw an exception if the name field is missing" do
- expect { @resource.values [ { :type => :string, :data => "carmen" } ] }.to raise_error(ArgumentError)
+ it "raises an exception if extra fields are present" do
+ expect { resource.values [ { :name => "poosh", :type => :string, :data => "carmen", :screwdriver => "sonic" } ] }.to raise_error(ArgumentError)
end
- it "should throw an exception if the type field is missing" do
- expect { @resource.values [ { :name => "poosh", :data => "carmen" } ] }.to raise_error(ArgumentError)
+ it "does not allow a string" do
+ expect { resource.send(:values, "souffle") }.to raise_error(ArgumentError)
end
- it "should throw an exception if the data field is missing" do
- expect { @resource.values [ { :name => "poosh", :type => :string } ] }.to raise_error(ArgumentError)
+ it "does not allow an integer" do
+ expect { resource.send(:values, 100) }.to raise_error(ArgumentError)
end
- it "should throw an exception if extra fields are present" do
- expect { @resource.values [ { :name => "poosh", :type => :string, :data => "carmen", :screwdriver => "sonic" } ] }.to raise_error(ArgumentError)
+ it "raises an exception if type of name is not string" do
+ expect { resource.values([ { :name => 123, :type => :string, :data => "carmen" } ]) }.to raise_error(ArgumentError)
end
- it "should not allow a string" do
- expect { @resource.send(:values, "souffle") }.to raise_error(ArgumentError)
+ it "does not raise an exception if type of name is string" do
+ expect { resource.values([ { :name => "123", :type => :string, :data => "carmen" } ]) }.to_not raise_error
end
- it "should not allow an integer" do
- expect { @resource.send(:values, 100) }.to raise_error(ArgumentError)
+ context "type key not given" do
+ it "does not raise an exception" do
+ expect { resource.values([ { :name => "123", :data => "carmen" } ]) }.to_not raise_error
+ end
+ end
+
+ context "type key given" do
+ it "raises an exception if type of type is not symbol" do
+ expect { resource.values([ { :name => "123", :type => "string", :data => "carmen" } ]) }.to raise_error(ArgumentError)
+ end
+
+ it "does not raise an exception if type of type is symbol" do
+ expect { resource.values([ { :name => "123", :type => :string, :data => "carmen" } ]) }.to_not raise_error
+ end
end
end
describe Chef::Resource::RegistryKey, "recursive" do
- before(:each) do
- @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius')
- end
+ let(:resource) { Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius') }
- it "should allow a boolean" do
- @resource.recursive(true)
- expect(@resource.recursive).to eql(true)
+ it "allows a boolean" do
+ resource.recursive(true)
+ expect(resource.recursive).to eql(true)
end
- it "should not allow a hash" do
- expect { @resource.recursive({ :sonic => :screwdriver }) }.to raise_error(ArgumentError)
+ it "does not allow a hash" do
+ expect { resource.recursive({ :sonic => :screwdriver }) }.to raise_error(ArgumentError)
end
- it "should not allow an array" do
- expect { @resource.recursive([:nose, :chin]) }.to raise_error(ArgumentError)
+ it "does not allow an array" do
+ expect { resource.recursive([:nose, :chin]) }.to raise_error(ArgumentError)
end
- it "should not allow a string" do
- expect { @resource.recursive("souffle") }.to raise_error(ArgumentError)
+ it "does not allow a string" do
+ expect { resource.recursive("souffle") }.to raise_error(ArgumentError)
end
- it "should not allow an integer" do
- expect { @resource.recursive(100) }.to raise_error(ArgumentError)
+ it "does not allow an integer" do
+ expect { resource.recursive(100) }.to raise_error(ArgumentError)
end
end
describe Chef::Resource::RegistryKey, "architecture" do
- before(:each) do
- @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius')
- end
+ let(:resource) { Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius') }
[ :i386, :x86_64, :machine ].each do |arch|
- it "should allow #{arch} as a symbol" do
- @resource.architecture(arch)
- expect(@resource.architecture).to eql(arch)
+ it "allows #{arch} as a symbol" do
+ resource.architecture(arch)
+ expect(resource.architecture).to eql(arch)
end
end
- it "should not allow a hash" do
- expect { @resource.architecture({ :sonic => :screwdriver }) }.to raise_error(ArgumentError)
+ it "does not allow other symbols" do
+ expect { resource.architecture(:nope) }.to raise_error(ArgumentError)
end
- it "should not allow an array" do
- expect { @resource.architecture([:nose, :chin]) }.to raise_error(ArgumentError)
+ it "does not allow a hash" do
+ expect { resource.architecture({ :sonic => :screwdriver }) }.to raise_error(ArgumentError)
end
- it "should not allow a string" do
- expect { @resource.architecture("souffle") }.to raise_error(ArgumentError)
+ it "does not allow an array" do
+ expect { resource.architecture([:nose, :chin]) }.to raise_error(ArgumentError)
end
- it "should not allow an integer" do
- expect { @resource.architecture(100) }.to raise_error(ArgumentError)
+ it "does not allow a string" do
+ expect { resource.architecture("souffle") }.to raise_error(ArgumentError)
+ end
+
+ it "does not allow an integer" do
+ expect { resource.architecture(100) }.to raise_error(ArgumentError)
end
end
describe Chef::Resource::RegistryKey, ":unscrubbed_values" do
- before(:each) do
- @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius')
- end
+ let(:resource) { Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius') }
- it "should return unsafe data as-is" do
+ it "returns unsafe data as-is" do
key_values = [ { :name => "poosh", :type => :binary, :data => 255.chr * 1 } ]
- @resource.values(key_values)
- expect(@resource.unscrubbed_values).to eql(key_values)
+ resource.values(key_values)
+ expect(resource.unscrubbed_values).to eql(key_values)
end
end
describe Chef::Resource::RegistryKey, "state" do
- before(:each) do
- @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius')
- end
+ let(:resource) { Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius') }
- it "should return scrubbed values" do
- @resource.values([ { :name => "poosh", :type => :binary, :data => 255.chr * 1 } ])
- expect(@resource.state).to eql( { :values => [{ :name => "poosh", :type => :binary, :data => "a8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89" }] } )
+ it "returns scrubbed values" do
+ resource.values([ { :name => "poosh", :type => :binary, :data => 255.chr * 1 } ])
+ expect(resource.state_for_resource_reporter[:values]).to eql( [{ :name => "poosh", :type => :binary, :data => "a8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89" }] )
end
end
diff --git a/spec/unit/resource/remote_directory_spec.rb b/spec/unit/resource/remote_directory_spec.rb
index cdca214db6..afa16678fc 100644
--- a/spec/unit/resource/remote_directory_spec.rb
+++ b/spec/unit/resource/remote_directory_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,78 +20,76 @@ require "spec_helper"
describe Chef::Resource::RemoteDirectory do
- before(:each) do
- @resource = Chef::Resource::RemoteDirectory.new("/etc/dunk")
- end
+ let(:resource) { Chef::Resource::RemoteDirectory.new("/etc/dunk") }
- it "should create a new Chef::Resource::RemoteDirectory" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::RemoteDirectory)
+ it "path is the name property" do
+ expect(resource.path).to eql("/etc/dunk")
end
- it "should set the path to the first argument to new" do
- expect(@resource.path).to eql("/etc/dunk")
+ it "accepts a string for the remote directory source" do
+ resource.source "foo"
+ expect(resource.source).to eql("foo")
end
- it "should accept a string for the remote directory source" do
- @resource.source "foo"
- expect(@resource.source).to eql("foo")
+ it "has the basename of the remote directory resource as the default source" do
+ resource.path "/foo/bar"
+ expect(resource.source).to eql("bar")
end
- it "should have the basename of the remote directory resource as the default source" do
- expect(@resource.source).to eql("dunk")
+ it "accepts a number for the remote files backup" do
+ resource.files_backup 1
+ expect(resource.files_backup).to eql(1)
end
- it "should accept a number for the remote files backup" do
- @resource.files_backup 1
- expect(@resource.files_backup).to eql(1)
+ it "accepts false for the remote files backup" do
+ resource.files_backup false
+ expect(resource.files_backup).to eql(false)
end
- it "should accept false for the remote files backup" do
- @resource.files_backup false
- expect(@resource.files_backup).to eql(false)
+ it "accepts 3 or 4 digits for the files_mode" do
+ resource.files_mode 100
+ expect(resource.files_mode).to eql(100)
+ resource.files_mode 1000
+ expect(resource.files_mode).to eql(1000)
end
- it "should accept 3 or 4 digets for the files_mode" do
- @resource.files_mode 100
- expect(@resource.files_mode).to eql(100)
- @resource.files_mode 1000
- expect(@resource.files_mode).to eql(1000)
+ it "accepts a string or number for the files group" do
+ resource.files_group "heart"
+ expect(resource.files_group).to eql("heart")
+ resource.files_group 1000
+ expect(resource.files_group).to eql(1000)
end
- it "should accept a string or number for the files group" do
- @resource.files_group "heart"
- expect(@resource.files_group).to eql("heart")
- @resource.files_group 1000
- expect(@resource.files_group).to eql(1000)
+ it "accepts a string or number for the files owner" do
+ resource.files_owner "heart"
+ expect(resource.files_owner).to eql("heart")
+ resource.files_owner 1000
+ expect(resource.files_owner).to eql(1000)
end
- it "should accept a string or number for the files owner" do
- @resource.files_owner "heart"
- expect(@resource.files_owner).to eql("heart")
- @resource.files_owner 1000
- expect(@resource.files_owner).to eql(1000)
+ it "overwrites by default" do
+ expect(resource.overwrite).to be true
end
describe "when it has cookbook, files owner, files mode, and source" do
before do
- @resource.path("/var/path/")
- @resource.cookbook("pokemon.rb")
- @resource.files_owner("root")
- @resource.files_group("supergroup")
- @resource.files_mode("0664")
- @resource.source("/var/source/")
+ resource.path("/var/path/")
+ resource.cookbook("pokemon.rb")
+ resource.files_owner("root")
+ resource.files_group("supergroup")
+ resource.files_mode("0664")
+ resource.source("/var/source/")
end
it "describes its state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:files_owner]).to eq("root")
expect(state[:files_group]).to eq("supergroup")
expect(state[:files_mode]).to eq("0664")
end
it "returns the path as its identity" do
- expect(@resource.identity).to eq("/var/path/")
+ expect(resource.identity).to eq("/var/path/")
end
end
end
diff --git a/spec/unit/resource/remote_file_spec.rb b/spec/unit/resource/remote_file_spec.rb
index 718129aba3..691c55219c 100644
--- a/spec/unit/resource/remote_file_spec.rb
+++ b/spec/unit/resource/remote_file_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,147 +21,141 @@ require "spec_helper"
describe Chef::Resource::RemoteFile do
- before(:each) do
- @resource = Chef::Resource::RemoteFile.new("fakey_fakerton")
- end
+ let(:resource) { Chef::Resource::RemoteFile.new("fakey_fakerton") }
describe "initialize" do
- it "should create a new Chef::Resource::RemoteFile" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::File)
- expect(@resource).to be_a_kind_of(Chef::Resource::RemoteFile)
+ it "is a subclass of Chef::Resource::File" do
+ expect(resource).to be_a_kind_of(Chef::Resource::File)
end
end
it "says its provider is RemoteFile when the source is an absolute URI" do
- @resource.source("http://www.google.com/robots.txt")
- expect(@resource.provider).to eq(Chef::Provider::RemoteFile)
- expect(Chef::Platform.find_provider(:noplatform, "noversion", @resource)).to eq(Chef::Provider::RemoteFile)
+ resource.source("http://www.google.com/robots.txt")
+ expect(resource.provider_for_action(:create)).to be_kind_of(Chef::Provider::RemoteFile)
end
it "says its provider is RemoteFile when the source is a network share" do
- @resource.source("\\\\fakey\\fakerton\\fake.txt")
- expect(@resource.provider).to eq(Chef::Provider::RemoteFile)
- expect(Chef::Platform.find_provider(:noplatform, "noversion", @resource)).to eq(Chef::Provider::RemoteFile)
+ resource.source("\\\\fakey\\fakerton\\fake.txt")
+ expect(resource.provider_for_action(:create)).to be_kind_of(Chef::Provider::RemoteFile)
end
describe "source" do
it "does not have a default value for 'source'" do
- expect(@resource.source).to eql([])
+ expect(resource.source).to eql([])
end
- it "should accept a URI for the remote file source" do
- @resource.source "http://opscode.com/"
- expect(@resource.source).to eql([ "http://opscode.com/" ])
+ it "accepts a URI for the remote file source" do
+ resource.source "http://opscode.com/"
+ expect(resource.source).to eql([ "http://opscode.com/" ])
end
- it "should accept a windows network share source" do
- @resource.source "\\\\fakey\\fakerton\\fake.txt"
- expect(@resource.source).to eql([ "\\\\fakey\\fakerton\\fake.txt" ])
+ it "accepts a windows network share source" do
+ resource.source "\\\\fakey\\fakerton\\fake.txt"
+ expect(resource.source).to eql([ "\\\\fakey\\fakerton\\fake.txt" ])
end
- it "should accept file URIs with spaces" do
- @resource.source("file:///C:/foo bar")
- expect(@resource.source).to eql(["file:///C:/foo bar"])
+ it "accepts file URIs with spaces" do
+ resource.source("file:///C:/foo bar")
+ expect(resource.source).to eql(["file:///C:/foo bar"])
end
- it "should accept a delayed evalutator (string) for the remote file source" do
- @resource.source Chef::DelayedEvaluator.new { "http://opscode.com/" }
- expect(@resource.source).to eql([ "http://opscode.com/" ])
+ it "accepts a delayed evalutator (string) for the remote file source" do
+ resource.source Chef::DelayedEvaluator.new { "http://opscode.com/" }
+ expect(resource.source).to eql([ "http://opscode.com/" ])
end
- it "should accept an array of URIs for the remote file source" do
- @resource.source([ "http://opscode.com/", "http://puppetlabs.com/" ])
- expect(@resource.source).to eql([ "http://opscode.com/", "http://puppetlabs.com/" ])
+ it "accepts an array of URIs for the remote file source" do
+ resource.source([ "http://opscode.com/", "http://puppetlabs.com/" ])
+ expect(resource.source).to eql([ "http://opscode.com/", "http://puppetlabs.com/" ])
end
- it "should accept a delated evaluator (array) for the remote file source" do
- @resource.source Chef::DelayedEvaluator.new { [ "http://opscode.com/", "http://puppetlabs.com/" ] }
- expect(@resource.source).to eql([ "http://opscode.com/", "http://puppetlabs.com/" ])
+ it "accepts a delated evaluator (array) for the remote file source" do
+ resource.source Chef::DelayedEvaluator.new { [ "http://opscode.com/", "http://puppetlabs.com/" ] }
+ expect(resource.source).to eql([ "http://opscode.com/", "http://puppetlabs.com/" ])
end
- it "should accept an multiple URIs as arguments for the remote file source" do
- @resource.source("http://opscode.com/", "http://puppetlabs.com/")
- expect(@resource.source).to eql([ "http://opscode.com/", "http://puppetlabs.com/" ])
+ it "accepts an multiple URIs as arguments for the remote file source" do
+ resource.source("http://opscode.com/", "http://puppetlabs.com/")
+ expect(resource.source).to eql([ "http://opscode.com/", "http://puppetlabs.com/" ])
end
- it "should only accept a single argument if a delayed evalutor is used" do
- expect {
- @resource.source("http://opscode.com/", Chef::DelayedEvaluator.new { "http://opscode.com/" })
- }.to raise_error(Chef::Exceptions::InvalidRemoteFileURI)
+ it "only accept a single argument if a delayed evalutor is used" do
+ expect do
+ resource.source("http://opscode.com/", Chef::DelayedEvaluator.new { "http://opscode.com/" })
+ end.to raise_error(Chef::Exceptions::InvalidRemoteFileURI)
end
- it "should only accept a single array item if a delayed evalutor is used" do
- expect {
- @resource.source(["http://opscode.com/", Chef::DelayedEvaluator.new { "http://opscode.com/" }])
- }.to raise_error(Chef::Exceptions::InvalidRemoteFileURI)
+ it "only accept a single array item if a delayed evalutor is used" do
+ expect do
+ resource.source(["http://opscode.com/", Chef::DelayedEvaluator.new { "http://opscode.com/" }])
+ end.to raise_error(Chef::Exceptions::InvalidRemoteFileURI)
end
it "does not accept a non-URI as the source" do
- expect { @resource.source("not-a-uri") }.to raise_error(Chef::Exceptions::InvalidRemoteFileURI)
+ expect { resource.source("not-a-uri") }.to raise_error(Chef::Exceptions::InvalidRemoteFileURI)
end
it "does not accept a non-URI as the source when read from a delayed evaluator" do
- expect {
- @resource.source(Chef::DelayedEvaluator.new { "not-a-uri" })
- @resource.source
- }.to raise_error(Chef::Exceptions::InvalidRemoteFileURI)
+ expect do
+ resource.source(Chef::DelayedEvaluator.new { "not-a-uri" })
+ resource.source
+ end.to raise_error(Chef::Exceptions::InvalidRemoteFileURI)
end
- it "should raise an exception when source is an empty array" do
- expect { @resource.source([]) }.to raise_error(ArgumentError)
+ it "raises an exception when source is an empty array" do
+ expect { resource.source([]) }.to raise_error(ArgumentError)
end
end
describe "checksum" do
- it "should accept a string for the checksum object" do
- @resource.checksum "asdf"
- expect(@resource.checksum).to eql("asdf")
+ it "accepts a string for the checksum object" do
+ resource.checksum "asdf"
+ expect(resource.checksum).to eql("asdf")
end
- it "should default to nil" do
- expect(@resource.checksum).to eq(nil)
+ it "defaults to nil" do
+ expect(resource.checksum).to eq(nil)
end
end
describe "ftp_active_mode" do
- it "should accept a boolean for the ftp_active_mode object" do
- @resource.ftp_active_mode true
- expect(@resource.ftp_active_mode).to be_truthy
+ it "accepts a boolean for the ftp_active_mode object" do
+ resource.ftp_active_mode true
+ expect(resource.ftp_active_mode).to be_truthy
end
- it "should default to false" do
- expect(@resource.ftp_active_mode).to be_falsey
+ it "defaults to false" do
+ expect(resource.ftp_active_mode).to be_falsey
end
end
describe "conditional get options" do
it "defaults to using etags and last modified" do
- expect(@resource.use_etags).to be_truthy
- expect(@resource.use_last_modified).to be_truthy
+ expect(resource.use_etags).to be_truthy
+ expect(resource.use_last_modified).to be_truthy
end
it "enable or disables etag and last modified options as a group" do
- @resource.use_conditional_get(false)
- expect(@resource.use_etags).to be_falsey
- expect(@resource.use_last_modified).to be_falsey
+ resource.use_conditional_get(false)
+ expect(resource.use_etags).to be_falsey
+ expect(resource.use_last_modified).to be_falsey
- @resource.use_conditional_get(true)
- expect(@resource.use_etags).to be_truthy
- expect(@resource.use_last_modified).to be_truthy
+ resource.use_conditional_get(true)
+ expect(resource.use_etags).to be_truthy
+ expect(resource.use_last_modified).to be_truthy
end
it "disables etags indivdually" do
- @resource.use_etags(false)
- expect(@resource.use_etags).to be_falsey
- expect(@resource.use_last_modified).to be_truthy
+ resource.use_etags(false)
+ expect(resource.use_etags).to be_falsey
+ expect(resource.use_last_modified).to be_truthy
end
it "disables last modified individually" do
- @resource.use_last_modified(false)
- expect(@resource.use_last_modified).to be_falsey
- expect(@resource.use_etags).to be_truthy
+ resource.use_last_modified(false)
+ expect(resource.use_last_modified).to be_falsey
+ expect(resource.use_etags).to be_truthy
end
end
@@ -169,21 +163,21 @@ describe Chef::Resource::RemoteFile do
describe "when it has group, mode, owner, source, and checksum" do
before do
if Chef::Platform.windows?
- @resource.path("C:/temp/origin/file.txt")
- @resource.rights(:read, "Everyone")
- @resource.deny_rights(:full_control, "Clumsy_Sam")
+ resource.path("C:/temp/origin/file.txt")
+ resource.rights(:read, "Everyone")
+ resource.deny_rights(:full_control, "Clumsy_Sam")
else
- @resource.path("/this/path/")
- @resource.group("pokemon")
- @resource.mode("0664")
- @resource.owner("root")
+ resource.path("/this/path/")
+ resource.group("pokemon")
+ resource.mode("0664")
+ resource.owner("root")
end
- @resource.source("https://www.google.com/images/srpr/logo3w.png")
- @resource.checksum("1" * 26)
+ resource.source("https://www.google.com/images/srpr/logo3w.png")
+ resource.checksum("1" * 26)
end
it "describes its state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
if Chef::Platform.windows?
puts state
expect(state[:rights]).to eq([{ :permissions => :read, :principals => "Everyone" }])
@@ -198,9 +192,9 @@ describe Chef::Resource::RemoteFile do
it "returns the path as its identity" do
if Chef::Platform.windows?
- expect(@resource.identity).to eq("C:/temp/origin/file.txt")
+ expect(resource.identity).to eq("C:/temp/origin/file.txt")
else
- expect(@resource.identity).to eq("/this/path/")
+ expect(resource.identity).to eq("/this/path/")
end
end
end
diff --git a/spec/unit/resource/rhsm_errata_level_spec.rb b/spec/unit/resource/rhsm_errata_level_spec.rb
new file mode 100644
index 0000000000..3284107e75
--- /dev/null
+++ b/spec/unit/resource/rhsm_errata_level_spec.rb
@@ -0,0 +1,46 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::RhsmErrataLevel do
+
+ let(:resource) { Chef::Resource::RhsmErrataLevel.new("moderate") }
+
+ it "has a resource name of :rhsm_errata_level" do
+ expect(resource.resource_name).to eql(:rhsm_errata_level)
+ end
+
+ it "has a default action of install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "the errata_level property is the name property" do
+ expect(resource.errata_level).to eql("moderate")
+ end
+
+ it "coerces the errata_level to be lowercase" do
+ resource.errata_level "Important"
+ expect(resource.errata_level).to eql("important")
+ end
+
+ it "raises an exception if invalid errata_level is passed" do
+ expect do
+ resource.errata_level "FOO"
+ end.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+end
diff --git a/spec/unit/resource/rhsm_errata_spec.rb b/spec/unit/resource/rhsm_errata_spec.rb
new file mode 100644
index 0000000000..8da7269ca8
--- /dev/null
+++ b/spec/unit/resource/rhsm_errata_spec.rb
@@ -0,0 +1,35 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::RhsmErrata do
+
+ let(:resource) { Chef::Resource::RhsmErrata.new("foo") }
+
+ it "has a resource name of :rhsm_errata" do
+ expect(resource.resource_name).to eql(:rhsm_errata)
+ end
+
+ it "has a default action of install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "the errata_id property is the name property" do
+ expect(resource.errata_id).to eql("foo")
+ end
+end
diff --git a/spec/unit/resource/rhsm_register_spec.rb b/spec/unit/resource/rhsm_register_spec.rb
new file mode 100644
index 0000000000..2e360b5708
--- /dev/null
+++ b/spec/unit/resource/rhsm_register_spec.rb
@@ -0,0 +1,199 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::RhsmRegister do
+
+ let(:resource) { Chef::Resource::RhsmRegister.new("foo") }
+ let(:provider) { resource.provider_for_action(:register) }
+
+ it "has a resource name of :rhsm_register" do
+ expect(resource.resource_name).to eql(:rhsm_register)
+ end
+
+ it "has a default action of register" do
+ expect(resource.action).to eql([:register])
+ end
+
+ it "coerces activation_key to an array" do
+ resource.activation_key "foo"
+ expect(resource.activation_key).to eql(["foo"])
+ end
+
+ describe "#katello_cert_rpm_installed?" do
+ let(:cmd) { double("cmd") }
+
+ before do
+ allow(Mixlib::ShellOut).to receive(:new).and_return(cmd)
+ allow(cmd).to receive(:run_command)
+ end
+
+ context "when the output contains katello-ca-consumer" do
+ it "returns true" do
+ allow(cmd).to receive(:stdout).and_return("katello-ca-consumer-somehostname-1.0-1")
+ expect(provider.katello_cert_rpm_installed?).to eq(true)
+ end
+ end
+
+ context "when the output does not contain katello-ca-consumer" do
+ it "returns false" do
+ allow(cmd).to receive(:stdout).and_return("katello-agent-but-not-the-ca")
+ expect(provider.katello_cert_rpm_installed?).to eq(false)
+ end
+ end
+ end
+
+ describe "#register_command" do
+ before do
+ allow(provider).to receive(:activation_key).and_return([])
+ allow(provider).to receive(:auto_attach)
+ end
+
+ context "when activation keys exist" do
+ before do
+ allow(resource).to receive(:activation_key).and_return(%w{key1 key2})
+ end
+
+ context "when no org exists" do
+ it "raises an exception" do
+ allow(resource).to receive(:organization).and_return(nil)
+ expect { provider.register_command }.to raise_error(RuntimeError)
+ end
+ end
+
+ context "when an org exists" do
+ it "returns a command containing the keys and org" do
+ allow(resource).to receive(:organization).and_return("myorg")
+
+ expect(provider.register_command).to match("--activationkey=key1 --activationkey=key2 --org=myorg")
+ end
+ end
+
+ context "when auto_attach is true" do
+ it "does not return a command with --auto-attach since it is not supported with activation keys" do
+ allow(resource).to receive(:organization).and_return("myorg")
+ allow(resource).to receive(:auto_attach).and_return(true)
+
+ expect(provider.register_command).not_to match("--auto-attach")
+ end
+ end
+ end
+
+ context "when username and password exist" do
+ before do
+ allow(resource).to receive(:username).and_return("myuser")
+ allow(resource).to receive(:password).and_return("mypass")
+ allow(resource).to receive(:environment)
+ allow(resource).to receive(:using_satellite_host?)
+ allow(resource).to receive(:activation_key).and_return([])
+ end
+
+ context "when auto_attach is true" do
+ it "returns a command containing --auto-attach" do
+ allow(resource).to receive(:auto_attach).and_return(true)
+
+ expect(provider.register_command).to match("--auto-attach")
+ end
+ end
+
+ context "when auto_attach is false" do
+ it "returns a command that does not contain --auto-attach" do
+ allow(resource).to receive(:auto_attach).and_return(false)
+
+ expect(provider.register_command).not_to match("--auto-attach")
+ end
+ end
+
+ context "when auto_attach is nil" do
+ it "returns a command that does not contain --auto-attach" do
+ allow(resource).to receive(:auto_attach).and_return(nil)
+
+ expect(provider.register_command).not_to match("--auto-attach")
+ end
+ end
+
+ context "when environment does not exist" do
+ context "when registering to a satellite server" do
+ it "raises an exception" do
+ allow(provider).to receive(:using_satellite_host?).and_return(true)
+ allow(resource).to receive(:environment).and_return(nil)
+ expect { provider.register_command }.to raise_error(RuntimeError)
+ end
+ end
+
+ context "when registering to RHSM proper" do
+ before do
+ allow(provider).to receive(:using_satellite_host?).and_return(false)
+ allow(resource).to receive(:environment).and_return(nil)
+ end
+
+ it "does not raise an exception" do
+ expect { provider.register_command }.not_to raise_error
+ end
+
+ it "returns a command containing the username and password and no environment" do
+ allow(resource).to receive(:environment).and_return("myenv")
+ expect(provider.register_command).to match("--username=myuser --password=mypass")
+ expect(provider.register_command).not_to match("--environment")
+ end
+ end
+ end
+
+ context "when an environment exists" do
+ it "returns a command containing the username, password, and environment" do
+ allow(provider).to receive(:using_satellite_host?).and_return(true)
+ allow(resource).to receive(:environment).and_return("myenv")
+ expect(provider.register_command).to match("--username=myuser --password=mypass --environment=myenv")
+ end
+ end
+ end
+
+ context "when no activation keys, username, or password exist" do
+ it "raises an exception" do
+ allow(resource).to receive(:activation_key).and_return([])
+ allow(resource).to receive(:username).and_return(nil)
+ allow(resource).to receive(:password).and_return(nil)
+
+ expect { provider.register_command }.to raise_error(RuntimeError)
+ end
+ end
+ end
+
+ describe "#registered_with_rhsm?" do
+ let(:cmd) { double("cmd") }
+
+ before do
+ allow(Mixlib::ShellOut).to receive(:new).and_return(cmd)
+ allow(cmd).to receive(:run_command)
+ end
+
+ context "when the status is Unknown" do
+ it "returns false" do
+ allow(cmd).to receive(:stdout).and_return("Overall Status: Unknown")
+ expect(provider.registered_with_rhsm?).to eq(false)
+ end
+ end
+
+ context "when the status is anything else" do
+ it "returns true" do
+ allow(cmd).to receive(:stdout).and_return("Overall Status: Insufficient")
+ expect(provider.registered_with_rhsm?).to eq(true)
+ end
+ end
+ end
+end
diff --git a/spec/unit/resource/rhsm_repo_spec.rb b/spec/unit/resource/rhsm_repo_spec.rb
new file mode 100644
index 0000000000..97606a03c8
--- /dev/null
+++ b/spec/unit/resource/rhsm_repo_spec.rb
@@ -0,0 +1,59 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::RhsmRepo do
+
+ let(:resource) { Chef::Resource::RhsmRepo.new("foo") }
+ let(:provider) { resource.provider_for_action(:enable) }
+
+ it "has a resource name of :rhsm_repo" do
+ expect(resource.resource_name).to eql(:rhsm_repo)
+ end
+
+ it "has a default action of enable" do
+ expect(resource.action).to eql([:enable])
+ end
+
+ it "the repo_name property is the name property" do
+ expect(resource.repo_name).to eql("foo")
+ end
+
+ describe "#repo_enabled?" do
+ let(:cmd) { double("cmd") }
+ let(:output) { "Repo ID: repo123" }
+
+ before do
+ allow(Mixlib::ShellOut).to receive(:new).and_return(cmd)
+ allow(cmd).to receive(:run_command)
+ allow(cmd).to receive(:stdout).and_return(output)
+ end
+
+ context "when the repo provided matches the output" do
+ it "returns true" do
+ expect(provider.repo_enabled?("repo123")).to eq(true)
+ end
+ end
+
+ context "when the repo provided does not match the output" do
+ it "returns false" do
+ expect(provider.repo_enabled?("differentrepo")).to eq(false)
+ end
+ end
+ end
+end
diff --git a/spec/unit/resource/rhsm_subscription_spec.rb b/spec/unit/resource/rhsm_subscription_spec.rb
new file mode 100644
index 0000000000..0160624f39
--- /dev/null
+++ b/spec/unit/resource/rhsm_subscription_spec.rb
@@ -0,0 +1,93 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::RhsmSubscription do
+ let(:resource) { Chef::Resource::RhsmSubscription.new("foo") }
+ let(:provider) { resource.provider_for_action(:attach) }
+
+ it "has a resource name of :rhsm_subscription" do
+ expect(resource.resource_name).to eql(:rhsm_subscription)
+ end
+
+ it "has a default action of attach" do
+ expect(resource.action).to eql([:attach])
+ end
+
+ it "the pool_id property is the name property" do
+ expect(resource.pool_id).to eql("foo")
+ end
+
+ describe "#subscription_attached?" do
+ let(:cmd) { double("cmd") }
+ let(:output) { "Pool ID: pool123" }
+
+ before do
+ allow(Mixlib::ShellOut).to receive(:new).and_return(cmd)
+ allow(cmd).to receive(:run_command)
+ allow(cmd).to receive(:stdout).and_return(output)
+ end
+
+ context "when the pool provided matches the output" do
+ it "returns true" do
+ expect(provider.subscription_attached?("pool123")).to eq(true)
+ end
+ end
+
+ context "when the pool provided does not match the output" do
+ it "returns false" do
+ expect(provider.subscription_attached?("differentpool")).to eq(false)
+ end
+ end
+ end
+
+ describe "#serials_by_pool" do
+ let(:cmd) { double("cmd") }
+ let(:output) do
+ <<~EOL
+ Key1: value1
+ Pool ID: pool1
+ Serial: serial1
+ Key2: value2
+
+ Key1: value1
+ Pool ID: pool2
+ Serial: serial2
+ Key2: value2
+EOL
+ end
+
+ it "parses the output correctly" do
+ allow(Mixlib::ShellOut).to receive(:new).and_return(cmd)
+ allow(cmd).to receive(:run_command)
+ allow(cmd).to receive(:stdout).and_return(output)
+
+ expect(provider.serials_by_pool["pool1"]).to eq("serial1")
+ expect(provider.serials_by_pool["pool2"]).to eq("serial2")
+ end
+ end
+
+ describe "#pool_serial" do
+ let(:serials) { { "pool1" => "serial1", "pool2" => "serial2" } }
+
+ it "returns the serial for a given pool" do
+ allow(provider).to receive(:serials_by_pool).and_return(serials)
+ expect(provider.pool_serial("pool1")).to eq("serial1")
+ end
+ end
+end
diff --git a/spec/unit/resource/route_spec.rb b/spec/unit/resource/route_spec.rb
index 259ccf7eab..899012b9dc 100644
--- a/spec/unit/resource/route_spec.rb
+++ b/spec/unit/resource/route_spec.rb
@@ -21,87 +21,80 @@ require "spec_helper"
describe Chef::Resource::Route do
- before(:each) do
- @resource = Chef::Resource::Route.new("10.0.0.10")
- end
-
- it "should create a new Chef::Resource::Route" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Route)
- end
+ let(:resource) { Chef::Resource::Route.new("10.0.0.10") }
- it "should have a name" do
- expect(@resource.name).to eql("10.0.0.10")
+ it "has a name property" do
+ expect(resource.name).to eql("10.0.0.10")
end
- it "should have a default action of 'add'" do
- expect(@resource.action).to eql([:add])
+ it "has a default action of 'add'" do
+ expect(resource.action).to eql([:add])
end
- it "should accept add or delete for action" do
- expect { @resource.action :add }.not_to raise_error
- expect { @resource.action :delete }.not_to raise_error
- expect { @resource.action :lolcat }.to raise_error(ArgumentError)
+ it "accepts add or delete for action" do
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :lolcat }.to raise_error(ArgumentError)
end
- it "should use the object name as the target by default" do
- expect(@resource.target).to eql("10.0.0.10")
+ it "uses the object name as the target by default" do
+ expect(resource.target).to eql("10.0.0.10")
end
- it "should allow you to specify the netmask" do
- @resource.netmask "255.255.255.0"
- expect(@resource.netmask).to eql("255.255.255.0")
+ it "allows you to specify the netmask" do
+ resource.netmask "255.255.255.0"
+ expect(resource.netmask).to eql("255.255.255.0")
end
- it "should allow you to specify the gateway" do
- @resource.gateway "10.0.0.1"
- expect(@resource.gateway).to eql("10.0.0.1")
+ it "allows you to specify the gateway" do
+ resource.gateway "10.0.0.1"
+ expect(resource.gateway).to eql("10.0.0.1")
end
- it "should allow you to specify the metric" do
- @resource.metric 10
- expect(@resource.metric).to eql(10)
+ it "allows you to specify the metric" do
+ resource.metric 10
+ expect(resource.metric).to eql(10)
end
- it "should allow you to specify the device" do
- @resource.device "eth0"
- expect(@resource.device).to eql("eth0")
+ it "allows you to specify the device" do
+ resource.device "eth0"
+ expect(resource.device).to eql("eth0")
end
- it "should allow you to specify the route type" do
- @resource.route_type "host"
- expect(@resource.route_type).to eql(:host)
+ it "allows you to specify the route type" do
+ resource.route_type "host"
+ expect(resource.route_type).to eql(:host)
end
- it "should default to a host route type" do
- expect(@resource.route_type).to eql(:host)
+ it "defaults to a host route type" do
+ expect(resource.route_type).to eql(:host)
end
- it "should accept a net route type" do
- @resource.route_type :net
- expect(@resource.route_type).to eql(:net)
+ it "accepts a net route type" do
+ resource.route_type :net
+ expect(resource.route_type).to eql(:net)
end
- it "should reject any other route_type but :host and :net" do
- expect { @resource.route_type "lolcat" }.to raise_error(ArgumentError)
+ it "rejects any other route_type but :host and :net" do
+ expect { resource.route_type "lolcat" }.to raise_error(ArgumentError)
end
describe "when it has netmask, gateway, and device" do
before do
- @resource.target("charmander")
- @resource.netmask("lemask")
- @resource.gateway("111.111.111")
- @resource.device("forcefield")
+ resource.target("charmander")
+ resource.netmask("lemask")
+ resource.gateway("111.111.111")
+ resource.device("forcefield")
end
it "describes its state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:netmask]).to eq("lemask")
expect(state[:gateway]).to eq("111.111.111")
end
it "returns the target as its identity" do
- expect(@resource.identity).to eq("charmander")
+ expect(resource.identity).to eq("charmander")
end
end
end
diff --git a/spec/unit/resource/rpm_package_spec.rb b/spec/unit/resource/rpm_package_spec.rb
index e1488f1dd1..0104e10dae 100644
--- a/spec/unit/resource/rpm_package_spec.rb
+++ b/spec/unit/resource/rpm_package_spec.rb
@@ -34,13 +34,15 @@ describe Chef::Resource::RpmPackage, "initialize" do
end
describe Chef::Resource::RpmPackage, "allow_downgrade" do
- before(:each) do
- @resource = Chef::Resource::RpmPackage.new("foo")
+ let(:resource) { Chef::Resource::RpmPackage.new("foo") }
+
+ it "is a subclass of Chef::Resource::Package" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Package)
end
- it "should allow you to specify whether allow_downgrade is true or false" do
- expect { @resource.allow_downgrade true }.not_to raise_error
- expect { @resource.allow_downgrade false }.not_to raise_error
- expect { @resource.allow_downgrade "monkey" }.to raise_error(ArgumentError)
+ it "allows you to specify whether allow_downgrade is true or false" do
+ expect { resource.allow_downgrade true }.not_to raise_error
+ expect { resource.allow_downgrade false }.not_to raise_error
+ expect { resource.allow_downgrade "monkey" }.to raise_error(ArgumentError)
end
end
diff --git a/spec/unit/resource/ruby_block_spec.rb b/spec/unit/resource/ruby_block_spec.rb
index f393fbea46..fd0da32123 100644
--- a/spec/unit/resource/ruby_block_spec.rb
+++ b/spec/unit/resource/ruby_block_spec.rb
@@ -21,41 +21,34 @@ require "spec_helper"
describe Chef::Resource::RubyBlock do
- before(:each) do
- @resource = Chef::Resource::RubyBlock.new("fakey_fakerton")
- end
-
- it "should create a new Chef::Resource::RubyBlock" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::RubyBlock)
- end
+ let(:resource) { Chef::Resource::RubyBlock.new("fakey_fakerton") }
- it "should have a default action of 'run'" do
- expect(@resource.action).to eql([:run])
+ it "has a default action of 'run'" do
+ expect(resource.action).to eql([:run])
end
- it "should have a resource name of :ruby_block" do
- expect(@resource.resource_name).to eql(:ruby_block)
+ it "has a resource name of :ruby_block" do
+ expect(resource.resource_name).to eql(:ruby_block)
end
- it "should accept a ruby block/proc/.. for the 'block' parameter" do
- expect(@resource.block do
+ it "accepts a ruby block/proc/.. for the 'block' parameter" do
+ expect(resource.block do
"foo"
end.call).to eql("foo")
end
it "allows the action to be 'create'" do
- @resource.action :create
- expect(@resource.action).to eq([:create])
+ resource.action :create
+ expect(resource.action).to eq([:create])
end
describe "when it has been initialized with block code" do
before do
- @resource.block_name("puts 'harrrr'")
+ resource.block_name("puts 'harrrr'")
end
it "returns the block as its identity" do
- expect(@resource.identity).to eq("puts 'harrrr'")
+ expect(resource.identity).to eq("puts 'harrrr'")
end
end
end
diff --git a/spec/unit/resource/ruby_spec.rb b/spec/unit/resource/ruby_spec.rb
index d7b6759462..4327917295 100644
--- a/spec/unit/resource/ruby_spec.rb
+++ b/spec/unit/resource/ruby_spec.rb
@@ -19,22 +19,13 @@
require "spec_helper"
describe Chef::Resource::Ruby do
+ let(:resource) { Chef::Resource::Ruby.new("fakey_fakerton") }
- before(:each) do
- @resource = Chef::Resource::Ruby.new("fakey_fakerton")
+ it "has a resource name of :ruby" do
+ expect(resource.resource_name).to eql(:ruby)
end
- it "should create a new Chef::Resource::Ruby" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Ruby)
+ it "has an interpreter of ruby" do
+ expect(resource.interpreter).to eql("ruby")
end
-
- it "should have a resource name of :ruby" do
- expect(@resource.resource_name).to eql(:ruby)
- end
-
- it "should have an interpreter of ruby" do
- expect(@resource.interpreter).to eql("ruby")
- end
-
end
diff --git a/spec/unit/resource/scm_spec.rb b/spec/unit/resource/scm_spec.rb
index f39334348e..33c2f17498 100644
--- a/spec/unit/resource/scm_spec.rb
+++ b/spec/unit/resource/scm_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Daniel DeLeo (<dan@kallistec.com>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,174 +20,156 @@
require "spec_helper"
describe Chef::Resource::Scm do
-
- before(:each) do
- @resource = Chef::Resource::Scm.new("my awesome app")
- end
-
- it "should be a SCM resource" do
- expect(@resource).to be_a_kind_of(Chef::Resource::Scm)
- end
+ let(:resource) { Chef::Resource::Scm.new("my awesome app") }
it "supports :checkout, :export, :sync, :diff, and :log actions" do
- expect(@resource.allowed_actions).to include(:checkout)
- expect(@resource.allowed_actions).to include(:export)
- expect(@resource.allowed_actions).to include(:sync)
- expect(@resource.allowed_actions).to include(:diff)
- expect(@resource.allowed_actions).to include(:log)
+ expect(resource.allowed_actions).to include(:checkout)
+ expect(resource.allowed_actions).to include(:export)
+ expect(resource.allowed_actions).to include(:sync)
+ expect(resource.allowed_actions).to include(:diff)
+ expect(resource.allowed_actions).to include(:log)
end
it "takes the destination path as a string" do
- @resource.destination "/path/to/deploy/dir"
- expect(@resource.destination).to eql("/path/to/deploy/dir")
+ resource.destination "/path/to/deploy/dir"
+ expect(resource.destination).to eql("/path/to/deploy/dir")
end
it "takes a string for the repository URL" do
- @resource.repository "git://github.com/opscode/chef.git"
- expect(@resource.repository).to eql("git://github.com/opscode/chef.git")
+ resource.repository "git://github.com/opscode/chef.git"
+ expect(resource.repository).to eql("git://github.com/opscode/chef.git")
end
it "takes a string for the revision" do
- @resource.revision "abcdef"
- expect(@resource.revision).to eql("abcdef")
+ resource.revision "abcdef"
+ expect(resource.revision).to eql("abcdef")
end
it "defaults to the ``HEAD'' revision" do
- expect(@resource.revision).to eql("HEAD")
+ expect(resource.revision).to eql("HEAD")
end
it "takes a string for the user to run as" do
- @resource.user "dr_deploy"
- expect(@resource.user).to eql("dr_deploy")
+ resource.user "dr_deploy"
+ expect(resource.user).to eql("dr_deploy")
end
it "also takes an integer for the user to run as" do
- @resource.user 0
- expect(@resource.user).to eql(0)
+ resource.user 0
+ expect(resource.user).to eql(0)
end
it "takes a string for the group to run as, defaulting to nil" do
- expect(@resource.group).to be_nil
- @resource.group "opsdevs"
- expect(@resource.group).to eq("opsdevs")
+ expect(resource.group).to be_nil
+ resource.group "opsdevs"
+ expect(resource.group).to eq("opsdevs")
end
it "also takes an integer for the group to run as" do
- @resource.group 23
- expect(@resource.group).to eq(23)
+ resource.group 23
+ expect(resource.group).to eq(23)
end
it "has a svn_username String attribute" do
- @resource.svn_username "moartestsplz"
- expect(@resource.svn_username).to eql("moartestsplz")
+ resource.svn_username "moartestsplz"
+ expect(resource.svn_username).to eql("moartestsplz")
end
it "has a svn_password String attribute" do
- @resource.svn_password "taftplz"
- expect(@resource.svn_password).to eql("taftplz")
- end
-
- it "has a svn_arguments String attribute" do
- @resource.svn_arguments "--more-taft plz"
- expect(@resource.svn_arguments).to eql("--more-taft plz")
- end
-
- it "has a svn_info_args String attribute" do
- expect(@resource.svn_info_args).to be_nil
- @resource.svn_info_args("--no-moar-plaintext-creds yep")
- expect(@resource.svn_info_args).to eq("--no-moar-plaintext-creds yep")
+ resource.svn_password "taftplz"
+ expect(resource.svn_password).to eql("taftplz")
end
it "takes the depth as an integer for shallow clones" do
- @resource.depth 5
- expect(@resource.depth).to eq(5)
- expect { @resource.depth "five" }.to raise_error(ArgumentError)
+ resource.depth 5
+ expect(resource.depth).to eq(5)
+ expect { resource.depth "five" }.to raise_error(ArgumentError)
end
it "defaults to nil depth for a full clone" do
- expect(@resource.depth).to be_nil
+ expect(resource.depth).to be_nil
end
it "takes a boolean for #enable_submodules" do
- @resource.enable_submodules true
- expect(@resource.enable_submodules).to be_truthy
- expect { @resource.enable_submodules "lolz" }.to raise_error(ArgumentError)
+ resource.enable_submodules true
+ expect(resource.enable_submodules).to be_truthy
+ expect { resource.enable_submodules "lolz" }.to raise_error(ArgumentError)
end
it "defaults to not enabling submodules" do
- expect(@resource.enable_submodules).to be_falsey
+ expect(resource.enable_submodules).to be_falsey
end
it "takes a boolean for #enable_checkout" do
- @resource.enable_checkout true
- expect(@resource.enable_checkout).to be_truthy
- expect { @resource.enable_checkout "lolz" }.to raise_error(ArgumentError)
+ resource.enable_checkout true
+ expect(resource.enable_checkout).to be_truthy
+ expect { resource.enable_checkout "lolz" }.to raise_error(ArgumentError)
end
it "defaults to enabling checkout" do
- expect(@resource.enable_checkout).to be_truthy
+ expect(resource.enable_checkout).to be_truthy
end
it "takes a string for the remote" do
- @resource.remote "opscode"
- expect(@resource.remote).to eql("opscode")
- expect { @resource.remote 1337 }.to raise_error(ArgumentError)
+ resource.remote "opscode"
+ expect(resource.remote).to eql("opscode")
+ expect { resource.remote 1337 }.to raise_error(ArgumentError)
end
it "defaults to ``origin'' for the remote" do
- expect(@resource.remote).to eq("origin")
+ expect(resource.remote).to eq("origin")
end
it "takes a string for the ssh wrapper" do
- @resource.ssh_wrapper "with_ssh_fu"
- expect(@resource.ssh_wrapper).to eql("with_ssh_fu")
+ resource.ssh_wrapper "with_ssh_fu"
+ expect(resource.ssh_wrapper).to eql("with_ssh_fu")
end
it "defaults to nil for the ssh wrapper" do
- expect(@resource.ssh_wrapper).to be_nil
+ expect(resource.ssh_wrapper).to be_nil
end
it "defaults to nil for the environment" do
- expect(@resource.environment).to be_nil
+ expect(resource.environment).to be_nil
end
describe "when it has a timeout attribute" do
let(:ten_seconds) { 10 }
- before { @resource.timeout(ten_seconds) }
+ before { resource.timeout(ten_seconds) }
it "stores this timeout" do
- expect(@resource.timeout).to eq(ten_seconds)
+ expect(resource.timeout).to eq(ten_seconds)
end
end
describe "when it has no timeout attribute" do
- it "should have no default timeout" do
- expect(@resource.timeout).to be_nil
+ it "has no default timeout" do
+ expect(resource.timeout).to be_nil
end
end
describe "when it has repository, revision, user, and group" do
before do
- @resource.destination("hell")
- @resource.repository("apt")
- @resource.revision("1.2.3")
- @resource.user("root")
- @resource.group("super_adventure_club")
+ resource.destination("hell")
+ resource.repository("apt")
+ resource.revision("1.2.3")
+ resource.user("root")
+ resource.group("super_adventure_club")
end
it "describes its state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:revision]).to eq("1.2.3")
end
it "returns the destination as its identity" do
- expect(@resource.identity).to eq("hell")
+ expect(resource.identity).to eq("hell")
end
end
describe "when it has a environment attribute" do
let(:test_environment) { { "CHEF_ENV" => "/tmp" } }
- before { @resource.environment(test_environment) }
+ before { resource.environment(test_environment) }
it "stores this environment" do
- expect(@resource.environment).to eq(test_environment)
+ expect(resource.environment).to eq(test_environment)
end
end
end
diff --git a/spec/unit/resource/script_spec.rb b/spec/unit/resource/script_spec.rb
index fca9fb0d7b..554d99710e 100644
--- a/spec/unit/resource/script_spec.rb
+++ b/spec/unit/resource/script_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,7 @@ describe Chef::Resource::Script do
let(:script_resource) { Chef::Resource::Script.new(resource_instance_name) }
let(:resource_name) { :script }
- it "should accept a string for the interpreter" do
+ it "accepts a string for the interpreter" do
script_resource.interpreter "naaaaNaNaNaaNaaNaaNaa"
expect(script_resource.interpreter).to eql("naaaaNaNaNaaNaaNaaNaa")
end
diff --git a/spec/unit/resource/service_spec.rb b/spec/unit/resource/service_spec.rb
index e06f5bca5e..335ad76fc8 100644
--- a/spec/unit/resource/service_spec.rb
+++ b/spec/unit/resource/service_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: AJ Christensen (<aj@hjksolutions.com>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,160 +20,172 @@
require "spec_helper"
describe Chef::Resource::Service do
+ let(:resource) { Chef::Resource::Service.new("chef") }
- before(:each) do
- @resource = Chef::Resource::Service.new("chef")
+ it "does not set a provider unless node[:init_package] is defined as systemd" do
+ expect(resource.provider).to eq(nil)
end
- it "should create a new Chef::Resource::Service" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Service)
+ it "sets the service_name to the first argument to new" do
+ expect(resource.service_name).to eql("chef")
end
- it "should not set a provider unless node[:init_package] is defined as systemd" do
- expect(@resource.provider).to eq(nil)
+ it "sets the pattern to be the service name by default" do
+ expect(resource.pattern).to eql("chef")
end
- it "should set the service_name to the first argument to new" do
- expect(@resource.service_name).to eql("chef")
+ it "accepts a string for the service name" do
+ resource.service_name "something"
+ expect(resource.service_name).to eql("something")
end
- it "should set the pattern to be the service name by default" do
- expect(@resource.pattern).to eql("chef")
+ it "accepts a string for the service pattern" do
+ resource.pattern ".*"
+ expect(resource.pattern).to eql(".*")
end
- it "should accept a string for the service name" do
- @resource.service_name "something"
- expect(@resource.service_name).to eql("something")
+ it "does not accept a regexp for the service pattern" do
+ expect do
+ resource.pattern /.*/
+ end.to raise_error(ArgumentError)
end
- it "should accept a string for the service pattern" do
- @resource.pattern ".*"
- expect(@resource.pattern).to eql(".*")
+ it "accepts a string for the service start command" do
+ resource.start_command "/etc/init.d/chef start"
+ expect(resource.start_command).to eql("/etc/init.d/chef start")
end
- it "should not accept a regexp for the service pattern" do
- expect {
- @resource.pattern /.*/
- }.to raise_error(ArgumentError)
+ it "does not accept a regexp for the service start command" do
+ expect do
+ resource.start_command /.*/
+ end.to raise_error(ArgumentError)
end
- it "should accept a string for the service start command" do
- @resource.start_command "/etc/init.d/chef start"
- expect(@resource.start_command).to eql("/etc/init.d/chef start")
+ it "accepts a string for the service stop command" do
+ resource.stop_command "/etc/init.d/chef stop"
+ expect(resource.stop_command).to eql("/etc/init.d/chef stop")
end
- it "should not accept a regexp for the service start command" do
- expect {
- @resource.start_command /.*/
- }.to raise_error(ArgumentError)
+ it "does not accept a regexp for the service stop command" do
+ expect do
+ resource.stop_command /.*/
+ end.to raise_error(ArgumentError)
end
- it "should accept a string for the service stop command" do
- @resource.stop_command "/etc/init.d/chef stop"
- expect(@resource.stop_command).to eql("/etc/init.d/chef stop")
+ it "accepts a string for the service status command" do
+ resource.status_command "/etc/init.d/chef status"
+ expect(resource.status_command).to eql("/etc/init.d/chef status")
end
- it "should not accept a regexp for the service stop command" do
- expect {
- @resource.stop_command /.*/
- }.to raise_error(ArgumentError)
+ it "does not accept a regexp for the service status command" do
+ expect do
+ resource.status_command /.*/
+ end.to raise_error(ArgumentError)
end
- it "should accept a string for the service status command" do
- @resource.status_command "/etc/init.d/chef status"
- expect(@resource.status_command).to eql("/etc/init.d/chef status")
+ it "accepts a string for the service restart command" do
+ resource.restart_command "/etc/init.d/chef restart"
+ expect(resource.restart_command).to eql("/etc/init.d/chef restart")
end
- it "should not accept a regexp for the service status command" do
- expect {
- @resource.status_command /.*/
- }.to raise_error(ArgumentError)
+ it "does not accept a regexp for the service restart command" do
+ expect do
+ resource.restart_command /.*/
+ end.to raise_error(ArgumentError)
end
- it "should accept a string for the service restart command" do
- @resource.restart_command "/etc/init.d/chef restart"
- expect(@resource.restart_command).to eql("/etc/init.d/chef restart")
+ it "accepts a string for the service reload command" do
+ resource.reload_command "/etc/init.d/chef reload"
+ expect(resource.reload_command).to eql("/etc/init.d/chef reload")
end
- it "should not accept a regexp for the service restart command" do
- expect {
- @resource.restart_command /.*/
- }.to raise_error(ArgumentError)
+ it "does not accept a regexp for the service reload command" do
+ expect do
+ resource.reload_command /.*/
+ end.to raise_error(ArgumentError)
end
- it "should accept a string for the service reload command" do
- @resource.reload_command "/etc/init.d/chef reload"
- expect(@resource.reload_command).to eql("/etc/init.d/chef reload")
+ it "accepts a string for the service init command" do
+ resource.init_command "/etc/init.d/chef"
+ expect(resource.init_command).to eql("/etc/init.d/chef")
end
- it "should not accept a regexp for the service reload command" do
- expect {
- @resource.reload_command /.*/
- }.to raise_error(ArgumentError)
+ it "does not accept a regexp for the service init command" do
+ expect do
+ resource.init_command /.*/
+ end.to raise_error(ArgumentError)
end
- it "should accept a string for the service init command" do
- @resource.init_command "/etc/init.d/chef"
- expect(@resource.init_command).to eql("/etc/init.d/chef")
+ it "accepts an array for options" do
+ resource.options ["-r", "-s"]
+ expect(resource.options).to eql(["-r", "-s"])
end
- it "should not accept a regexp for the service init command" do
- expect {
- @resource.init_command /.*/
- }.to raise_error(ArgumentError)
+ it "accepts a string for options" do
+ resource.options "-r"
+ expect(resource.options).to eql(["-r"])
+ end
+
+ it "accepts a string with multiple flags for options" do
+ resource.options "-r -s"
+ expect(resource.options).to eql(["-r", "-s"])
+ end
+
+ it "does not accept a boolean for options" do
+ expect do
+ resource.options true
+ end.to raise_error(ArgumentError)
end
%w{enabled running}.each do |attrib|
- it "should accept true for #{attrib}" do
- @resource.send(attrib, true)
- expect(@resource.send(attrib)).to eql(true)
+ it "accepts true for #{attrib}" do
+ resource.send(attrib, true)
+ expect(resource.send(attrib)).to eql(true)
end
- it "should accept false for #{attrib}" do
- @resource.send(attrib, false)
- expect(@resource.send(attrib)).to eql(false)
+ it "accepts false for #{attrib}" do
+ resource.send(attrib, false)
+ expect(resource.send(attrib)).to eql(false)
end
- it "should not accept a string for #{attrib}" do
- expect { @resource.send(attrib, "poop") }.to raise_error(ArgumentError)
+ it "does not accept a string for #{attrib}" do
+ expect { resource.send(attrib, "poop") }.to raise_error(ArgumentError)
end
- it "should default all the feature support to nil" do
+ it "defaults all the feature support to nil" do
support_hash = { :status => nil, :restart => nil, :reload => nil }
- expect(@resource.supports).to eq(support_hash)
+ expect(resource.supports).to eq(support_hash)
end
- it "should allow you to set what features this resource supports as a array" do
+ it "allows you to set what features this resource supports as a array" do
support_array = [ :status, :restart ]
- support_hash = { :status => true, :restart => true, :reload => nil }
- @resource.supports(support_array)
- expect(@resource.supports).to eq(support_hash)
+ support_hash = { :status => true, :restart => true }
+ resource.supports(support_array)
+ expect(resource.supports).to eq(support_hash)
end
- it "should allow you to set what features this resource supports as a hash" do
- support_hash = { :status => true, :restart => true, :reload => false }
- @resource.supports(support_hash)
- expect(@resource.supports).to eq(support_hash)
+ it "allows you to set what features this resource supports as a hash" do
+ support_hash = { :status => true, :restart => true }
+ resource.supports(support_hash)
+ expect(resource.supports).to eq(support_hash)
end
end
describe "when it has pattern and supports" do
before do
- @resource.service_name("superfriend")
- @resource.enabled(true)
- @resource.running(false)
+ resource.service_name("superfriend")
+ resource.enabled(true)
+ resource.running(false)
end
it "describes its state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:enabled]).to eql(true)
expect(state[:running]).to eql(false)
end
it "returns the service name as its identity" do
- expect(@resource.identity).to eq("superfriend")
+ expect(resource.identity).to eq("superfriend")
end
end
-
end
diff --git a/spec/unit/resource/smartos_package_spec.rb b/spec/unit/resource/smartos_package_spec.rb
index 56c0fbdb8b..a0bd5853f5 100644
--- a/spec/unit/resource/smartos_package_spec.rb
+++ b/spec/unit/resource/smartos_package_spec.rb
@@ -30,4 +30,9 @@ describe Chef::Resource::SmartosPackage, "initialize" do
platform_family: "smartos"
)
+ let(:resource) { Chef::Resource::SmartosPackage.new("foo") }
+
+ it "sets the package_name to the name provided" do
+ expect(resource.package_name).to eql("foo")
+ end
end
diff --git a/spec/unit/resource/solaris_package_spec.rb b/spec/unit/resource/solaris_package_spec.rb
index 1f1ef4da30..dda8240af8 100644
--- a/spec/unit/resource/solaris_package_spec.rb
+++ b/spec/unit/resource/solaris_package_spec.rb
@@ -32,11 +32,9 @@ describe Chef::Resource::SolarisPackage, "initialize" do
)
end
- before(:each) do
- @resource = Chef::Resource::SolarisPackage.new("foo")
- end
+ let(:resource) { Chef::Resource::SolarisPackage.new("foo") }
- it "should set the package_name to the name provided" do
- expect(@resource.package_name).to eql("foo")
+ it "sets the package_name to the name provided" do
+ expect(resource.package_name).to eql("foo")
end
end
diff --git a/spec/unit/resource/subversion_spec.rb b/spec/unit/resource/subversion_spec.rb
index a2901bf53b..dd6526e3dc 100644
--- a/spec/unit/resource/subversion_spec.rb
+++ b/spec/unit/resource/subversion_spec.rb
@@ -28,44 +28,58 @@ describe Chef::Resource::Subversion do
action: :install
)
- before do
- @svn = Chef::Resource::Subversion.new("ohai, svn project!")
- end
+ let(:resource) { Chef::Resource::Subversion.new("ohai, svn project!") }
it "is a subclass of Resource::Scm" do
- expect(@svn).to be_an_instance_of(Chef::Resource::Subversion)
- expect(@svn).to be_a_kind_of(Chef::Resource::Scm)
+ expect(resource).to be_an_instance_of(Chef::Resource::Subversion)
+ expect(resource).to be_a_kind_of(Chef::Resource::Scm)
+ end
+
+ it "set destination property to the name_property" do
+ expect(resource.destination).to eq("ohai, svn project!")
end
it "allows the force_export action" do
- expect(@svn.allowed_actions).to include(:force_export)
+ expect(resource.allowed_actions).to include(:force_export)
end
it "sets svn info arguments to --no-auth-cache by default" do
- expect(@svn.svn_info_args).to eq("--no-auth-cache")
+ expect(resource.svn_info_args).to eq("--no-auth-cache")
end
it "resets svn info arguments to nil when given false in the setter" do
- @svn.svn_info_args(false)
- expect(@svn.svn_info_args).to be_nil
+ resource.svn_info_args(false)
+ expect(resource.svn_info_args).to be_nil
end
it "sets svn arguments to --no-auth-cache by default" do
- expect(@svn.svn_arguments).to eq("--no-auth-cache")
+ expect(resource.svn_arguments).to eq("--no-auth-cache")
end
it "sets svn binary to nil by default" do
- expect(@svn.svn_binary).to be_nil
+ expect(resource.svn_binary).to be_nil
end
it "resets svn arguments to nil when given false in the setter" do
- @svn.svn_arguments(false)
- expect(@svn.svn_arguments).to be_nil
+ resource.svn_arguments(false)
+ expect(resource.svn_arguments).to be_nil
+ end
+
+ it "has a svn_arguments String attribute" do
+ expect(resource.svn_arguments).to eq("--no-auth-cache") # the default
+ resource.svn_arguments "--more-taft plz"
+ expect(resource.svn_arguments).to eql("--more-taft plz")
+ end
+
+ it "has a svn_info_args String attribute" do
+ expect(resource.svn_info_args).to eq("--no-auth-cache") # the default
+ resource.svn_info_args("--no-moar-plaintext-creds yep")
+ expect(resource.svn_info_args).to eq("--no-moar-plaintext-creds yep")
end
it "hides password from custom exception message" do
- @svn.svn_password "l33th4x0rpa$$w0rd"
- e = @svn.customize_exception(Chef::Exceptions::Exec.new "Exception with password #{@svn.svn_password}")
- expect(e.message.include?(@svn.svn_password)).to be_falsey
+ resource.svn_password "l33th4x0rpa$$w0rd"
+ e = resource.customize_exception(Chef::Exceptions::Exec.new "Exception with password #{resource.svn_password}")
+ expect(e.message.include?(resource.svn_password)).to be_falsey
end
end
diff --git a/spec/unit/resource/sudo_spec.rb b/spec/unit/resource/sudo_spec.rb
new file mode 100644
index 0000000000..8f5103597c
--- /dev/null
+++ b/spec/unit/resource/sudo_spec.rb
@@ -0,0 +1,92 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::Sudo do
+
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::Sudo.new("someone", run_context) }
+
+ it "has a resource name of :sudo" do
+ expect(resource.resource_name).to eql(:sudo)
+ end
+
+ it "has a default action of create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "the filename property is the name property" do
+ expect(resource.filename).to eql("someone")
+ end
+
+ it "coerces filename property values . & ~ to __" do
+ resource.filename "something.something~"
+ expect(resource.filename).to eql("something__something__")
+ end
+
+ it "supports the legacy 'user' property" do
+ resource.user ["foo"]
+ expect(resource.users).to eql(["foo"])
+ end
+
+ it "supports the legacy 'groups' property" do
+ resource.group ["%foo"]
+ expect(resource.groups).to eql(["%foo"])
+ end
+
+ it "coerces users & groups String vals to Arrays" do
+ resource.users "something"
+ resource.groups "%something"
+ expect(resource.users).to eql(["something"])
+ expect(resource.groups).to eql(["%something"])
+ end
+
+ it "coerces users & group String vals no matter the spacing" do
+ resource.users "user1, user2 , user3 ,user4"
+ resource.groups "group1, group2 , group3 ,group4"
+ expect(resource.users).to eql(%w{user1 user2 user3 user4})
+ expect(resource.groups).to eql(["%group1", "%group2", "%group3", "%group4"])
+ end
+
+ it "coerces groups values to properly start with %" do
+ resource.groups ["foo", "%bar"]
+ expect(resource.groups).to eql(["%foo", "%bar"])
+ end
+
+ it "it sets the config prefix to /etc on linux" do
+ node.automatic[:platform_family] = "debian"
+ expect(resource.config_prefix).to eql("/etc")
+ end
+
+ it "it sets the config prefix to /private/etc on macOS" do
+ node.automatic[:platform_family] = "mac_os_x"
+ expect(resource.config_prefix).to eql("/private/etc")
+ end
+
+ it "it sets the config prefix to /usr/local/etc on FreeBSD" do
+ node.automatic[:platform_family] = "freebsd"
+ expect(resource.config_prefix).to eql("/usr/local/etc")
+ end
+
+ it "it sets the config prefix to /opt/local/etc on smartos" do
+ node.automatic[:platform_family] = "smartos"
+ expect(resource.config_prefix).to eql("/opt/local/etc")
+ end
+end
diff --git a/spec/unit/resource/swap_file_spec.rb b/spec/unit/resource/swap_file_spec.rb
new file mode 100644
index 0000000000..905e12646a
--- /dev/null
+++ b/spec/unit/resource/swap_file_spec.rb
@@ -0,0 +1,40 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::SwapFile do
+ let(:resource) { Chef::Resource::SwapFile.new("swapfile") }
+
+ it "sets resource name as :swap_file" do
+ expect(resource.resource_name).to eql(:swap_file)
+ end
+
+ it "sets the path as its name" do
+ expect(resource.path).to eql("swapfile")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create and :remove actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :delete }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/sysctl_spec.rb b/spec/unit/resource/sysctl_spec.rb
new file mode 100644
index 0000000000..934db9b9e2
--- /dev/null
+++ b/spec/unit/resource/sysctl_spec.rb
@@ -0,0 +1,56 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::Sysctl do
+ let(:resource) { Chef::Resource::Sysctl.new("something.something") }
+
+ it "sets resource name as :sysctl" do
+ expect(resource.resource_name).to eql(:sysctl)
+ end
+
+ it "sets the default action as :apply" do
+ expect(resource.action).to eql([:apply])
+ end
+
+ it "sets the key property as its name property" do
+ expect(resource.key).to eql("something.something")
+ end
+
+ it "coerces Arrays in the value property to space delimited Strings" do
+ resource.value [1, 2, 3]
+ expect(resource.value).to eql("1 2 3")
+ end
+
+ it "coerces Integers in the value property to Strings" do
+ resource.value 1
+ expect(resource.value).to eql("1")
+ end
+
+ it "coerces Floats in the value property to Strings" do
+ resource.value 1.1
+ expect(resource.value).to eql("1.1")
+ end
+
+ it "supports :apply and :remove actions" do
+ expect { resource.action :apply }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :delete }.to raise_error(ArgumentError)
+ expect { resource.action :install }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/systemd_unit_spec.rb b/spec/unit/resource/systemd_unit_spec.rb
index 7e46872525..9d156402a1 100644
--- a/spec/unit/resource/systemd_unit_spec.rb
+++ b/spec/unit/resource/systemd_unit_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Nathan Williams (<nath.e.will@gmail.com>)
-# Copyright:: Copyright 2016, Nathan Williams
+# Copyright:: Copyright 2016-2018, Nathan Williams
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,12 +19,8 @@
require "spec_helper"
describe Chef::Resource::SystemdUnit do
- before(:each) do
- @resource = Chef::Resource::SystemdUnit.new("sysstat-collect.timer")
- end
-
+ let(:resource) { Chef::Resource::SystemdUnit.new("sysstat-collect.timer") }
let(:unit_content_string) { "[Unit]\nDescription = Run system activity accounting tool every 10 minutes\n\n[Timer]\nOnCalendar = *:00/10\n\n[Install]\nWantedBy = sysstat.service\n" }
-
let(:unit_content_hash) do
{
"Unit" => {
@@ -39,77 +35,75 @@ describe Chef::Resource::SystemdUnit do
}
end
- it "creates a new Chef::Resource::SystemdUnit" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::SystemdUnit)
- end
-
- it "should have a name" do
- expect(@resource.name).to eql("sysstat-collect.timer")
+ it "has a name property" do
+ expect(resource.name).to eql("sysstat-collect.timer")
end
it "has a default action of nothing" do
- expect(@resource.action).to eql([:nothing])
+ expect(resource.action).to eql([:nothing])
end
it "supports appropriate unit actions" do
- expect { @resource.action :create }.not_to raise_error
- expect { @resource.action :delete }.not_to raise_error
- expect { @resource.action :enable }.not_to raise_error
- expect { @resource.action :disable }.not_to raise_error
- expect { @resource.action :mask }.not_to raise_error
- expect { @resource.action :unmask }.not_to raise_error
- expect { @resource.action :start }.not_to raise_error
- expect { @resource.action :stop }.not_to raise_error
- expect { @resource.action :restart }.not_to raise_error
- expect { @resource.action :reload }.not_to raise_error
- expect { @resource.action :wrong }.to raise_error(ArgumentError)
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :preset }.not_to raise_error
+ expect { resource.action :revert }.not_to raise_error
+ expect { resource.action :enable }.not_to raise_error
+ expect { resource.action :disable }.not_to raise_error
+ expect { resource.action :reenable }.not_to raise_error
+ expect { resource.action :mask }.not_to raise_error
+ expect { resource.action :unmask }.not_to raise_error
+ expect { resource.action :start }.not_to raise_error
+ expect { resource.action :stop }.not_to raise_error
+ expect { resource.action :restart }.not_to raise_error
+ expect { resource.action :reload }.not_to raise_error
+ expect { resource.action :wrong }.to raise_error(ArgumentError)
end
it "accepts boolean state properties" do
- expect { @resource.active false }.not_to raise_error
- expect { @resource.active true }.not_to raise_error
- expect { @resource.active "yes" }.to raise_error(ArgumentError)
+ expect { resource.active false }.not_to raise_error
+ expect { resource.active true }.not_to raise_error
+ expect { resource.active "yes" }.to raise_error(ArgumentError)
- expect { @resource.enabled true }.not_to raise_error
- expect { @resource.enabled false }.not_to raise_error
- expect { @resource.enabled "no" }.to raise_error(ArgumentError)
+ expect { resource.enabled true }.not_to raise_error
+ expect { resource.enabled false }.not_to raise_error
+ expect { resource.enabled "no" }.to raise_error(ArgumentError)
- expect { @resource.masked true }.not_to raise_error
- expect { @resource.masked false }.not_to raise_error
- expect { @resource.masked :nope }.to raise_error(ArgumentError)
+ expect { resource.masked true }.not_to raise_error
+ expect { resource.masked false }.not_to raise_error
+ expect { resource.masked :nope }.to raise_error(ArgumentError)
- expect { @resource.static true }.not_to raise_error
- expect { @resource.static false }.not_to raise_error
- expect { @resource.static "yep" }.to raise_error(ArgumentError)
+ expect { resource.static true }.not_to raise_error
+ expect { resource.static false }.not_to raise_error
+ expect { resource.static "yep" }.to raise_error(ArgumentError)
end
it "accepts the content property" do
- expect { @resource.content nil }.not_to raise_error
- expect { @resource.content "test" }.not_to raise_error
- expect { @resource.content({}) }.not_to raise_error
- expect { @resource.content 5 }.to raise_error(ArgumentError)
+ expect { resource.content nil }.not_to raise_error
+ expect { resource.content "test" }.not_to raise_error
+ expect { resource.content({}) }.not_to raise_error
+ expect { resource.content 5 }.to raise_error(ArgumentError)
end
it "accepts the user property" do
- expect { @resource.user nil }.not_to raise_error
- expect { @resource.user "deploy" }.not_to raise_error
- expect { @resource.user 5 }.to raise_error(ArgumentError)
+ expect { resource.user nil }.not_to raise_error
+ expect { resource.user "deploy" }.not_to raise_error
+ expect { resource.user 5 }.to raise_error(ArgumentError)
end
it "accepts the triggers_reload property" do
- expect { @resource.triggers_reload true }.not_to raise_error
- expect { @resource.triggers_reload false }.not_to raise_error
- expect { @resource.triggers_reload "no" }.to raise_error(ArgumentError)
+ expect { resource.triggers_reload true }.not_to raise_error
+ expect { resource.triggers_reload false }.not_to raise_error
+ expect { resource.triggers_reload "no" }.to raise_error(ArgumentError)
end
it "reports its state" do
- @resource.active true
- @resource.enabled true
- @resource.masked false
- @resource.static false
- @resource.content "test"
- state = @resource.state
+ resource.active true
+ resource.enabled true
+ resource.masked false
+ resource.static false
+ resource.content "test"
+ state = resource.state_for_resource_reporter
expect(state[:active]).to eq(true)
expect(state[:enabled]).to eq(true)
expect(state[:masked]).to eq(false)
@@ -118,16 +112,16 @@ describe Chef::Resource::SystemdUnit do
end
it "returns the unit name as its identity" do
- expect(@resource.identity).to eq("sysstat-collect.timer")
+ expect(resource.identity).to eq("sysstat-collect.timer")
end
it "serializes to ini with a string-formatted content property" do
- @resource.content(unit_content_string)
- expect(@resource.to_ini).to eq unit_content_string
+ resource.content(unit_content_string)
+ expect(resource.to_ini).to eq unit_content_string
end
it "serializes to ini with a hash-formatted content property" do
- @resource.content(unit_content_hash)
- expect(@resource.to_ini).to eq unit_content_string
+ resource.content(unit_content_hash)
+ expect(resource.to_ini).to eq unit_content_string
end
end
diff --git a/spec/unit/resource/template_spec.rb b/spec/unit/resource/template_spec.rb
index 9060f02d29..14e48900d3 100644
--- a/spec/unit/resource/template_spec.rb
+++ b/spec/unit/resource/template_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,76 +19,71 @@
require "spec_helper"
describe Chef::Resource::Template do
-
- before(:each) do
- @resource = Chef::Resource::Template.new("fakey_fakerton")
- end
+ let(:resource) { Chef::Resource::Template.new("fakey_fakerton") }
describe "initialize" do
- it "should create a new Chef::Resource::Template" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::File)
- expect(@resource).to be_a_kind_of(Chef::Resource::Template)
+ it "is a subclass of Chef::Resource::File" do
+ expect(resource).to be_a_kind_of(Chef::Resource::File)
end
end
describe "source" do
- it "should accept a string for the template source" do
- @resource.source "something"
- expect(@resource.source).to eql("something")
+ it "accepts a string for the template source" do
+ resource.source "something"
+ expect(resource.source).to eql("something")
end
- it "should have a default based on the param name with .erb appended" do
- expect(@resource.source).to eql("fakey_fakerton.erb")
+ it "has a default based on the param name with .erb appended" do
+ expect(resource.source).to eql("fakey_fakerton.erb")
end
- it "should use only the basename of the file as the default" do
+ it "uses only the basename of the file as the default" do
r = Chef::Resource::Template.new("/tmp/obit/fakey_fakerton")
expect(r.source).to eql("fakey_fakerton.erb")
end
end
describe "variables" do
- it "should accept a hash for the variable list" do
- @resource.variables({ :reluctance => :awkward })
- expect(@resource.variables).to eq({ :reluctance => :awkward })
+ it "accepts a hash for the variable list" do
+ resource.variables({ :reluctance => :awkward })
+ expect(resource.variables).to eq({ :reluctance => :awkward })
end
end
describe "cookbook" do
- it "should accept a string for the cookbook name" do
- @resource.cookbook("foo")
- expect(@resource.cookbook).to eq("foo")
+ it "accepts a string for the cookbook name" do
+ resource.cookbook("foo")
+ expect(resource.cookbook).to eq("foo")
end
- it "should default to nil" do
- expect(@resource.cookbook).to eq(nil)
+ it "defaults to nil" do
+ expect(resource.cookbook).to eq(nil)
end
end
describe "local" do
- it "should accept a boolean for whether a template is local or remote" do
- @resource.local(true)
- expect(@resource.local).to eq(true)
+ it "accepts a boolean for whether a template is local or remote" do
+ resource.local(true)
+ expect(resource.local).to eq(true)
end
- it "should default to false" do
- expect(@resource.local).to eq(false)
+ it "defaults to false" do
+ expect(resource.local).to eq(false)
end
end
describe "when it has a path, owner, group, mode, and checksum" do
before do
- @resource.path("/tmp/foo.txt")
- @resource.owner("root")
- @resource.group("wheel")
- @resource.mode("0644")
- @resource.checksum("1" * 64)
+ resource.path("/tmp/foo.txt")
+ resource.owner("root")
+ resource.group("wheel")
+ resource.mode("0644")
+ resource.checksum("1" * 64)
end
context "on unix", :unix_only do
it "describes its state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:owner]).to eq("root")
expect(state[:group]).to eq("wheel")
expect(state[:mode]).to eq("0644")
@@ -102,7 +97,7 @@ describe Chef::Resource::Template do
end
it "returns the file path as its identity" do
- expect(@resource.identity).to eq("/tmp/foo.txt")
+ expect(resource.identity).to eq("/tmp/foo.txt")
end
end
@@ -115,16 +110,16 @@ describe Chef::Resource::Template do
end
it "collects helper method bodies as blocks" do
- @resource.helper(:example_1) { "example_1" }
- @resource.helper(:example_2) { "example_2" }
- expect(@resource.inline_helper_blocks[:example_1].call).to eq("example_1")
- expect(@resource.inline_helper_blocks[:example_2].call).to eq("example_2")
+ resource.helper(:example_1) { "example_1" }
+ resource.helper(:example_2) { "example_2" }
+ expect(resource.inline_helper_blocks[:example_1].call).to eq("example_1")
+ expect(resource.inline_helper_blocks[:example_2].call).to eq("example_2")
end
it "compiles helper methods into a module" do
- @resource.helper(:example_1) { "example_1" }
- @resource.helper(:example_2) { "example_2" }
- modules = @resource.helper_modules
+ resource.helper(:example_1) { "example_1" }
+ resource.helper(:example_2) { "example_2" }
+ modules = resource.helper_modules
expect(modules.size).to eq(1)
o = Object.new
modules.each { |m| o.extend(m) }
@@ -133,38 +128,38 @@ describe Chef::Resource::Template do
end
it "compiles helper methods with arguments into a module" do
- @resource.helper(:shout) { |quiet| quiet.upcase }
- modules = @resource.helper_modules
+ resource.helper(:shout) { |quiet| quiet.upcase }
+ modules = resource.helper_modules
o = Object.new
modules.each { |m| o.extend(m) }
expect(o.shout("shout")).to eq("SHOUT")
end
it "raises an error when attempting to define a helper method without a method body" do
- expect { @resource.helper(:example) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { resource.helper(:example) }.to raise_error(Chef::Exceptions::ValidationFailed)
end
it "raises an error when attempting to define a helper method with a non-Symbod method name" do
- expect { @resource.helper("example") { "fail" } }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { resource.helper("example") { "fail" } }.to raise_error(Chef::Exceptions::ValidationFailed)
end
it "collects helper module bodies as blocks" do
- @resource.helpers do
+ resource.helpers do
def example_1
"example_1"
end
end
- module_body = @resource.inline_helper_modules.first
+ module_body = resource.inline_helper_modules.first
expect(module_body).to be_a(Proc)
end
it "compiles helper module bodies into modules" do
- @resource.helpers do
+ resource.helpers do
def example_1
"example_1"
end
end
- modules = @resource.helper_modules
+ modules = resource.helper_modules
expect(modules.size).to eq(1)
o = Object.new
modules.each { |m| o.extend(m) }
@@ -172,39 +167,37 @@ describe Chef::Resource::Template do
end
it "raises an error when no block or module name is given for helpers definition" do
- expect { @resource.helpers() }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { resource.helpers() }.to raise_error(Chef::Exceptions::ValidationFailed)
end
it "raises an error when a non-module is given for helpers definition" do
- expect { @resource.helpers("NotAModule") }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { resource.helpers("NotAModule") }.to raise_error(Chef::Exceptions::ValidationFailed)
end
it "raises an error when a module name and block are both given for helpers definition" do
- expect { @resource.helpers(ExampleHelpers) { module_code } }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { resource.helpers(ExampleHelpers) { module_code } }.to raise_error(Chef::Exceptions::ValidationFailed)
end
it "collects helper modules" do
- @resource.helpers(ExampleHelpers)
- expect(@resource.helper_modules).to include(ExampleHelpers)
+ resource.helpers(ExampleHelpers)
+ expect(resource.helper_modules).to include(ExampleHelpers)
end
it "combines all helpers into a set of compiled modules" do
- @resource.helpers(ExampleHelpers)
- @resource.helpers do
+ resource.helpers(ExampleHelpers)
+ resource.helpers do
def inline_module
"inline_module"
end
end
- @resource.helper(:inline_method) { "inline_method" }
- expect(@resource.helper_modules.size).to eq(3)
+ resource.helper(:inline_method) { "inline_method" }
+ expect(resource.helper_modules.size).to eq(3)
o = Object.new
- @resource.helper_modules.each { |m| o.extend(m) }
+ resource.helper_modules.each { |m| o.extend(m) }
expect(o.static_example).to eq("static_example")
expect(o.inline_module).to eq("inline_module")
expect(o.inline_method).to eq("inline_method")
end
-
end
-
end
diff --git a/spec/unit/resource/user_spec.rb b/spec/unit/resource/user_spec.rb
index 9648ee1305..1c583afc44 100644
--- a/spec/unit/resource/user_spec.rb
+++ b/spec/unit/resource/user_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,115 +19,104 @@
require "spec_helper"
describe Chef::Resource::User, "initialize" do
- before(:each) do
- @resource = Chef::Resource::User.new("adam")
- end
-
- it "should create a new Chef::Resource::User" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::User)
- end
+ let(:resource) { Chef::Resource::User.new("adam") }
- it "should set the resource_name to :user" do
- expect(@resource.resource_name).to eql(:user)
+ it "sets the resource_name to :user" do
+ expect(resource.resource_name).to eql(:user_resource_abstract_base_class)
end
- it "should set the username equal to the argument to initialize" do
- expect(@resource.username).to eql("adam")
+ it "sets the username equal to the argument to initialize" do
+ expect(resource.username).to eql("adam")
end
%w{comment uid gid home shell password}.each do |attrib|
- it "should set #{attrib} to nil" do
- expect(@resource.send(attrib)).to eql(nil)
+ it "sets #{attrib} to nil" do
+ expect(resource.send(attrib)).to eql(nil)
end
end
- it "should set action to :create" do
- expect(@resource.action).to eql([:create])
+ it "sets action to :create" do
+ expect(resource.action).to eql([:create])
end
- it "should set supports[:manage_home] to false" do
- expect(@resource.supports[:manage_home]).to eql(false)
+ it "sets manage_home to false" do
+ expect(resource.manage_home).to eql(false)
end
- it "should set supports[:non_unique] to false" do
- expect(@resource.supports[:non_unique]).to eql(false)
+ it "sets non_unique to false" do
+ expect(resource.non_unique).to eql(false)
end
- it "should set force to false" do
- expect(@resource.force).to eql(false)
+ it "sets force to false" do
+ expect(resource.force).to eql(false)
end
%w{create remove modify manage lock unlock}.each do |action|
- it "should allow action #{action}" do
- expect(@resource.allowed_actions.detect { |a| a == action.to_sym }).to eql(action.to_sym)
+ it "allows action #{action}" do
+ expect(resource.allowed_actions.detect { |a| a == action.to_sym }).to eql(action.to_sym)
end
end
- it "should accept domain users (@ or \ separator) on non-windows" do
- expect { @resource.username "domain\@user" }.not_to raise_error
- expect(@resource.username).to eq("domain\@user")
- expect { @resource.username "domain\\user" }.not_to raise_error
- expect(@resource.username).to eq("domain\\user")
+ it "accepts domain users (@ or \ separator) on non-windows" do
+ expect { resource.username "domain\@user" }.not_to raise_error
+ expect(resource.username).to eq("domain\@user")
+ expect { resource.username "domain\\user" }.not_to raise_error
+ expect(resource.username).to eq("domain\\user")
end
end
%w{username comment home shell password}.each do |attrib|
describe Chef::Resource::User, attrib do
- before(:each) do
- @resource = Chef::Resource::User.new("adam")
- end
+ let(:resource) { Chef::Resource::User.new("adam") }
- it "should allow a string" do
- @resource.send(attrib, "adam")
- expect(@resource.send(attrib)).to eql("adam")
+ it "allows a string" do
+ resource.send(attrib, "adam")
+ expect(resource.send(attrib)).to eql("adam")
end
- it "should not allow a hash" do
- expect { @resource.send(attrib, { :woot => "i found it" }) }.to raise_error(ArgumentError)
+ it "does not allow a hash" do
+ expect { resource.send(attrib, { :woot => "i found it" }) }.to raise_error(ArgumentError)
end
end
end
%w{uid gid}.each do |attrib|
describe Chef::Resource::User, attrib do
- before(:each) do
- @resource = Chef::Resource::User.new("adam")
- end
+ let(:resource) { Chef::Resource::User.new("adam") }
- it "should allow a string" do
- @resource.send(attrib, "100")
- expect(@resource.send(attrib)).to eql("100")
+ it "allows a string" do
+ resource.send(attrib, "100")
+ expect(resource.send(attrib)).to eql("100")
end
- it "should allow an integer" do
- @resource.send(attrib, 100)
- expect(@resource.send(attrib)).to eql(100)
+ it "allows an integer" do
+ resource.send(attrib, 100)
+ expect(resource.send(attrib)).to eql(100)
end
- it "should not allow a hash" do
- expect { @resource.send(attrib, { :woot => "i found it" }) }.to raise_error(ArgumentError)
+ it "does not allow a hash" do
+ expect { resource.send(attrib, { :woot => "i found it" }) }.to raise_error(ArgumentError)
end
end
describe "when it has uid, gid, and home" do
+ let(:resource) { Chef::Resource::User.new("root") }
+
before do
- @resource = Chef::Resource::User.new("root")
- @resource.uid(123)
- @resource.gid(456)
- @resource.home("/usr/local/root/")
+ resource.uid(123)
+ resource.gid(456)
+ resource.home("/usr/local/root/")
end
it "describes its state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:uid]).to eq(123)
expect(state[:gid]).to eq(456)
expect(state[:home]).to eq("/usr/local/root/")
end
it "returns the username as its identity" do
- expect(@resource.identity).to eq("root")
+ expect(resource.identity).to eq("root")
end
end
-
end
diff --git a/spec/unit/resource/windows_ad_join.rb b/spec/unit/resource/windows_ad_join.rb
new file mode 100644
index 0000000000..7fd02c2021
--- /dev/null
+++ b/spec/unit/resource/windows_ad_join.rb
@@ -0,0 +1,45 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsAdJoin do
+ let(:resource) { Chef::Resource::WindowsAdJoin.new("example.com") }
+
+ it "sets resource name as :windows_ad_join" do
+ expect(resource.resource_name).to eql(:windows_ad_join)
+ end
+
+ it "sets the domain_name as its name" do
+ expect(resource.domain_name).to eql("example.com")
+ end
+
+ it "only accepts FQDNs for the domain_name property" do
+ expect { resource.domain_name "example" }.to raise_error(ArgumentError)
+ end
+
+ it "sets the default action as :join" do
+ expect(resource.action).to eql([:join])
+ end
+
+ it "accepts :immediate, :delayed, or :never values for 'reboot' property" do
+ expect { resource.reboot :immediate }.not_to raise_error
+ expect { resource.reboot :delayed }.not_to raise_error
+ expect { resource.reboot :never }.not_to raise_error
+ expect { resource.reboot :nopenope }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/windows_auto_run_spec.rb b/spec/unit/resource/windows_auto_run_spec.rb
new file mode 100644
index 0000000000..c92bbb2f98
--- /dev/null
+++ b/spec/unit/resource/windows_auto_run_spec.rb
@@ -0,0 +1,50 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsAutorun do
+ let(:resource) { Chef::Resource::WindowsAutorun.new("some_path") }
+
+ it "sets resource name as :windows_auto_run" do
+ expect(resource.resource_name).to eql(:windows_auto_run)
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "sets the program_name property as its name" do
+ expect(resource.program_name).to eql("some_path")
+ end
+ it "supports :machine and :user in the root property" do
+ expect { resource.root :user }.not_to raise_error
+ expect { resource.root :machine }.not_to raise_error
+ expect { resource.root "user" }.to raise_error(ArgumentError)
+ end
+
+ it "supports :create and :remove actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :delete }.to raise_error(ArgumentError)
+ end
+
+ it "coerces forward slashes to backslashes for the path" do
+ resource.path "C:/something.exe"
+ expect(resource.path).to eql('C:\\something.exe')
+ end
+end
diff --git a/spec/unit/resource/env_spec.rb b/spec/unit/resource/windows_env_spec.rb
index cff862b69e..776e4f7cd5 100644
--- a/spec/unit/resource/env_spec.rb
+++ b/spec/unit/resource/windows_env_spec.rb
@@ -19,66 +19,64 @@
require "spec_helper"
-describe Chef::Resource::Env do
+describe Chef::Resource::WindowsEnv do
- before(:each) do
- @resource = Chef::Resource::Env.new("FOO")
- end
+ let(:resource) { Chef::Resource::WindowsEnv.new("FOO") }
- it "should create a new Chef::Resource::Env" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Env)
+ it "creates a new Chef::Resource::WindowsEnv" do
+ expect(resource).to be_a_kind_of(Chef::Resource)
+ expect(resource).to be_a_kind_of(Chef::Resource::WindowsEnv)
end
- it "should have a name" do
- expect(@resource.name).to eql("FOO")
+ it "has a name" do
+ expect(resource.name).to eql("FOO")
end
- it "should have a default action of 'create'" do
- expect(@resource.action).to eql([:create])
+ it "has a default action of 'create'" do
+ expect(resource.action).to eql([:create])
end
{ :create => false, :delete => false, :modify => false, :flibber => true }.each do |action, bad_value|
it "should #{bad_value ? 'not' : ''} accept #{action}" do
if bad_value
- expect { @resource.action action }.to raise_error(ArgumentError)
+ expect { resource.action action }.to raise_error(ArgumentError)
else
- expect { @resource.action action }.not_to raise_error
+ expect { resource.action action }.not_to raise_error
end
end
end
- it "should use the object name as the key_name by default" do
- expect(@resource.key_name).to eql("FOO")
+ it "uses the object name as the key_name by default" do
+ expect(resource.key_name).to eql("FOO")
end
- it "should accept a string as the env value via 'value'" do
- expect { @resource.value "bar" }.not_to raise_error
+ it "accepts a string as the env value via 'value'" do
+ expect { resource.value "bar" }.not_to raise_error
end
- it "should not accept a Hash for the env value via 'to'" do
- expect { @resource.value Hash.new }.to raise_error(ArgumentError)
+ it "does not accept a Hash for the env value via 'to'" do
+ expect { resource.value Hash.new }.to raise_error(ArgumentError)
end
- it "should allow you to set an env value via 'to'" do
- @resource.value "bar"
- expect(@resource.value).to eql("bar")
+ it "allows you to set an env value via 'to'" do
+ resource.value "bar"
+ expect(resource.value).to eql("bar")
end
describe "when it has key name and value" do
before do
- @resource.key_name("charmander")
- @resource.value("level7")
- @resource.delim("hi")
+ resource.key_name("charmander")
+ resource.value("level7")
+ resource.delim("hi")
end
it "describes its state" do
- state = @resource.state
+ state = resource.state_for_resource_reporter
expect(state[:value]).to eq("level7")
end
it "returns the key name as its identity" do
- expect(@resource.identity).to eq("charmander")
+ expect(resource.identity).to eq("charmander")
end
end
diff --git a/spec/unit/resource/windows_feature.rb b/spec/unit/resource/windows_feature.rb
new file mode 100644
index 0000000000..c8b8587ed8
--- /dev/null
+++ b/spec/unit/resource/windows_feature.rb
@@ -0,0 +1,41 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsFeature do
+ let(:resource) { Chef::Resource::WindowsFeature.new("SNMP") }
+
+ it "sets resource name as :windows_feature" do
+ expect(resource.resource_name).to eql(:windows_feature)
+ end
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "sets the feature_name property as its name" do
+ expect(resource.feature_name).to eql("SNMP")
+ end
+
+ it "supports :install, :remove, and :delete actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :update }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/windows_feature_dism.rb b/spec/unit/resource/windows_feature_dism.rb
new file mode 100644
index 0000000000..4f973f7e82
--- /dev/null
+++ b/spec/unit/resource/windows_feature_dism.rb
@@ -0,0 +1,51 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsFeatureDism do
+ let(:resource) { Chef::Resource::WindowsFeatureDism.new(%w{SNMP DHCP}) }
+
+ it "sets resource name as :windows_feature_dism" do
+ expect(resource.resource_name).to eql(:windows_feature_dism)
+ end
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "sets the feature_name property as its name property" do
+ expect(resource.feature_name).to eql(%w{snmp dhcp})
+ end
+
+ it "coerces comma separated lists of features to a lowercase array" do
+ resource.feature_name "SNMP, DHCP"
+ expect(resource.feature_name).to eql(%w{snmp dhcp})
+ end
+
+ it "coerces a single feature as a String to a lowercase array" do
+ resource.feature_name "SNMP"
+ expect(resource.feature_name).to eql(["snmp"])
+ end
+
+ it "supports :install, :remove, and :delete actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :update }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/windows_feature_powershell.rb b/spec/unit/resource/windows_feature_powershell.rb
new file mode 100644
index 0000000000..5ea226575c
--- /dev/null
+++ b/spec/unit/resource/windows_feature_powershell.rb
@@ -0,0 +1,51 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsFeaturePowershell do
+ let(:resource) { Chef::Resource::WindowsFeaturePowershell.new(%w{SNMP DHCP}) }
+
+ it "sets resource name as :windows_feature_powershell" do
+ expect(resource.resource_name).to eql(:windows_feature_powershell)
+ end
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "sets the feature_name property as its name property" do
+ expect(resource.feature_name).to eql(%w{SNMP DHCP})
+ end
+
+ it "coerces comma separated lists of features to arrays" do
+ resource.feature_name "SNMP, DHCP"
+ expect(resource.feature_name).to eql(%w{SNMP DHCP})
+ end
+
+ it "coerces a single feature as a String into an array" do
+ resource.feature_name "SNMP"
+ expect(resource.feature_name).to eql(["SNMP"])
+ end
+
+ it "supports :install, :remove, and :delete actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :update }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/windows_font_spec.rb b/spec/unit/resource/windows_font_spec.rb
new file mode 100644
index 0000000000..a98e3f4a40
--- /dev/null
+++ b/spec/unit/resource/windows_font_spec.rb
@@ -0,0 +1,44 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsFont do
+ let(:resource) { Chef::Resource::WindowsFont.new("some_font") }
+
+ it "sets resource name as :windows_font" do
+ expect(resource.resource_name).to eql(:windows_font)
+ end
+
+ it "sets the font_name as its name" do
+ expect(resource.font_name).to eql("some_font")
+ end
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install action" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :remove }.to raise_error(ArgumentError)
+ end
+
+ it "coerces backslashes in the source property to forward slashes" do
+ resource.source 'C:\foo\bar\fontfile'
+ expect(resource.source).to eql("C:/foo/bar/fontfile")
+ end
+end
diff --git a/spec/unit/resource/windows_package_spec.rb b/spec/unit/resource/windows_package_spec.rb
index 5aa3707199..bb4052f8e3 100644
--- a/spec/unit/resource/windows_package_spec.rb
+++ b/spec/unit/resource/windows_package_spec.rb
@@ -33,8 +33,8 @@ describe Chef::Resource::WindowsPackage, "initialize" do
let(:resource) { Chef::Resource::WindowsPackage.new("solitaire.msi") }
- it "returns a Chef::Resource::WindowsPackage" do
- expect(resource).to be_a_kind_of(Chef::Resource::WindowsPackage)
+ it "is a subclass of Chef::Resource::Package" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Package)
end
it "sets the resource_name to :windows_package" do
@@ -88,7 +88,7 @@ describe Chef::Resource::WindowsPackage, "initialize" do
let(:resource_source) { "https://foo.bar/solitare.msi" }
let(:resource) { Chef::Resource::WindowsPackage.new(resource_source) }
- it "should return the source unmodified" do
+ it "returns the source unmodified" do
expect(resource.source).to eq(resource_source)
end
end
diff --git a/spec/unit/resource/windows_pagefile_spec.rb b/spec/unit/resource/windows_pagefile_spec.rb
new file mode 100644
index 0000000000..dd6353eccb
--- /dev/null
+++ b/spec/unit/resource/windows_pagefile_spec.rb
@@ -0,0 +1,45 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsPagefile do
+ let(:resource) { Chef::Resource::WindowsPagefile.new("C:\\pagefile.sys") }
+
+ it "sets resource name as :windows_pagefile" do
+ expect(resource.resource_name).to eql(:windows_pagefile)
+ end
+
+ it "sets the path as its name" do
+ expect(resource.path).to eql("C:\\pagefile.sys")
+ end
+
+ it "coerces forward slashes in the path property to back slashes" do
+ resource.path "C:/pagefile.sys"
+ expect(resource.path).to eql("C:\\pagefile.sys")
+ end
+
+ it "sets the default action as :set" do
+ expect(resource.action).to eql([:set])
+ end
+
+ it "supports :set and :delete actions" do
+ expect { resource.action :set }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :create }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/windows_path_spec.rb b/spec/unit/resource/windows_path_spec.rb
new file mode 100644
index 0000000000..8f29a11e4e
--- /dev/null
+++ b/spec/unit/resource/windows_path_spec.rb
@@ -0,0 +1,41 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2008-2017, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsPath do
+ let(:resource) { Chef::Resource::WindowsPath.new("some_path") }
+
+ it "sets resource name as :windows_path" do
+ expect(resource.resource_name).to eql(:windows_path)
+ end
+
+ it "sets the path as its name" do
+ expect(resource.path).to eql("some_path")
+ end
+
+ it "sets the default action as :add" do
+ expect(resource.action).to eql([:add])
+ end
+
+ it "supports :add and :remove actions" do
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :delete }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/windows_printer_port_spec.rb b/spec/unit/resource/windows_printer_port_spec.rb
new file mode 100644
index 0000000000..6b29c2610a
--- /dev/null
+++ b/spec/unit/resource/windows_printer_port_spec.rb
@@ -0,0 +1,45 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsPrinterPort do
+ let(:resource) { Chef::Resource::WindowsPrinterPort.new("63.192.209.236") }
+
+ it "sets resource name as :windows_printer_port" do
+ expect(resource.resource_name).to eql(:windows_printer_port)
+ end
+
+ it "sets the ipv4_address as its name" do
+ expect(resource.ipv4_address).to eql("63.192.209.236")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create and :delete actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :remove }.to raise_error(ArgumentError)
+ end
+
+ it "raises an error if ipv4_address isn't in X.X.X.X format" do
+ expect { resource.ipv4_address "63.192.209.236" }.not_to raise_error
+ expect { resource.ipv4_address "a.b.c.d" }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/windows_printer_spec.rb b/spec/unit/resource/windows_printer_spec.rb
new file mode 100644
index 0000000000..5c773005ea
--- /dev/null
+++ b/spec/unit/resource/windows_printer_spec.rb
@@ -0,0 +1,45 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsPrinter do
+ let(:resource) { Chef::Resource::WindowsPrinter.new("some_printer") }
+
+ it "sets resource name as :windows_printer" do
+ expect(resource.resource_name).to eql(:windows_printer)
+ end
+
+ it "sets the device_id as its name" do
+ expect(resource.device_id).to eql("some_printer")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create and :delete actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :remove }.to raise_error(ArgumentError)
+ end
+
+ it "raises an error if ipv4_address isn't in X.X.X.X format" do
+ expect { resource.ipv4_address "63.192.209.236" }.not_to raise_error
+ expect { resource.ipv4_address "a.b.c.d" }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/windows_service_spec.rb b/spec/unit/resource/windows_service_spec.rb
index 2455a70f02..f5de5b6965 100644
--- a/spec/unit/resource/windows_service_spec.rb
+++ b/spec/unit/resource/windows_service_spec.rb
@@ -29,10 +29,6 @@ describe Chef::Resource::WindowsService, "initialize" do
let(:resource) { Chef::Resource::WindowsService.new("BITS") }
- it "returns a Chef::Resource::WindowsService" do
- expect(resource).to be_a_kind_of(Chef::Resource::WindowsService)
- end
-
it "sets the resource_name to :windows_service" do
expect(resource.resource_name).to eql(:windows_service)
end
@@ -46,4 +42,29 @@ describe Chef::Resource::WindowsService, "initialize" do
resource.action :configure_startup
expect(resource.action).to eq([:configure_startup])
end
+
+ # Attributes that are Strings
+ %i{description service_name binary_path_name load_order_group dependencies
+ run_as_user run_as_password display_name}.each do |prop|
+ it "support setting #{prop}" do
+ resource.send("#{prop}=", "some value")
+ expect(resource.send(prop)).to eq("some value")
+ end
+ end
+
+ # Attributes that are Integers
+ %i{desired_access error_control service_type}.each do |prop|
+ it "support setting #{prop}" do
+ resource.send("#{prop}=", 1)
+ expect(resource.send(prop)).to eq(1)
+ end
+ end
+
+ # Attributes that are Booleans
+ %i{delayed_start}.each do |prop|
+ it "support setting #{prop}" do
+ resource.send("#{prop}=", true)
+ expect(resource.send(prop)).to eq(true)
+ end
+ end
end
diff --git a/spec/unit/resource/windows_shortcut_spec.rb b/spec/unit/resource/windows_shortcut_spec.rb
new file mode 100644
index 0000000000..0b5e2325b9
--- /dev/null
+++ b/spec/unit/resource/windows_shortcut_spec.rb
@@ -0,0 +1,39 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsShortcut do
+ let(:resource) { Chef::Resource::WindowsShortcut.new("some_path") }
+
+ it "sets resource name as :windows_shortcut" do
+ expect(resource.resource_name).to eql(:windows_shortcut)
+ end
+
+ it "sets the shortcut_name property as its name" do
+ expect(resource.shortcut_name).to eql("some_path")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create action only" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/windows_task_spec.rb b/spec/unit/resource/windows_task_spec.rb
new file mode 100644
index 0000000000..6dbda35274
--- /dev/null
+++ b/spec/unit/resource/windows_task_spec.rb
@@ -0,0 +1,336 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2008-2017, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsTask, :windows_only do
+ let(:resource) { Chef::Resource::WindowsTask.new("sample_task") }
+
+ it "sets resource name as :windows_task" do
+ expect(resource.resource_name).to eql(:windows_task)
+ end
+
+ it "sets the task_name as its name" do
+ expect(resource.task_name).to eql("sample_task")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "sets the default user as System" do
+ expect(resource.user).to eql("SYSTEM")
+ end
+
+ it "sets the default run_level as :limited" do
+ expect(resource.run_level).to eql(:limited)
+ end
+
+ it "sets the default force as false" do
+ expect(resource.force).to eql(false)
+ end
+
+ it "sets the default interactive_enabled as false" do
+ expect(resource.interactive_enabled).to eql(false)
+ end
+
+ it "sets the default frequency_modifier as 1" do
+ expect(resource.frequency_modifier).to eql(1)
+ end
+
+ context "when frequency is not provided" do
+ it "raises ArgumentError to provide frequency" do
+ expect { resource.after_created }.to raise_error(ArgumentError, "Frequency needs to be provided. Valid frequencies are :minute, :hourly, :daily, :weekly, :monthly, :once, :on_logon, :onstart, :on_idle, :none." )
+ end
+ end
+
+ context "when user is set but password is not" do
+ before do
+ resource.frequency :hourly
+ end
+ it "raises an error if the user is a non-system user" do
+ resource.user "bob"
+ expect { resource.after_created }.to raise_error(ArgumentError, %q{Cannot specify a user other than the system users without specifying a password!. Valid passwordless users: 'NT AUTHORITY\SYSTEM', 'SYSTEM', 'NT AUTHORITY\LOCALSERVICE', 'NT AUTHORITY\NETWORKSERVICE', 'BUILTIN\USERS', 'USERS'})
+ end
+
+ it "does not raise an error if the user is a system user" do
+ resource.user 'NT AUTHORITY\SYSTEM'
+ expect { resource.after_created }.to_not raise_error
+ end
+
+ it "does not raise an error if the user is a system user even if lowercase" do
+ resource.user 'nt authority\system'
+ expect { resource.after_created }.to_not raise_error
+ end
+ end
+
+ context "when random_delay is passed" do
+ # changed this sepc since random_delay property is valid with it frequency :once
+ it "not raises error if frequency is `:once`" do
+ resource.frequency :once
+ resource.random_delay "20"
+ resource.start_time "15:00"
+ expect { resource.after_created }.to_not raise_error(ArgumentError, "`random_delay` property is supported only for frequency :once, :minute, :hourly, :daily, :weekly and :monthly")
+ end
+
+ it "raises error for invalid random_delay" do
+ resource.frequency :monthly
+ resource.random_delay "xyz"
+ expect { resource.after_created }.to raise_error(ArgumentError, "Invalid value passed for `random_delay`. Please pass seconds as an Integer (e.g. 60) or a String with numeric values only (e.g. '60').")
+ end
+
+ it "raises error for invalid random_delay which looks like an Integer" do
+ resource.frequency :monthly
+ resource.random_delay "5,000"
+ expect { resource.after_created }.to raise_error(ArgumentError, "Invalid value passed for `random_delay`. Please pass seconds as an Integer (e.g. 60) or a String with numeric values only (e.g. '60').")
+ end
+
+ it "converts '60' seconds into integer 1 minute format" do
+ resource.frequency :monthly
+ resource.random_delay "60"
+ resource.after_created
+ expect(resource.random_delay).to eq(1)
+ end
+
+ it "converts 60 Integer into integer 1 minute format" do
+ resource.frequency :monthly
+ resource.random_delay 60
+ resource.after_created
+ expect(resource.random_delay).to eq(1)
+ end
+
+ it "raises error that random_delay is not supported" do
+ expect { resource.send(:validate_random_delay, 60, :on_idle) }.to raise_error(ArgumentError, "`random_delay` property is supported only for frequency :once, :minute, :hourly, :daily, :weekly and :monthly")
+ end
+ end
+
+ context "when execution_time_limit isn't specified" do
+ it "sets the default value to PT72H which get converted to minute as 4320" do
+ resource.frequency :hourly
+ resource.after_created
+ expect(resource.execution_time_limit).to eq(4320)
+ end
+ end
+
+ context "when execution_time_limit is passed" do
+ it "raises error for invalid execution_time_limit" do
+ resource.execution_time_limit "abc"
+ expect { resource.after_created }.to raise_error(ArgumentError, "Invalid value passed for `execution_time_limit`. Please pass seconds as an Integer (e.g. 60) or a String with numeric values only (e.g. '60').")
+ end
+
+ it "raises error for invalid execution_time_limit that looks like an Integer" do
+ resource.execution_time_limit "5,000"
+ expect { resource.after_created }.to raise_error(ArgumentError, "Invalid value passed for `execution_time_limit`. Please pass seconds as an Integer (e.g. 60) or a String with numeric values only (e.g. '60').")
+ end
+
+ it "converts seconds Integer into integer minute format" do
+ resource.frequency :hourly
+ resource.execution_time_limit 60
+ resource.after_created
+ expect(resource.execution_time_limit).to eq(1)
+ end
+
+ it "converts seconds String into integer minute format" do
+ resource.frequency :hourly
+ resource.execution_time_limit "60"
+ resource.after_created
+ expect(resource.execution_time_limit).to eq(1)
+ end
+ end
+
+ context "#validate_start_time" do
+ it "raises error if start_time is nil when frequency `:once`" do
+ resource.frequency :once
+ expect { resource.send(:validate_start_time, nil, :once) }.to raise_error(ArgumentError, "`start_time` needs to be provided with `frequency :once`")
+ end
+
+ it "raises error if start_time is given when frequency `:none`" do
+ resource.frequency :none
+ expect { resource.send(:validate_start_time, "12.00", :none) }.to raise_error(ArgumentError, "`start_time` property is not supported with `frequency :none`")
+ end
+
+ it "raises error if start_time is not HH:mm format" do
+ resource.frequency :once
+ expect { resource.send(:validate_start_time, "2:30", :once) }.to raise_error(ArgumentError, "`start_time` property must be in the HH:mm format (e.g. 6:20pm -> 18:20).")
+ end
+
+ it "does not raise error if start_time is in HH:mm format" do
+ resource.frequency :once
+ expect { resource.send(:validate_start_time, "12:30", :once) }.not_to raise_error
+ end
+ end
+
+ context "#validate_start_day" do
+ it "not to raise error if start_day is passed with invalid frequency (:onstart)" do
+ expect { resource.send(:validate_start_day, "02/07/1984", :onstart) }.not_to raise_error
+ end
+
+ it "not to raise error if start_day is passed with invalid frequency (:on_idle)" do
+ expect { resource.send(:validate_start_day, "02/07/1984", :on_idle) }.not_to raise_error
+ end
+
+ it "not to raise error if start_day is passed with invalid frequency (:on_logon)" do
+ expect { resource.send(:validate_start_day, "02/07/1984", :on_logon) }.not_to raise_error
+ end
+
+ it "not raise error if start_day is passed with valid frequency (:weekly)" do
+ expect { resource.send(:validate_start_day, "02/07/1984", :weekly) }.not_to raise_error
+ end
+
+ it "not to raise error if start_day is passed with invalid date format (DD/MM/YYYY)" do
+ expect { resource.send(:validate_start_day, "28/12/2009", :weekly) }.to raise_error(ArgumentError, "`start_day` property must be in the MM/DD/YYYY format.")
+ end
+
+ it "raise error if start_day is passed with invalid date format (M/DD/YYYY)" do
+ expect { resource.send(:validate_start_day, "2/07/1984", :weekly) }.to raise_error(ArgumentError, "`start_day` property must be in the MM/DD/YYYY format.")
+ end
+
+ it "raise error if start_day is passed with invalid date format (MM/D/YYYY)" do
+ expect { resource.send(:validate_start_day, "02/7/1984", :weekly) }.to raise_error(ArgumentError, "`start_day` property must be in the MM/DD/YYYY format.")
+ end
+
+ it "raise error if start_day is passed with invalid date format (MM/DD/YY)" do
+ expect { resource.send(:validate_start_day, "02/07/84", :weekly) }.to raise_error(ArgumentError, "`start_day` property must be in the MM/DD/YYYY format.")
+ end
+ end
+
+ context "#validate_interactive_setting" do
+ it "raises error when interactive_enabled is passed without password" do
+ expect { resource.send(:validate_interactive_setting, true, nil) }.to raise_error("Please provide the password when attempting to set interactive/non-interactive.")
+ end
+ end
+
+ context "#validate_create_frequency_modifier" do
+ context "when frequency is :minute" do
+ it "raises error if frequency_modifier > 1439" do
+ expect { resource.send(:validate_create_frequency_modifier, :minute, 1500) }.to raise_error("frequency_modifier value 1500 is invalid. Valid values for :minute frequency are 1 - 1439.")
+ end
+ end
+
+ context "when frequency is :hourly" do
+ it "raises error if frequency_modifier > 23" do
+ expect { resource.send(:validate_create_frequency_modifier, :hourly, 24) }.to raise_error("frequency_modifier value 24 is invalid. Valid values for :hourly frequency are 1 - 23.")
+ end
+ end
+
+ context "when frequency is :daily" do
+ it "raises error if frequency_modifier > 365" do
+ expect { resource.send(:validate_create_frequency_modifier, :daily, 366) }.to raise_error("frequency_modifier value 366 is invalid. Valid values for :daily frequency are 1 - 365.")
+ end
+ end
+
+ context "when frequency is :weekly" do
+ it "raises error if frequency_modifier > 52" do
+ expect { resource.send(:validate_create_frequency_modifier, :weekly, 53) }.to raise_error("frequency_modifier value 53 is invalid. Valid values for :weekly frequency are 1 - 52.")
+ end
+ end
+
+ context "when frequency is :monthly" do
+ it "raises error if frequency_modifier > 12" do
+ expect { resource.send(:validate_create_frequency_modifier, :monthly, 14) }.to raise_error("frequency_modifier value 14 is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST'.")
+ end
+
+ it "raises error if frequency_modifier is invalid" do
+ expect { resource.send(:validate_create_frequency_modifier, :monthly, "abc") }.to raise_error("frequency_modifier value abc is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST'.")
+ end
+ end
+ end
+
+ context "#validate_create_day" do
+ it "raises error if frequency is not :weekly or :monthly" do
+ expect { resource.send(:validate_create_day, "Mon", :once, 1) }.to raise_error("day property is only valid for tasks that run monthly or weekly")
+ end
+
+ it "accepts a valid single day" do
+ expect { resource.send(:validate_create_day, "Mon", :weekly, 1) }.not_to raise_error
+ end
+
+ it "accepts a comma separated list of valid days" do
+ expect { resource.send(:validate_create_day, "Mon, tue, THU", :weekly, 1) }.not_to raise_error
+ end
+
+ it "raises error for invalid day value" do
+ expect { resource.send(:validate_create_day, "xyz", :weekly, 1) }.to raise_error(ArgumentError, "day property invalid. Only valid values are: MON, TUE, WED, THU, FRI, SAT, SUN, *. Multiple values must be separated by a comma.")
+ end
+ end
+
+ context "#validate_create_months" do
+ it "raises error if frequency is not :monthly" do
+ expect { resource.send(:validate_create_months, "Jan", :once) }.to raise_error(ArgumentError, "months property is only valid for tasks that run monthly")
+ end
+
+ it "accepts a valid single month" do
+ expect { resource.send(:validate_create_months, "Feb", :monthly) }.not_to raise_error
+ end
+
+ it "accepts a comma separated list of valid months" do
+ expect { resource.send(:validate_create_months, "Jan, mar, AUG", :monthly) }.not_to raise_error
+ end
+
+ it "raises error for invalid month value" do
+ expect { resource.send(:validate_create_months, "xyz", :monthly) }.to raise_error(ArgumentError, "months property invalid. Only valid values are: JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC, *. Multiple values must be separated by a comma.")
+ end
+ end
+
+ context "#validate_idle_time" do
+ it "raises error if frequency is not :on_idle" do
+ [:minute, :hourly, :daily, :weekly, :monthly, :once, :on_logon, :onstart, :none].each do |frequency|
+ expect { resource.send(:validate_idle_time, 5, frequency) }.to raise_error(ArgumentError, "idle_time property is only valid for tasks that run on_idle")
+ end
+ end
+
+ it "raises error if idle_time > 999" do
+ expect { resource.send(:validate_idle_time, 1000, :on_idle) }.to raise_error(ArgumentError, "idle_time value 1000 is invalid. Valid values for :on_idle frequency are 1 - 999.")
+ end
+
+ it "raises error if idle_time < 0" do
+ expect { resource.send(:validate_idle_time, -5, :on_idle) }.to raise_error(ArgumentError, "idle_time value -5 is invalid. Valid values for :on_idle frequency are 1 - 999.")
+ end
+
+ it "raises error if idle_time is not set" do
+ expect { resource.send(:validate_idle_time, nil, :on_idle) }.to raise_error(ArgumentError, "idle_time value should be set for :on_idle frequency.")
+ end
+
+ it "does not raises error if idle_time is not set for other frequencies" do
+ [:minute, :hourly, :daily, :weekly, :monthly, :once, :on_logon, :onstart, :none].each do |frequency|
+ expect { resource.send(:validate_idle_time, nil, frequency) }.not_to raise_error
+ end
+ end
+ end
+
+ context "#sec_to_dur" do
+ it "return nil when passed 0" do
+ expect(resource.send(:sec_to_dur, 0)).to eql("PT0S")
+ end
+ it "return PT1S when passed 1" do
+ expect(resource.send(:sec_to_dur, 1)).to eql("PT1S")
+ end
+ it "return PT86400S when passed 86400" do
+ expect(resource.send(:sec_to_dur, 86400)).to eql("PT86400S")
+ end
+ it "return PT86401S when passed 86401" do
+ expect(resource.send(:sec_to_dur, 86401)).to eql("PT86401S")
+ end
+ it "return PT86500S when passed 86500" do
+ expect(resource.send(:sec_to_dur, 86500)).to eql("PT86500S")
+ end
+ it "return PT604801S when passed 604801" do
+ expect(resource.send(:sec_to_dur, 604801)).to eql("PT604801S")
+ end
+ end
+end
diff --git a/spec/unit/resource/yum_package_spec.rb b/spec/unit/resource/yum_package_spec.rb
index dd0d3ae928..ce289ce45a 100644
--- a/spec/unit/resource/yum_package_spec.rb
+++ b/spec/unit/resource/yum_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,12 +32,53 @@ describe Chef::Resource::YumPackage, "initialize" do
end
+describe Chef::Resource::YumPackage do
+ before(:each) do
+ @resource = Chef::Resource::YumPackage.new("foo")
+ end
+
+ # this set of tests is somewhat terrible. the yum provider promiscuously writes over
+ # the new_resource.package_named/version/arch properties. until that is fixed properly
+ # we need to coerce and dup those properties into normal arrays. this does not affect
+ # strings because those are not mutated in place and they are not (currently) frozen
+ # in immutable attributes (even though they really, really should be).
+ context "when passed immutable node attribute arrays" do
+ let(:node) { Chef::Node.new }
+
+ before do
+ node.default["foo"] = %w{one two three}
+ end
+
+ it "allows mutation of the package_name array" do
+ @resource.package_name node["foo"]
+ expect(@resource.package_name).not_to be_a_kind_of(Chef::Node::ImmutableArray)
+ expect { @resource.package_name[0] = "four" }.not_to raise_error
+ expect(@resource.package_name).to eql(%w{four two three})
+ end
+
+ it "allows mutation of the version array" do
+ @resource.version node["foo"]
+ expect(@resource.version).not_to be_a_kind_of(Chef::Node::ImmutableArray)
+ expect { @resource.version[0] = "four" }.not_to raise_error
+ expect(@resource.version).to eql(%w{four two three})
+ end
+
+ it "allows mutation of the arch array" do
+ @resource.arch node["foo"]
+ expect(@resource.arch).not_to be_a_kind_of(Chef::Node::ImmutableArray)
+ expect { @resource.arch[0] = "four" }.not_to raise_error
+ expect(@resource.arch).to eql(%w{four two three})
+ end
+
+ end
+end
+
describe Chef::Resource::YumPackage, "arch" do
before(:each) do
@resource = Chef::Resource::YumPackage.new("foo")
end
- it "should set the arch variable to whatever is passed in" do
+ it "sets the arch variable to whatever is passed in" do
@resource.arch("i386")
expect(@resource.arch).to eql("i386")
end
@@ -65,6 +106,26 @@ describe Chef::Resource::YumPackage, "flush_cache" do
@resource.flush_cache(flush_hash)
expect(@resource.flush_cache).to eq(flush_hash)
end
+
+ it "should allow 'true' for flush_cache" do
+ @resource.flush_cache(true)
+ expect(@resource.flush_cache).to eq({ before: true, after: true })
+ end
+
+ it "should allow 'false' for flush_cache" do
+ @resource.flush_cache(false)
+ expect(@resource.flush_cache).to eq({ before: false, after: false })
+ end
+
+ it "should allow ':before' for flush_cache" do
+ @resource.flush_cache(:before)
+ expect(@resource.flush_cache).to eq({ before: true, after: false })
+ end
+
+ it "should allow ':after' for flush_cache" do
+ @resource.flush_cache(:after)
+ expect(@resource.flush_cache).to eq({ before: false, after: true })
+ end
end
describe Chef::Resource::YumPackage, "allow_downgrade" do
diff --git a/spec/unit/resource/yum_repository_spec.rb b/spec/unit/resource/yum_repository_spec.rb
new file mode 100644
index 0000000000..353ff7ce23
--- /dev/null
+++ b/spec/unit/resource/yum_repository_spec.rb
@@ -0,0 +1,106 @@
+#
+# Author:: Thom May (<thom@chef.io>)
+# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::YumRepository do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::YumRepository.new("multiverse", run_context) }
+
+ it "has a resource_name of :yum_repository" do
+ expect(resource.resource_name).to eq(:yum_repository)
+ end
+
+ it "the repositoryid property is the name_property" do
+ expect(resource.repositoryid).to eq("multiverse")
+ end
+
+ it "fails if the user provides a repositoryid with a forward slash" do
+ expect { resource.repositoryid "foo/bar" }.to raise_error(ArgumentError)
+ end
+
+ it "the timeout property expects numeric Strings" do
+ expect { resource.timeout "123" }.not_to raise_error(ArgumentError)
+ expect { resource.timeout "123foo" }.to raise_error(ArgumentError)
+ end
+
+ it "the priority property expects numeric Strings from '1' to '99'" do
+ expect { resource.priority "99" }.not_to raise_error(ArgumentError)
+ expect { resource.priority "1" }.not_to raise_error(ArgumentError)
+ expect { resource.priority "100" }.to raise_error(ArgumentError)
+ expect { resource.priority "0" }.to raise_error(ArgumentError)
+ end
+
+ it "the failovermethod property accepts 'priority' or 'roundrobin'" do
+ expect { resource.failovermethod "priority" }.not_to raise_error(ArgumentError)
+ expect { resource.failovermethod "roundrobin" }.not_to raise_error(ArgumentError)
+ expect { resource.failovermethod "bob" }.to raise_error(ArgumentError)
+ end
+
+ it "the http_caching property accepts 'packages', 'all', or 'none'" do
+ expect { resource.http_caching "packages" }.not_to raise_error(ArgumentError)
+ expect { resource.http_caching "all" }.not_to raise_error(ArgumentError)
+ expect { resource.http_caching "none" }.not_to raise_error(ArgumentError)
+ expect { resource.http_caching "bob" }.to raise_error(ArgumentError)
+ end
+
+ it "the metadata_expire property accepts a time value or 'never'" do
+ expect { resource.metadata_expire "100" }.not_to raise_error(ArgumentError)
+ expect { resource.metadata_expire "100d" }.not_to raise_error(ArgumentError)
+ expect { resource.metadata_expire "100h" }.not_to raise_error(ArgumentError)
+ expect { resource.metadata_expire "100m" }.not_to raise_error(ArgumentError)
+ expect { resource.metadata_expire "never" }.not_to raise_error(ArgumentError)
+ expect { resource.metadata_expire "100s" }.to raise_error(ArgumentError)
+ end
+
+ it "the mirror_expire property accepts a time value" do
+ expect { resource.mirror_expire "100" }.not_to raise_error(ArgumentError)
+ expect { resource.mirror_expire "100d" }.not_to raise_error(ArgumentError)
+ expect { resource.mirror_expire "100h" }.not_to raise_error(ArgumentError)
+ expect { resource.mirror_expire "100m" }.not_to raise_error(ArgumentError)
+ expect { resource.mirror_expire "never" }.to raise_error(ArgumentError)
+ end
+
+ it "the mirrorlist_expire property accepts a time value" do
+ expect { resource.mirrorlist_expire "100" }.not_to raise_error(ArgumentError)
+ expect { resource.mirrorlist_expire "100d" }.not_to raise_error(ArgumentError)
+ expect { resource.mirrorlist_expire "100h" }.not_to raise_error(ArgumentError)
+ expect { resource.mirrorlist_expire "100m" }.not_to raise_error(ArgumentError)
+ expect { resource.mirrorlist_expire "never" }.to raise_error(ArgumentError)
+ end
+
+ context "on linux", :linux_only do
+ it "resolves to a Noop class when yum is not found" do
+ expect(Chef::Provider::YumRepository).to receive(:which).with("yum").and_return(false)
+ expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
+ end
+
+ it "resolves to a YumRepository class when yum is found" do
+ expect(Chef::Provider::YumRepository).to receive(:which).with("yum").and_return(true)
+ expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::YumRepository)
+ end
+ end
+
+ context "on windows", :windows_only do
+ it "resolves to a NoOp provider" do
+ expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
+ end
+ end
+end
diff --git a/spec/unit/resource/zypper_repository_spec.rb b/spec/unit/resource/zypper_repository_spec.rb
new file mode 100644
index 0000000000..8f93aecd8e
--- /dev/null
+++ b/spec/unit/resource/zypper_repository_spec.rb
@@ -0,0 +1,70 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 2017 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::ZypperRepository do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::ZypperRepository.new("repo-source", run_context) }
+
+ it "has a resource_name of :zypper_repository" do
+ expect(resource.resource_name).to eq(:zypper_repository)
+ end
+
+ it "repo_name is the name_property" do
+ expect(resource.repo_name).to eql("repo-source")
+ end
+
+ it "fails if the user provides a repo_name with a forward slash" do
+ expect { resource.repo_name "foo/bar" }.to raise_error(ArgumentError)
+ end
+
+ it "has a default action of create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports all valid actions" do
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :refresh }.not_to raise_error
+ expect { resource.action :delete }.to raise_error(ArgumentError)
+ end
+
+ context "on linux", :linux_only do
+ it "resolves to a Noop class when on non-linux OS" do
+ node.automatic[:os] = "windows"
+ node.automatic[:platform_family] = "windows"
+ expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
+ end
+
+ it "resolves to a Noop class when on non-suse linux" do
+ node.automatic[:os] = "linux"
+ node.automatic[:platform_family] = "debian"
+ expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
+ end
+
+ it "resolves to a ZypperRepository class when on a suse platform_family" do
+ node.automatic[:os] = "linux"
+ node.automatic[:platform_family] = "suse"
+ expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::ZypperRepository)
+ end
+ end
+end
diff --git a/spec/unit/resource_collection/resource_set_spec.rb b/spec/unit/resource_collection/resource_set_spec.rb
index 20f6f70911..9955bc2bea 100644
--- a/spec/unit/resource_collection/resource_set_spec.rb
+++ b/spec/unit/resource_collection/resource_set_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Ball (<tball@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright 2014-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,6 +27,7 @@ describe Chef::ResourceCollection::ResourceSet do
let(:zen_master) { Chef::Resource::ZenMaster.new(zen_master_name) }
let(:zen_master2) { Chef::Resource::ZenMaster.new(zen_master2_name) }
let(:zen_follower) { Chef::Resource::ZenFollower.new(zen_follower_name) }
+ let(:zen_array) { Chef::Resource::ZenMaster.new( [ zen_master_name, zen_master2_name ]) }
describe "initialize" do
it "should return a Chef::ResourceSet" do
@@ -113,13 +114,27 @@ describe Chef::ResourceCollection::ResourceSet do
end
it "should find resources by strings of zen_master[a,b]" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
collection.insert_as(zen_master)
collection.insert_as(zen_master2)
check_by_names(collection.find("zen_master[#{zen_master_name},#{zen_master2_name}]"),
zen_master_name, zen_master2_name)
end
+ it "should find array names" do
+ collection.insert_as(zen_array)
+ expect(collection.find("zen_master[#{zen_master_name}, #{zen_master2_name}]")).to eql(zen_array)
+ end
+
+ it "should favor array names over multi resource syntax" do
+ collection.insert_as(zen_master)
+ collection.insert_as(zen_master2)
+ collection.insert_as(zen_array)
+ expect(collection.find("zen_master[#{zen_master_name}, #{zen_master2_name}]")).to eql(zen_array)
+ end
+
it "should find resources by strings of zen_master[a,b] with custom names" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
collection.insert_as(zen_master, :zzz, "name1")
collection.insert_as(zen_master2, :zzz, "name2")
check_by_names(collection.find("zzz[name1,name2]"),
@@ -134,6 +149,7 @@ describe Chef::ResourceCollection::ResourceSet do
end
it "should find resources of multiple types by strings of zen_master[a] with custom names" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
collection.insert_as(zen_master, :zzz, "name1")
collection.insert_as(zen_master2, :zzz, "name2")
collection.insert_as(zen_follower, :yyy, "name3")
@@ -162,6 +178,35 @@ describe Chef::ResourceCollection::ResourceSet do
it "raises an error when attempting to find a resource that does not exist" do
expect { collection.find("script[nonesuch]") }.to raise_error(Chef::Exceptions::ResourceNotFound)
end
+
+ context "nameless resources" do
+ let(:zen_master_name) { "" }
+
+ it "looks up nameless resources with find without brackets" do
+ collection.insert_as(zen_master)
+ expect(collection.find("zen_master")).to eq(zen_master)
+ end
+
+ it "looks up nameless resources with find with empty brackets" do
+ collection.insert_as(zen_master)
+ expect(collection.find("zen_master[]")).to eq(zen_master)
+ end
+
+ it "looks up nameless resources with find with empty string hash key" do
+ collection.insert_as(zen_master)
+ expect(collection.find(zen_master: "")).to eq(zen_master)
+ end
+
+ it "looks up nameless resources with lookup with empty brackets" do
+ collection.insert_as(zen_master)
+ expect(collection.lookup("zen_master[]")).to eq(zen_master)
+ end
+
+ it "looks up nameless resources with lookup with the resource" do
+ collection.insert_as(zen_master)
+ expect(collection.lookup(zen_master)).to eq(zen_master)
+ end
+ end
end
describe "validate_lookup_spec!" do
diff --git a/spec/unit/resource_collection_spec.rb b/spec/unit/resource_collection_spec.rb
index 3fec2d9477..c696572b13 100644
--- a/spec/unit/resource_collection_spec.rb
+++ b/spec/unit/resource_collection_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -98,11 +98,11 @@ describe Chef::ResourceCollection do
it "should allow you to iterate over every resource in the collection" do
load_up_resources
results = Array.new
- expect {
+ expect do
rc.each do |r|
results << r.name
end
- }.not_to raise_error
+ end.not_to raise_error
results.each_index do |i|
case i
when 0
@@ -120,11 +120,11 @@ describe Chef::ResourceCollection do
it "should allow you to iterate over every resource by index" do
load_up_resources
results = Array.new
- expect {
+ expect do
rc.each_index do |i|
results << rc[i].name
end
- }.not_to raise_error
+ end.not_to raise_error
results.each_index do |i|
case i
when 0
@@ -219,6 +219,7 @@ describe Chef::ResourceCollection do
end
it "should find resources by strings of zen_master[a,b]" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
load_up_resources
results = rc.resources("zen_master[monkey,dog]")
expect(results.length).to eql(2)
@@ -291,10 +292,10 @@ describe Chef::ResourceCollection do
expect(rc.respond_to?(:from_json)).to eq(false)
end
- it "should convert from json using the CHEF::JSONCompat library" do
+ it "should convert from json using the Chef::JSONCompat library" do
rc << resource
json = Chef::JSONCompat.to_json(rc)
- s_rc = Chef::JSONCompat.from_json(json)
+ s_rc = Chef::ResourceCollection.from_json(json)
expect(s_rc).to be_a_kind_of(Chef::ResourceCollection)
expect(s_rc[0].name).to eql(resource.name)
end
diff --git a/spec/unit/resource_definition_spec.rb b/spec/unit/resource_definition_spec.rb
index 45dfaffca7..cc19cc7a01 100644
--- a/spec/unit/resource_definition_spec.rb
+++ b/spec/unit/resource_definition_spec.rb
@@ -53,26 +53,26 @@ describe Chef::ResourceDefinition do
end
it "should accept a new definition with a symbol for a name" do
- expect {
+ expect do
defn.define :smoke do
end
- }.not_to raise_error
- expect {
+ end.not_to raise_error
+ expect do
defn.define "george washington" do
end
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
expect(defn.name).to eql(:smoke)
end
it "should accept a new definition with a hash" do
- expect {
+ expect do
defn.define :smoke, :cigar => "cuban", :cigarette => "marlboro" do
end
- }.not_to raise_error
+ end.not_to raise_error
end
it "should expose the prototype hash params in the params hash" do
- defn.define :smoke, :cigar => "cuban", :cigarette => "marlboro" do; end
+ defn.define(:smoke, :cigar => "cuban", :cigarette => "marlboro") {}
expect(defn.params[:cigar]).to eql("cuban")
expect(defn.params[:cigarette]).to eql("marlboro")
end
@@ -91,16 +91,16 @@ describe Chef::ResourceDefinition do
end
it "should raise an exception if prototype_params is not a hash" do
- expect {
+ expect do
defn.define :monkey, Array.new do
end
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should raise an exception if define is called without a block" do
- expect {
+ expect do
defn.define :monkey
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should load a description from a file" do
diff --git a/spec/unit/resource_inspector_spec.rb b/spec/unit/resource_inspector_spec.rb
new file mode 100644
index 0000000000..2cb9c0bb65
--- /dev/null
+++ b/spec/unit/resource_inspector_spec.rb
@@ -0,0 +1,60 @@
+# Copyright:: Copyright 2018, Chef Software, Inc
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/resource_inspector"
+
+class DummyResource < Chef::Resource
+ resource_name :dummy
+ description "A dummy resource"
+ examples <<~EOH
+ dummy "foo" do
+ first "yes"
+ end
+ EOH
+ introduced "14.0"
+ property :first, String, description: "My First Property", introduced: "14.0"
+
+ action :dummy do
+ return true
+ end
+end
+
+describe ResourceInspector do
+ describe "inspecting a resource" do
+ subject { ResourceInspector.extract_resource(DummyResource, false) }
+
+ it "returns a hash with required data" do
+ expect(subject[:description]).to eq "A dummy resource"
+ expect(subject[:actions]).to match_array [:nothing, :dummy]
+ end
+
+ context "excluding built in properties" do
+ it "returns a single property" do
+ expect(subject[:properties].count).to eq 1
+ expect(subject[:properties].first[:name]).to eq :first
+ end
+ end
+
+ context "including built in properties" do
+ subject { ResourceInspector.extract_resource(DummyResource, true) }
+ it "returns many properties" do
+ expect(subject[:properties].count).to be > 1
+ expect(subject[:properties].map { |n| n[:name] }).to include(:name, :first, :sensitive)
+ end
+ end
+ end
+end
diff --git a/spec/unit/resource_reporter_spec.rb b/spec/unit/resource_reporter_spec.rb
index 0df3a165bd..97a4fe4b30 100644
--- a/spec/unit/resource_reporter_spec.rb
+++ b/spec/unit/resource_reporter_spec.rb
@@ -3,7 +3,7 @@
# Author:: Prajakta Purohit (<prajakta@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
#
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright 2012-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -92,8 +92,8 @@ describe Chef::ResourceReporter do
context "when chef fails" do
before do
- allow(@rest_client).to receive(:raw_request).and_return({ "result" => "ok" });
- allow(@rest_client).to receive(:post).and_return({ "uri" => "https://example.com/reports/nodes/spitfire/runs/#{@run_id}" });
+ allow(@rest_client).to receive(:raw_request).and_return({ "result" => "ok" })
+ allow(@rest_client).to receive(:post).and_return({ "uri" => "https://example.com/reports/nodes/spitfire/runs/#{@run_id}" })
end
@@ -259,12 +259,36 @@ describe Chef::ResourceReporter do
describe "when generating a report for the server" do
before do
- allow(@rest_client).to receive(:raw_request).and_return({ "result" => "ok" });
- allow(@rest_client).to receive(:post).and_return({ "uri" => "https://example.com/reports/nodes/spitfire/runs/#{@run_id}" });
+ allow(@rest_client).to receive(:raw_request).and_return({ "result" => "ok" })
+ allow(@rest_client).to receive(:post).and_return({ "uri" => "https://example.com/reports/nodes/spitfire/runs/#{@run_id}" })
@resource_reporter.run_started(@run_status)
end
+ context "when the new_resource is sensitive" do
+ before do
+ @execute_resource = Chef::Resource::Execute.new("sensitive-resource")
+ @execute_resource.name("sensitive-resource")
+ @execute_resource.command('echo "password: SECRET"')
+ @execute_resource.sensitive(true)
+ @resource_reporter.resource_action_start(@execute_resource, :run)
+ @resource_reporter.resource_current_state_loaded(@execute_resource, :run, @current_resource)
+ @resource_reporter.resource_updated(@execute_resource, :run)
+ @resource_reporter.resource_completed(@execute_resource)
+ @run_status.stop_clock
+ @report = @resource_reporter.prepare_run_data
+ @first_update_report = @report["resources"].first
+ end
+
+ it "resource_name in prepared_run_data should be the same" do
+ expect(@first_update_report["name"]).to eq("sensitive-resource")
+ end
+
+ it "resource_command in prepared_run_data should be blank" do
+ expect(@first_update_report["after"]).to eq({ :command => "sensitive-resource" })
+ end
+ end
+
context "when the new_resource does not have a string for name and identity" do
context "the new_resource name and id are nil" do
before do
@@ -375,11 +399,11 @@ describe Chef::ResourceReporter do
end
it "includes an updated resource's initial state" do
- expect(@first_update_report["before"]).to eq(current_resource.state)
+ expect(@first_update_report["before"]).to eq(current_resource.state_for_resource_reporter)
end
it "includes an updated resource's final state" do
- expect(@first_update_report["after"]).to eq(new_resource.state)
+ expect(@first_update_report["after"]).to eq(new_resource.state_for_resource_reporter)
end
it "includes the resource's name" do
@@ -516,11 +540,11 @@ describe Chef::ResourceReporter do
end
it "includes an updated resource's initial state" do
- expect(@first_update_report["before"]).to eq(@current_resource.state)
+ expect(@first_update_report["before"]).to eq(@current_resource.state_for_resource_reporter)
end
it "includes an updated resource's final state" do
- expect(@first_update_report["after"]).to eq(@new_resource.state)
+ expect(@first_update_report["after"]).to eq(@new_resource.state_for_resource_reporter)
end
it "includes the resource's name" do
@@ -611,7 +635,7 @@ describe Chef::ResourceReporter do
end
it "prints an error about the 404" do
- expect(Chef::Log).to receive(:debug).with(/404/)
+ expect(Chef::Log).to receive(:trace).with(/404/)
@resource_reporter.run_started(@run_status)
end
@@ -664,9 +688,9 @@ describe Chef::ResourceReporter do
it "fails the run and prints an message about the error" do
expect(Chef::Log).to receive(:error).with(/500/)
- expect {
+ expect do
@resource_reporter.run_started(@run_status)
- }.to raise_error(Net::HTTPServerException)
+ end.to raise_error(Net::HTTPServerException)
end
end
@@ -749,9 +773,9 @@ describe Chef::ResourceReporter do
it "should raise if an unkwown error happens" do
allow(@rest_client).to receive(:raw_request).and_raise(Exception.new)
- expect {
+ expect do
@resource_reporter.post_reporting_data
- }.to raise_error(Exception)
+ end.to raise_error(Exception)
end
end
end
diff --git a/spec/unit/resource_resolver_spec.rb b/spec/unit/resource_resolver_spec.rb
index f3a20ab0e3..d707ade009 100644
--- a/spec/unit/resource_resolver_spec.rb
+++ b/spec/unit/resource_resolver_spec.rb
@@ -20,11 +20,11 @@ require "spec_helper"
require "chef/resource_resolver"
describe Chef::ResourceResolver do
- it '#resolve' do
+ it "#resolve" do
expect(described_class.resolve(:execute)).to eq(Chef::Resource::Execute)
end
- it '#list' do
+ it "#list" do
expect(described_class.list(:package)).to_not be_empty
end
@@ -33,19 +33,19 @@ describe Chef::ResourceResolver do
described_class.new(Chef::Node.new, "execute")
end
- it '#resolve' do
+ it "#resolve" do
expect(resolver.resolve).to eq Chef::Resource::Execute
end
- it '#list' do
+ it "#list" do
expect(resolver.list).to eq [ Chef::Resource::Execute ]
end
- it '#provided_by? returns true when resource class is in the list' do
+ it "#provided_by? returns true when resource class is in the list" do
expect(resolver.provided_by?(Chef::Resource::Execute)).to be_truthy
end
- it '#provided_by? returns false when resource class is not in the list' do
+ it "#provided_by? returns false when resource class is not in the list" do
expect(resolver.provided_by?(Chef::Resource::File)).to be_falsey
end
end
diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb
index e88931fa54..523f9f7365 100644
--- a/spec/unit/resource_spec.rb
+++ b/spec/unit/resource_spec.rb
@@ -3,7 +3,7 @@
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Tim Hinderliter (<tim@chef.io>)
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -95,7 +95,7 @@ describe Chef::Resource do
end
describe "when an identity attribute has been declared" do
- let(:file_resource) {
+ let(:file_resource) do
file_resource_class = Class.new(Chef::Resource) do
identity_attr :path
attr_accessor :path
@@ -104,7 +104,7 @@ describe Chef::Resource do
file_resource = file_resource_class.new("identity-attr-test")
file_resource.path = "/tmp/foo.txt"
file_resource
- }
+ end
it "gives the value of its identity attribute" do
expect(file_resource.identity).to eq("/tmp/foo.txt")
@@ -140,7 +140,7 @@ describe Chef::Resource do
end
describe "when a set of state attributes has been declared" do
- let(:file_resource) {
+ let(:file_resource) do
file_resource_class = Class.new(Chef::Resource) do
state_attrs :checksum, :owner, :group, :mode
@@ -157,10 +157,10 @@ describe Chef::Resource do
file_resource.group = "wheel"
file_resource.mode = "0644"
file_resource
- }
+ end
it "describes its state" do
- resource_state = file_resource.state
+ resource_state = file_resource.state_for_resource_reporter
expect(resource_state.keys).to match_array([:checksum, :owner, :group, :mode])
expect(resource_state[:checksum]).to eq("abc123")
expect(resource_state[:owner]).to eq("root")
@@ -169,15 +169,34 @@ describe Chef::Resource do
end
end
+ describe "#state_for_resource_reporter" do
+ context "when a property is marked as sensitive" do
+ it "suppresses the sensitive property's value" do
+ resource_class = Class.new(Chef::Resource) { property :foo, String, sensitive: true }
+ resource = resource_class.new("sensitive_property_tests")
+ resource.foo = "some value"
+ expect(resource.state_for_resource_reporter[:foo]).to eq("*sensitive value suppressed*")
+ end
+ end
+
+ context "when a property is not marked as sensitive" do
+ it "does not suppress the property's value" do
+ resource_class = Class.new(Chef::Resource) { property :foo, String }
+ resource = resource_class.new("sensitive_property_tests")
+ resource.foo = "some value"
+ expect(resource.state_for_resource_reporter[:foo]).to eq("some value")
+ end
+ end
+ end
+
describe "load_from" do
- let(:prior_resource) {
+ let(:prior_resource) do
prior_resource = Chef::Resource.new("funk")
- prior_resource.supports(:funky => true)
prior_resource.source_line
prior_resource.allowed_actions << :funkytown
prior_resource.action(:funkytown)
prior_resource
- }
+ end
before(:each) do
resource.allowed_actions << :funkytown
run_context.resource_collection << prior_resource
@@ -185,7 +204,6 @@ describe Chef::Resource do
it "should load the attributes of a prior resource" do
resource.load_from(prior_resource)
- expect(resource.supports).to eq({ :funky => true })
end
it "should not inherit the action from the prior resource" do
@@ -213,14 +231,6 @@ describe Chef::Resource do
end
end
- describe "noop" do
- it "should accept true or false for noop" do
- expect { resource.noop true }.not_to raise_error
- expect { resource.noop false }.not_to raise_error
- expect { resource.noop "eat it" }.to raise_error(ArgumentError)
- end
- end
-
describe "notifies" do
it "should make notified resources appear in the actions hash" do
run_context.resource_collection << Chef::Resource::ZenMaster.new("coffee")
@@ -236,9 +246,9 @@ describe Chef::Resource do
it "should raise an exception if told to act in other than :delay or :immediate(ly)" do
run_context.resource_collection << Chef::Resource::ZenMaster.new("coffee")
- expect {
+ expect do
resource.notifies :reload, run_context.resource_collection.find(:zen_master => "coffee"), :someday
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should allow multiple notified resources appear in the actions hash" do
@@ -274,6 +284,23 @@ describe Chef::Resource do
resource.notifies :reload, run_context.resource_collection.find(:zen_master => "coffee, tea")
expect(resource.delayed_notifications.detect { |e| e.resource.name == "coffee, tea" && e.action == :reload }).not_to be_nil
end
+
+ it "notifies a resource without a name via a string name with brackets" do
+ run_context.resource_collection << Chef::Resource::ZenMaster.new("")
+ resource.notifies :reload, "zen_master[]"
+ end
+
+ it "notifies a resource without a name via a string name without brackets" do
+ run_context.resource_collection << Chef::Resource::ZenMaster.new("")
+ resource.notifies :reload, "zen_master"
+ expect(resource.delayed_notifications.first.resource).to eql("zen_master")
+ end
+
+ it "notifies a resource without a name via a hash name with an empty string" do
+ run_context.resource_collection << Chef::Resource::ZenMaster.new("")
+ resource.notifies :reload, zen_master: ""
+ expect(resource.delayed_notifications.first.resource).to eql(zen_master: "")
+ end
end
describe "subscribes" do
@@ -334,6 +361,63 @@ describe Chef::Resource do
end
end
+ describe "to_text" do
+ it "prints nice message" do
+ resource_class = Class.new(Chef::Resource) { property :foo, String }
+ resource = resource_class.new("sensitive_property_tests")
+ resource.foo = "some value"
+ expect(resource.to_text).to match(/foo "some value"/)
+ end
+
+ context "when property is sensitive" do
+ it "supresses that properties value" do
+ resource_class = Class.new(Chef::Resource) { property :foo, String, sensitive: true }
+ resource = resource_class.new("sensitive_property_tests")
+ resource.foo = "some value"
+ expect(resource.to_text).to match(/foo "\*sensitive value suppressed\*"/)
+ end
+ end
+
+ context "when property is required" do
+ it "does not propagate vailidation errors" do
+ resource_class = Class.new(Chef::Resource) { property :foo, String, required: true }
+ resource = resource_class.new("required_property_tests")
+ expect { resource.to_text }.to_not raise_error Chef::Exceptions::ValidationFailed
+ end
+ end
+ end
+
+ context "Documentation of resources" do
+ it "can have a description" do
+ c = Class.new(Chef::Resource) do
+ description "my description"
+ end
+ expect(c.description).to eq "my description"
+ end
+
+ it "can say when it was introduced" do
+ c = Class.new(Chef::Resource) do
+ introduced "14.0"
+ end
+ expect(c.introduced).to eq "14.0"
+ end
+
+ it "can have some examples" do
+ c = Class.new(Chef::Resource) do
+ examples <<-EOH
+resource "foo" do
+ foo foo
+end
+ EOH
+ end
+ expect(c.examples).to eq <<-EOH
+resource "foo" do
+ foo foo
+end
+ EOH
+ end
+ end
+
describe "self.resource_name" do
context "When resource_name is not set" do
it "and there are no provides lines, resource_name is nil" do
@@ -414,18 +498,6 @@ describe Chef::Resource do
end
end
- describe "is" do
- it "should return the arguments passed with 'is'" do
- zm = Chef::Resource::ZenMaster.new("coffee")
- expect(zm.is("one", "two", "three")).to eq(%w{one two three})
- end
-
- it "should allow arguments preceded by is to methods" do
- resource.noop(resource.is(true))
- expect(resource.noop).to eql(true)
- end
- end
-
describe "to_json" do
it "should serialize to json" do
json = resource.to_json
@@ -443,10 +515,10 @@ describe Chef::Resource do
let(:resource_class) { Class.new(Chef::Resource) { property :a, default: 1 } }
it "should include the default in the hash" do
expect(resource.to_hash.keys.sort).to eq([:a, :allowed_actions, :params, :provider, :updated,
- :updated_by_last_action, :before, :supports,
- :noop, :ignore_failure, :name, :source_line,
- :action, :retries, :retry_delay, :elapsed_time,
- :default_guard_interpreter, :guard_interpreter, :sensitive].sort)
+ :updated_by_last_action, :before,
+ :name, :source_line,
+ :action, :elapsed_time,
+ :default_guard_interpreter, :guard_interpreter].sort)
expect(resource.to_hash[:name]).to eq "funk"
expect(resource.to_hash[:a]).to eq 1
end
@@ -455,10 +527,10 @@ describe Chef::Resource do
it "should convert to a hash" do
hash = resource.to_hash
expected_keys = [ :allowed_actions, :params, :provider, :updated,
- :updated_by_last_action, :before, :supports,
- :noop, :ignore_failure, :name, :source_line,
- :action, :retries, :retry_delay, :elapsed_time,
- :default_guard_interpreter, :guard_interpreter, :sensitive ]
+ :updated_by_last_action, :before,
+ :name, :source_line,
+ :action, :elapsed_time,
+ :default_guard_interpreter, :guard_interpreter ]
expect(hash.keys - expected_keys).to eq([])
expect(expected_keys - hash.keys).to eq([])
expect(hash[:name]).to eql("funk")
@@ -468,24 +540,12 @@ describe Chef::Resource do
describe "self.json_create" do
it "should deserialize itself from json" do
json = Chef::JSONCompat.to_json(resource)
- serialized_node = Chef::JSONCompat.from_json(json)
+ serialized_node = Chef::Resource.from_json(json)
expect(serialized_node).to be_a_kind_of(Chef::Resource)
expect(serialized_node.name).to eql(resource.name)
end
end
- describe "supports" do
- it "should allow you to set what features this resource supports" do
- support_hash = { :one => :two }
- resource.supports(support_hash)
- expect(resource.supports).to eql(support_hash)
- end
-
- it "should return the current value of supports" do
- expect(resource.supports).to eq({})
- end
- end
-
describe "ignore_failure" do
it "should default to throwing an error if a provider fails for a resource" do
expect(resource.ignore_failure).to eq(false)
@@ -496,19 +556,24 @@ describe Chef::Resource do
expect(resource.ignore_failure).to eq(true)
end
- it "should allow you to epic_fail" do
- resource.epic_fail(true)
- expect(resource.epic_fail).to eq(true)
+ it "should allow you to set quiet ignore_failure as a symbol" do
+ resource.ignore_failure(:quiet)
+ expect(resource.ignore_failure).to eq(:quiet)
+ end
+
+ it "should allow you to set quiet ignore_failure as a string" do
+ resource.ignore_failure("quiet")
+ expect(resource.ignore_failure).to eq("quiet")
end
end
describe "retries" do
- let(:retriable_resource) {
+ let(:retriable_resource) do
retriable_resource = Chef::Resource::Cat.new("precious", run_context)
retriable_resource.provider = Chef::Provider::SnakeOil
retriable_resource.action = :purr
retriable_resource
- }
+ end
before do
node.automatic_attrs[:platform] = "fubuntu"
@@ -545,31 +610,18 @@ describe Chef::Resource do
expect { retriable_resource.run_action(:purr) }.to raise_error(RuntimeError)
expect(retriable_resource.retries).to eq(3)
end
- end
- describe "setting the base provider class for the resource" do
-
- it "defaults to Chef::Provider for the base class" do
- expect(Chef::Resource.provider_base).to eq(Chef::Provider)
- end
-
- it "allows the base provider to be overridden" do
- Chef::Config.treat_deprecation_warnings_as_errors(false)
- class OverrideProviderBaseTest < Chef::Resource
- provider_base Chef::Provider::Package
- end
+ it "should not rescue from non-StandardError exceptions" do
+ retriable_resource.retries(3)
+ retriable_resource.retry_delay(0) # No need to wait.
- expect(OverrideProviderBaseTest.provider_base).to eq(Chef::Provider::Package)
- end
+ provider = Chef::Provider::SnakeOil.new(retriable_resource, run_context)
+ allow(Chef::Provider::SnakeOil).to receive(:new).and_return(provider)
+ allow(provider).to receive(:action_purr).and_raise(LoadError)
- it "warns when setting provider_base" do
- expect {
- class OverrideProviderBaseTest2 < Chef::Resource
- provider_base Chef::Provider::Package
- end
- }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
+ expect(retriable_resource).not_to receive(:sleep)
+ expect { retriable_resource.run_action(:purr) }.to raise_error(LoadError)
end
-
end
it "runs an action by finding its provider, loading the current resource and then running the action" do
@@ -607,11 +659,11 @@ describe Chef::Resource do
end
describe "when invoking its action" do
- let(:resource) {
+ let(:resource) do
resource = Chef::Resource.new("provided", run_context)
resource.provider = Chef::Provider::SnakeOil
resource
- }
+ end
before do
node.automatic_attrs[:platform] = "fubuntu"
node.automatic_attrs[:platform_version] = "10.04"
@@ -797,11 +849,11 @@ describe Chef::Resource do
end
describe "when resource action is :nothing" do
- let(:resource1) {
+ let(:resource1) do
resource1 = Chef::Resource::Cat.new("sugar", run_context)
resource1.action = :nothing
resource1
- }
+ end
before do
node.automatic_attrs[:platform] = "fubuntu"
node.automatic_attrs[:platform_version] = "10.04"
@@ -818,10 +870,8 @@ describe Chef::Resource do
it "should run only_if/not_if conditionals when notified to run another action (CHEF-972)" do
snitch_var1 = snitch_var2 = 0
runner = Chef::Runner.new(run_context)
- Chef::Platform.set(
- :resource => :cat,
- :provider => Chef::Provider::SnakeOil
- )
+
+ Chef::Provider::SnakeOil.provides :cat
resource1.only_if { snitch_var1 = 1 }
resource1.not_if { snitch_var2 = 2 }
diff --git a/spec/unit/rest/auth_credentials_spec.rb b/spec/unit/rest/auth_credentials_spec.rb
deleted file mode 100644
index 2728463c81..0000000000
--- a/spec/unit/rest/auth_credentials_spec.rb
+++ /dev/null
@@ -1,292 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Christopher Brown (<cb@chef.io>)
-# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-require "uri"
-require "net/https"
-
-describe Chef::REST::AuthCredentials do
- before do
- @key_file_fixture = CHEF_SPEC_DATA + "/ssl/private_key.pem"
- @key = OpenSSL::PKey::RSA.new(IO.read(@key_file_fixture).strip)
- @auth_credentials = Chef::REST::AuthCredentials.new("client-name", @key)
- end
-
- it "has a client name" do
- expect(@auth_credentials.client_name).to eq("client-name")
- end
-
- it "loads the private key when initialized with the path to the key" do
- expect(@auth_credentials.key).to respond_to(:private_encrypt)
- expect(@auth_credentials.key).to eq(@key)
- end
-
- describe "when loading the private key" do
- it "strips extra whitespace before checking the key" do
- key_file_fixture = CHEF_SPEC_DATA + "/ssl/private_key_with_whitespace.pem"
- expect { Chef::REST::AuthCredentials.new("client-name", @key_file_fixture) }.not_to raise_error
- end
- end
-
- describe "generating signature headers for a request" do
- before do
- @request_time = Time.at(1270920860)
- @request_params = { :http_method => :POST, :path => "/clients", :body => '{"some":"json"}', :host => "localhost" }
- allow(Chef::Config).to(
- receive(:[]).with(:authentication_protocol_version).and_return(protocol_version))
- end
-
- context "when configured for version 1.0 of the authn protocol" do
- let(:protocol_version) { "1.0" }
-
- it "generates signature headers for the request" do
- allow(Time).to receive(:now).and_return(@request_time)
- actual = @auth_credentials.signature_headers(@request_params)
- expect(actual["HOST"]).to eq("localhost")
- expect(actual["X-OPS-AUTHORIZATION-1"]).to eq("kBssX1ENEwKtNYFrHElN9vYGWS7OeowepN9EsYc9csWfh8oUovryPKDxytQ/")
- expect(actual["X-OPS-AUTHORIZATION-2"]).to eq("Wc2/nSSyxdWJjjfHzrE+YrqNQTaArOA7JkAf5p75eTUonCWcvNPjFrZVgKGS")
- expect(actual["X-OPS-AUTHORIZATION-3"]).to eq("yhzHJQh+lcVA9wwARg5Hu9q+ddS8xBOdm3Vp5atl5NGHiP0loiigMYvAvzPO")
- expect(actual["X-OPS-AUTHORIZATION-4"]).to eq("r9853eIxwYMhn5hLGhAGFQznJbE8+7F/lLU5Zmk2t2MlPY8q3o1Q61YD8QiJ")
- expect(actual["X-OPS-AUTHORIZATION-5"]).to eq("M8lIt53ckMyUmSU0DDURoiXLVkE9mag/6Yq2tPNzWq2AdFvBqku9h2w+DY5k")
- expect(actual["X-OPS-AUTHORIZATION-6"]).to eq("qA5Rnzw5rPpp3nrWA9jKkPw4Wq3+4ufO2Xs6w7GCjA==")
- expect(actual["X-OPS-CONTENT-HASH"]).to eq("1tuzs5XKztM1ANrkGNPah6rW9GY=")
- expect(actual["X-OPS-SIGN"]).to match(%r{(version=1\.0)|(algorithm=sha1;version=1.0;)})
- expect(actual["X-OPS-TIMESTAMP"]).to eq("2010-04-10T17:34:20Z")
- expect(actual["X-OPS-USERID"]).to eq("client-name")
- end
- end
-
- context "when configured for version 1.1 of the authn protocol" do
- let(:protocol_version) { "1.1" }
-
- it "generates the correct signature for version 1.1" do
- allow(Time).to receive(:now).and_return(@request_time)
- actual = @auth_credentials.signature_headers(@request_params)
- expect(actual["HOST"]).to eq("localhost")
- expect(actual["X-OPS-CONTENT-HASH"]).to eq("1tuzs5XKztM1ANrkGNPah6rW9GY=")
- expect(actual["X-OPS-SIGN"]).to eq("algorithm=sha1;version=1.1;")
- expect(actual["X-OPS-TIMESTAMP"]).to eq("2010-04-10T17:34:20Z")
- expect(actual["X-OPS-USERID"]).to eq("client-name")
-
- # mixlib-authN will test the actual signature stuff for each version of
- # the protocol so we won't test it again here.
- end
- end
- end
-end
-
-describe Chef::REST::RESTRequest do
- let(:url) { URI.parse("http://chef.example.com:4000/?q=chef_is_awesome") }
-
- def new_request(method = nil)
- method ||= :POST
- Chef::REST::RESTRequest.new(method, url, @req_body, @headers)
- end
-
- before do
- @auth_credentials = Chef::REST::AuthCredentials.new("client-name", CHEF_SPEC_DATA + "/ssl/private_key.pem")
- @req_body = '{"json_data":"as_a_string"}'
- @headers = { "Content-type" => "application/json",
- "Accept" => "application/json",
- "Accept-Encoding" => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE,
- "Host" => "chef.example.com:4000" }
- @request = Chef::REST::RESTRequest.new(:POST, url, @req_body, @headers)
- end
-
- it "stores the url it was created with" do
- expect(@request.url).to eq(url)
- end
-
- it "stores the HTTP method" do
- expect(@request.method).to eq(:POST)
- end
-
- it "adds the chef version header" do
- expect(@request.headers).to eq(@headers.merge("X-Chef-Version" => ::Chef::VERSION))
- end
-
- describe "configuring the HTTP request" do
- let(:url) do
- URI.parse("http://homie:theclown@chef.example.com:4000/?q=chef_is_awesome")
- end
-
- it "configures GET requests" do
- @req_body = nil
- rest_req = new_request(:GET)
- expect(rest_req.http_request).to be_a_kind_of(Net::HTTP::Get)
- expect(rest_req.http_request.path).to eq("/?q=chef_is_awesome")
- expect(rest_req.http_request.body).to be_nil
- end
-
- it "configures POST requests, including the body" do
- expect(@request.http_request).to be_a_kind_of(Net::HTTP::Post)
- expect(@request.http_request.path).to eq("/?q=chef_is_awesome")
- expect(@request.http_request.body).to eq(@req_body)
- end
-
- it "configures PUT requests, including the body" do
- rest_req = new_request(:PUT)
- expect(rest_req.http_request).to be_a_kind_of(Net::HTTP::Put)
- expect(rest_req.http_request.path).to eq("/?q=chef_is_awesome")
- expect(rest_req.http_request.body).to eq(@req_body)
- end
-
- it "configures DELETE requests" do
- rest_req = new_request(:DELETE)
- expect(rest_req.http_request).to be_a_kind_of(Net::HTTP::Delete)
- expect(rest_req.http_request.path).to eq("/?q=chef_is_awesome")
- expect(rest_req.http_request.body).to be_nil
- end
-
- it "configures HTTP basic auth" do
- rest_req = new_request(:GET)
- expect(rest_req.http_request.to_hash["authorization"]).to eq(["Basic aG9taWU6dGhlY2xvd24="])
- end
- end
-
- describe "configuring the HTTP client" do
- it "configures the HTTP client for the host and port" do
- http_client = new_request.http_client
- expect(http_client.address).to eq("chef.example.com")
- expect(http_client.port).to eq(4000)
- end
-
- it "configures the HTTP client with the read timeout set in the config file" do
- Chef::Config[:rest_timeout] = 9001
- expect(new_request.http_client.read_timeout).to eq(9001)
- end
-
- describe "for proxy" do
- before do
- stub_const("ENV", "http_proxy" => "http://proxy.example.com:3128",
- "https_proxy" => "http://sproxy.example.com:3129",
- "http_proxy_user" => nil,
- "http_proxy_pass" => nil,
- "https_proxy_user" => nil,
- "https_proxy_pass" => nil,
- "no_proxy" => nil
- )
- end
-
- describe "with :no_proxy nil" do
- it "configures the proxy address and port when using http scheme" do
- http_client = new_request.http_client
- expect(http_client.proxy?).to eq(true)
- expect(http_client.proxy_address).to eq("proxy.example.com")
- expect(http_client.proxy_port).to eq(3128)
- expect(http_client.proxy_user).to be_nil
- expect(http_client.proxy_pass).to be_nil
- end
-
- context "when the url has an https scheme" do
- let(:url) { URI.parse("https://chef.example.com:4000/?q=chef_is_awesome") }
-
- it "configures the proxy address and port when using https scheme" do
- http_client = new_request.http_client
- expect(http_client.proxy?).to eq(true)
- expect(http_client.proxy_address).to eq("sproxy.example.com")
- expect(http_client.proxy_port).to eq(3129)
- expect(http_client.proxy_user).to be_nil
- expect(http_client.proxy_pass).to be_nil
- end
- end
- end
-
- describe "with :no_proxy set" do
- before do
- stub_const("ENV", "no_proxy" => "10.*,*.example.com")
- end
-
- it "does not configure the proxy address and port when using http scheme" do
- http_client = new_request.http_client
- expect(http_client.proxy?).to eq(false)
- expect(http_client.proxy_address).to be_nil
- expect(http_client.proxy_port).to be_nil
- expect(http_client.proxy_user).to be_nil
- expect(http_client.proxy_pass).to be_nil
- end
-
- context "when the url has an https scheme" do
- let(:url) { URI.parse("https://chef.example.com:4000/?q=chef_is_awesome") }
-
- it "does not configure the proxy address and port when using https scheme" do
- http_client = new_request.http_client
- expect(http_client.proxy?).to eq(false)
- expect(http_client.proxy_address).to be_nil
- expect(http_client.proxy_port).to be_nil
- expect(http_client.proxy_user).to be_nil
- expect(http_client.proxy_pass).to be_nil
- end
- end
- end
-
- describe "with :http_proxy_user and :http_proxy_pass set" do
- before do
- stub_const("ENV", "http_proxy" => "http://homie:theclown@proxy.example.com:3128")
- end
-
- it "configures the proxy user and pass when using http scheme" do
- http_client = new_request.http_client
- expect(http_client.proxy?).to eq(true)
- expect(http_client.proxy_user).to eq("homie")
- expect(http_client.proxy_pass).to eq("theclown")
- end
-
- context "when the url has an https scheme" do
- let(:url) { URI.parse("https://chef.example.com:4000/?q=chef_is_awesome") }
-
- it "does not configure the proxy user and pass when using https scheme" do
- http_client = new_request.http_client
- expect(http_client.proxy?).to eq(false)
- expect(http_client.proxy_user).to be_nil
- expect(http_client.proxy_pass).to be_nil
- end
- end
- end
-
- describe "with :https_proxy_user and :https_proxy_pass set" do
- before do
- stub_const("ENV", "http_proxy" => "http://proxy.example.com:3128",
- "https_proxy" => "https://homie:theclown@sproxy.example.com:3129"
- )
- end
-
- it "does not configure the proxy user and pass when using http scheme" do
- http_client = new_request.http_client
- expect(http_client.proxy?).to eq(true)
- expect(http_client.proxy_user).to be_nil
- expect(http_client.proxy_pass).to be_nil
- end
-
- context "when the url has an https scheme" do
- let(:url) { URI.parse("https://chef.example.com:4000/?q=chef_is_awesome") }
-
- it "configures the proxy user and pass when using https scheme" do
- http_client = new_request.http_client
- expect(http_client.proxy?).to eq(true)
- expect(http_client.proxy_user).to eq("homie")
- expect(http_client.proxy_pass).to eq("theclown")
- end
- end
- end
- end
- end
-end
diff --git a/spec/unit/rest_spec.rb b/spec/unit/rest_spec.rb
deleted file mode 100644
index ea3bd88023..0000000000
--- a/spec/unit/rest_spec.rb
+++ /dev/null
@@ -1,753 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Christopher Brown (<cb@chef.io>)
-# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-require "uri"
-require "net/https"
-require "stringio"
-
-SIGNING_KEY_DOT_PEM = "-----BEGIN RSA PRIVATE KEY-----
-MIIEpAIBAAKCAQEA49TA0y81ps0zxkOpmf5V4/c4IeR5yVyQFpX3JpxO4TquwnRh
-8VSUhrw8kkTLmB3cS39Db+3HadvhoqCEbqPE6915kXSuk/cWIcNozujLK7tkuPEy
-YVsyTioQAddSdfe+8EhQVf3oHxaKmUd6waXrWqYCnhxgOjxocenREYNhZ/OETIei
-PbOku47vB4nJK/0GhKBytL2XnsRgfKgDxf42BqAi1jglIdeq8lAWZNF9TbNBU21A
-O1iuT7Pm6LyQujhggPznR5FJhXKRUARXBJZawxpGV4dGtdcahwXNE4601aXPra+x
-PcRd2puCNoEDBzgVuTSsLYeKBDMSfs173W1QYwIDAQABAoIBAGF05q7vqOGbMaSD
-2Q7YbuE/JTHKTBZIlBI1QC2x+0P5GDxyEFttNMOVzcs7xmNhkpRw8eX1LrInrpMk
-WsIBKAFFEfWYlf0RWtRChJjNl+szE9jQxB5FJnWtJH/FHa78tR6PsF24aQyzVcJP
-g0FGujBihwgfV0JSCNOBkz8MliQihjQA2i8PGGmo4R4RVzGfxYKTIq9vvRq/+QEa
-Q4lpVLoBqnENpnY/9PTl6JMMjW2b0spbLjOPVwDaIzXJ0dChjNXo15K5SHI5mALJ
-I5gN7ODGb8PKUf4619ez194FXq+eob5YJdilTFKensIUvt3YhP1ilGMM+Chi5Vi/
-/RCTw3ECgYEA9jTw4wv9pCswZ9wbzTaBj9yZS3YXspGg26y6Ohq3ZmvHz4jlT6uR
-xK+DDcUiK4072gci8S4Np0fIVS7q6ivqcOdzXPrTF5/j+MufS32UrBbUTPiM1yoO
-ECcy+1szl/KoLEV09bghPbvC58PFSXV71evkaTETYnA/F6RK12lEepcCgYEA7OSy
-bsMrGDVU/MKJtwqyGP9ubA53BorM4Pp9VVVSCrGGVhb9G/XNsjO5wJC8J30QAo4A
-s59ZzCpyNRy046AB8jwRQuSwEQbejSdeNgQGXhZ7aIVUtuDeFFdaIz/zjVgxsfj4
-DPOuzieMmJ2MLR4F71ocboxNoDI7xruPSE8dDhUCgYA3vx732cQxgtHwAkeNPJUz
-dLiE/JU7CnxIoSB9fYUfPLI+THnXgzp7NV5QJN2qzMzLfigsQcg3oyo6F2h7Yzwv
-GkjlualIRRzCPaCw4Btkp7qkPvbs1QngIHALt8fD1N69P3DPHkTwjG4COjKWgnJq
-qoHKS6Fe/ZlbigikI6KsuwKBgQCTlSLoyGRHr6oj0hqz01EDK9ciMJzMkZp0Kvn8
-OKxlBxYW+jlzut4MQBdgNYtS2qInxUoAnaz2+hauqhSzntK3k955GznpUatCqx0R
-b857vWviwPX2/P6+E3GPdl8IVsKXCvGWOBZWTuNTjQtwbDzsUepWoMgXnlQJSn5I
-YSlLxQKBgQD16Gw9kajpKlzsPa6XoQeGmZALT6aKWJQlrKtUQIrsIWM0Z6eFtX12
-2jjHZ0awuCQ4ldqwl8IfRogWMBkHOXjTPVK0YKWWlxMpD/5+bGPARa5fir8O1Zpo
-Y6S6MeZ69Rp89ma4ttMZ+kwi1+XyHqC/dlcVRW42Zl5Dc7BALRlJjQ==
------END RSA PRIVATE KEY-----"
-
-describe Chef::REST do
- let(:base_url) { "http://chef.example.com:4000" }
-
- let(:monkey_uri) { URI.parse("http://chef.example.com:4000/monkey") }
-
- let(:log_stringio) { StringIO.new }
-
- let(:request_id) { "1234" }
-
- let(:rest) do
- allow(Chef::REST::CookieJar).to receive(:instance).and_return({})
- allow(Chef::RequestID.instance).to receive(:request_id).and_return(request_id)
- rest = Chef::REST.new(base_url, nil, nil)
- Chef::REST::CookieJar.instance.clear
- rest
- end
-
- let(:standard_read_headers) { { "Accept" => "application/json", "Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-REMOTE-REQUEST-ID" => request_id, "X-Ops-Server-API-Version" => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION } }
- let(:standard_write_headers) { { "Accept" => "application/json", "Content-Type" => "application/json", "Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-REMOTE-REQUEST-ID" => request_id, "X-Ops-Server-API-Version" => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION } }
-
- before(:each) do
- Chef::Log.init(log_stringio)
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- end
-
- it "should have content length validation middleware after compressor middleware" do
- middlewares = rest.instance_variable_get(:@middlewares)
- content_length = middlewares.find_index { |e| e.is_a? Chef::HTTP::ValidateContentLength }
- decompressor = middlewares.find_index { |e| e.is_a? Chef::HTTP::Decompressor }
-
- expect(content_length).not_to be_nil
- expect(decompressor).not_to be_nil
- expect(decompressor < content_length).to be_truthy
- end
-
- it "should allow the options hash to be frozen" do
- options = {}.freeze
- # should not raise any exception
- Chef::REST.new(base_url, nil, nil, options)
- end
-
- it "emits a deprecation warning" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = true
- expect { Chef::REST.new(base_url) }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
- /Chef::REST is deprecated. Please use Chef::ServerAPI, or investigate Ridley or ChefAPI./
- end
-
- context "when created with a chef zero URL" do
-
- let(:url) { "chefzero://localhost:1" }
-
- it "does not load the signing key" do
- expect { Chef::REST.new(url) }.to_not raise_error
- end
- end
-
- describe "calling an HTTP verb on a path or absolute URL" do
- it "adds a relative URL to the base url it was initialized with" do
- expect(rest.create_url("foo/bar/baz")).to eq(URI.parse(base_url + "/foo/bar/baz"))
- end
-
- it "replaces the base URL when given an absolute URL" do
- expect(rest.create_url("http://chef-rulez.example.com:9000")).to eq(URI.parse("http://chef-rulez.example.com:9000"))
- end
-
- it "makes a :GET request with the composed url object" do
- expect(rest).to receive(:send_http_request).
- with(:GET, monkey_uri, standard_read_headers, false).
- and_return([1, 2, 3])
- expect(rest).to receive(:apply_response_middleware).with(1, 2, 3).and_return([1, 2, 3])
- expect(rest).to receive("success_response?".to_sym).with(1).and_return(true)
- rest.get_rest("monkey")
- end
-
- it "makes a :GET reqest for a streaming download with the composed url" do
- expect(rest).to receive(:streaming_request).with("monkey", {})
- rest.get_rest("monkey", true)
- end
-
- it "makes a :DELETE request with the composed url object" do
- expect(rest).to receive(:send_http_request).
- with(:DELETE, monkey_uri, standard_read_headers, false).
- and_return([1, 2, 3])
- expect(rest).to receive(:apply_response_middleware).with(1, 2, 3).and_return([1, 2, 3])
- expect(rest).to receive("success_response?".to_sym).with(1).and_return(true)
- rest.delete_rest("monkey")
- end
-
- it "makes a :POST request with the composed url object and data" do
- expect(rest).to receive(:send_http_request).
- with(:POST, monkey_uri, standard_write_headers, "\"data\"").
- and_return([1, 2, 3])
- expect(rest).to receive(:apply_response_middleware).with(1, 2, 3).and_return([1, 2, 3])
- expect(rest).to receive("success_response?".to_sym).with(1).and_return(true)
- rest.post_rest("monkey", "data")
- end
-
- it "makes a :PUT request with the composed url object and data" do
- expect(rest).to receive(:send_http_request).
- with(:PUT, monkey_uri, standard_write_headers, "\"data\"").
- and_return([1, 2, 3])
- expect(rest).to receive(:apply_response_middleware).with(1, 2, 3).and_return([1, 2, 3])
- expect(rest).to receive("success_response?".to_sym).with(1).and_return(true)
- rest.put_rest("monkey", "data")
- end
- end
-
- describe "legacy API" do
- let(:rest) do
- Chef::REST.new(base_url)
- end
-
- before(:each) do
- Chef::Config[:node_name] = "webmonkey.example.com"
- Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem"
- end
-
- it "responds to raw_http_request as a public method" do
- expect(rest.public_methods.map(&:to_s)).to include("raw_http_request")
- end
-
- it "calls the authn middleware" do
- data = "\"secure data\""
-
- auth_headers = standard_write_headers.merge({ "auth_done" => "yep" })
-
- expect(rest.authenticator).to receive(:handle_request).
- with(:POST, monkey_uri, standard_write_headers, data).
- and_return([:POST, monkey_uri, auth_headers, data])
- expect(rest).to receive(:send_http_request).
- with(:POST, monkey_uri, auth_headers, data).
- and_return([1, 2, 3])
- expect(rest).to receive("success_response?".to_sym).with(1).and_return(true)
- rest.raw_http_request(:POST, monkey_uri, standard_write_headers, data)
- end
-
- it "sets correct authn headers" do
- data = "\"secure data\""
- method, uri, auth_headers, d = rest.authenticator.handle_request(:POST, monkey_uri, standard_write_headers, data)
-
- expect(rest).to receive(:send_http_request).
- with(:POST, monkey_uri, auth_headers, data).
- and_return([1, 2, 3])
- expect(rest).to receive("success_response?".to_sym).with(1).and_return(true)
- rest.raw_http_request(:POST, monkey_uri, standard_write_headers, data)
- end
- end
-
- describe "when configured to authenticate to the Chef server" do
- let(:base_url) { URI.parse("http://chef.example.com:4000") }
-
- let(:rest) do
- Chef::REST.new(base_url)
- end
-
- before do
- Chef::Config[:node_name] = "webmonkey.example.com"
- Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem"
- end
-
- it "configures itself to use the node_name and client_key in the config by default" do
- expect(rest.client_name).to eq("webmonkey.example.com")
- expect(rest.signing_key_filename).to eq(CHEF_SPEC_DATA + "/ssl/private_key.pem")
- end
-
- it "provides access to the raw key data" do
- expect(rest.signing_key).to eq(SIGNING_KEY_DOT_PEM)
- end
-
- it "does not error out when initialized without credentials" do
- rest = Chef::REST.new(base_url, nil, nil) #should_not raise_error hides the bt from you, so screw it.
- expect(rest.client_name).to be_nil
- expect(rest.signing_key).to be_nil
- end
-
- it "indicates that requests should not be signed when it has no credentials" do
- rest = Chef::REST.new(base_url, nil, nil)
- expect(rest.sign_requests?).to be_falsey
- end
-
- it "raises PrivateKeyMissing when the key file doesn't exist" do
- expect { Chef::REST.new(base_url, "client-name", "/dev/null/nothing_here") }.to raise_error(Chef::Exceptions::PrivateKeyMissing)
- end
-
- it "raises InvalidPrivateKey when the key file doesnt' look like a key" do
- invalid_key_file = CHEF_SPEC_DATA + "/bad-config.rb"
- expect { Chef::REST.new(base_url, "client-name", invalid_key_file) }.to raise_error(Chef::Exceptions::InvalidPrivateKey)
- end
-
- it "can take private key as a sting :raw_key in options during initializaton" do
- expect(Chef::REST.new(base_url, "client-name", nil, :raw_key => SIGNING_KEY_DOT_PEM).signing_key).to eq(SIGNING_KEY_DOT_PEM)
- end
-
- it "raises InvalidPrivateKey when the key passed as string :raw_key in options doesnt' look like a key" do
- expect { Chef::REST.new(base_url, "client-name", nil, :raw_key => "bad key string") }.to raise_error(Chef::Exceptions::InvalidPrivateKey)
- end
-
- end
-
- context "when making REST requests" do
- let(:body) { "ninja" }
-
- let(:http_response) do
- http_response = Net::HTTPSuccess.new("1.1", "200", "successful rest req")
- allow(http_response).to receive(:read_body)
- allow(http_response).to receive(:body).and_return(body)
- http_response["Content-Length"] = body.bytesize.to_s
- http_response
- end
-
- let(:host_header) { "one" }
-
- let(:url) { URI.parse("http://one:80/?foo=bar") }
-
- let(:base_url) { "http://chef.example.com:4000" }
-
- let!(:http_client) do
- http_client = Net::HTTP.new(url.host, url.port)
- allow(http_client).to receive(:request).and_yield(http_response).and_return(http_response)
- http_client
- end
-
- let(:rest) do
- allow(Net::HTTP).to receive(:new).and_return(http_client)
- allow(Chef::REST::CookieJar).to receive(:instance).and_return({})
- allow(Chef::RequestID.instance).to receive(:request_id).and_return(request_id)
- rest = Chef::REST.new(base_url, nil, nil)
- Chef::REST::CookieJar.instance.clear
- rest
- end
-
- before(:each) do
- Chef::Config[:ssl_client_cert] = nil
- Chef::Config[:ssl_client_key] = nil
- end
-
- describe "as JSON API requests" do
- let(:request_mock) { {} }
-
- let(:base_headers) do #FIXME: huh?
- {
- "Accept" => "application/json",
- "X-Chef-Version" => Chef::VERSION,
- "Accept-Encoding" => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE,
- "Host" => host_header,
- "X-REMOTE-REQUEST-ID" => request_id,
- "X-Ops-Server-API-Version" => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION,
- }
- end
-
- before do
- allow(Net::HTTP::Get).to receive(:new).and_return(request_mock)
- end
-
- it "should always include the X-Chef-Version header" do
- expect(Net::HTTP::Get).to receive(:new).with("/?foo=bar", base_headers).and_return(request_mock)
- rest.request(:GET, url, {})
- end
-
- it "should always include the X-Remote-Request-Id header" do
- expect(Net::HTTP::Get).to receive(:new).with("/?foo=bar", base_headers).and_return(request_mock)
- rest.request(:GET, url, {})
- end
-
- it "sets the user agent to chef-client" do
- # XXX: must reset to default b/c knife changes the UA
- Chef::REST::RESTRequest.user_agent = Chef::REST::RESTRequest::DEFAULT_UA
- rest.request(:GET, url, {})
- expect(request_mock["User-Agent"]).to match(/^Chef Client\/#{Chef::VERSION}/)
- end
-
- # CHEF-3140
- context "when configured to disable compression" do
- let(:rest) do
- allow(Net::HTTP).to receive(:new).and_return(http_client)
- Chef::REST.new(base_url, nil, nil, :disable_gzip => true)
- end
-
- it "does not accept encoding gzip" do
- expect(rest.send(:build_headers, :GET, url, {})).not_to have_key("Accept-Encoding")
- end
-
- it "does not decompress a response encoded as gzip" do
- http_response.add_field("content-encoding", "gzip")
- request = Net::HTTP::Get.new(url.path)
- expect(Net::HTTP::Get).to receive(:new).and_return(request)
- # will raise a Zlib error if incorrect
- expect(rest.request(:GET, url, {})).to eq("ninja")
- end
- end
-
- context "when configured with custom http headers" do
- let(:custom_headers) do
- {
- "X-Custom-ChefSecret" => "sharpknives",
- "X-Custom-RequestPriority" => "extremely low",
- }
- end
-
- before(:each) do
- Chef::Config[:custom_http_headers] = custom_headers
- end
-
- after(:each) do
- Chef::Config[:custom_http_headers] = nil
- end
-
- it "should set them on the http request" do
- url_string = an_instance_of(String)
- header_hash = hash_including(custom_headers)
- expect(Net::HTTP::Get).to receive(:new).with(url_string, header_hash)
- rest.request(:GET, url, {})
- end
- end
-
- context "when setting cookies" do
- let(:rest) do
- allow(Net::HTTP).to receive(:new).and_return(http_client)
- Chef::REST::CookieJar.instance["#{url.host}:#{url.port}"] = "cookie monster"
- allow(Chef::RequestID.instance).to receive(:request_id).and_return(request_id)
- rest = Chef::REST.new(base_url, nil, nil)
- rest
- end
-
- it "should set the cookie for this request if one exists for the given host:port" do
- expect(Net::HTTP::Get).to receive(:new).with("/?foo=bar", base_headers.merge("Cookie" => "cookie monster")).and_return(request_mock)
- rest.request(:GET, url, {})
- end
- end
-
- it "should build a new HTTP GET request" do
- expect(Net::HTTP::Get).to receive(:new).with("/?foo=bar", base_headers).and_return(request_mock)
- rest.request(:GET, url, {})
- end
-
- it "should build a new HTTP POST request" do
- request = Net::HTTP::Post.new(url.path)
- expected_headers = base_headers.merge("Content-Type" => "application/json", "Content-Length" => "13")
-
- expect(Net::HTTP::Post).to receive(:new).with("/?foo=bar", expected_headers).and_return(request)
- rest.request(:POST, url, {}, { :one => :two })
- expect(request.body).to eq('{"one":"two"}')
- end
-
- it "should build a new HTTP PUT request" do
- request = Net::HTTP::Put.new(url.path)
- expected_headers = base_headers.merge("Content-Type" => "application/json", "Content-Length" => "13")
- expect(Net::HTTP::Put).to receive(:new).with("/?foo=bar", expected_headers).and_return(request)
- rest.request(:PUT, url, {}, { :one => :two })
- expect(request.body).to eq('{"one":"two"}')
- end
-
- it "should build a new HTTP DELETE request" do
- expect(Net::HTTP::Delete).to receive(:new).with("/?foo=bar", base_headers).and_return(request_mock)
- rest.request(:DELETE, url)
- end
-
- it "should raise an error if the method is not GET/PUT/POST/DELETE" do
- expect { rest.request(:MONKEY, url) }.to raise_error(ArgumentError)
- end
-
- it "returns nil when the response is successful but content-type is not JSON" do
- expect(rest.request(:GET, url)).to eq("ninja")
- end
-
- it "should fail if the response is truncated" do
- http_response["Content-Length"] = (body.bytesize + 99).to_s
- expect { rest.request(:GET, url) }.to raise_error(Chef::Exceptions::ContentLengthMismatch)
- end
-
- context "when JSON is returned" do
- let(:body) { '{"ohai2u":"json_api"}' }
- it "should inflate the body as to an object" do
- http_response.add_field("content-type", "application/json")
- expect(rest.request(:GET, url, {})).to eq({ "ohai2u" => "json_api" })
- end
-
- it "should fail if the response is truncated" do
- http_response.add_field("content-type", "application/json")
- http_response["Content-Length"] = (body.bytesize + 99).to_s
- expect { rest.request(:GET, url, {}) }.to raise_error(Chef::Exceptions::ContentLengthMismatch)
- end
- end
-
- %w{ HTTPFound HTTPMovedPermanently HTTPSeeOther HTTPUseProxy HTTPTemporaryRedirect HTTPMultipleChoice }.each do |resp_name|
- describe "when encountering a #{resp_name} redirect" do
- let(:http_response) do
- resp_cls = Net.const_get(resp_name)
- resp_code = Net::HTTPResponse::CODE_TO_OBJ.keys.detect { |k| Net::HTTPResponse::CODE_TO_OBJ[k] == resp_cls }
- http_response = Net::HTTPFound.new("1.1", resp_code, "bob is somewhere else again")
- http_response.add_field("location", url.path)
- allow(http_response).to receive(:read_body)
- http_response
- end
- it "should call request again" do
-
- expect { rest.request(:GET, url) }.to raise_error(Chef::Exceptions::RedirectLimitExceeded)
-
- [:PUT, :POST, :DELETE].each do |method|
- expect { rest.request(method, url) }.to raise_error(Chef::Exceptions::InvalidRedirect)
- end
- end
- end
- end
-
- context "when the response is 304 NotModified" do
- let (:http_response) do
- http_response = Net::HTTPNotModified.new("1.1", "304", "it's the same as when you asked 5 minutes ago")
- allow(http_response).to receive(:read_body)
- http_response
- end
-
- it "should return `false`" do
- expect(rest.request(:GET, url)).to be_falsey
- end
- end
-
- describe "when the request fails" do
- before do
- @original_log_level = Chef::Log.level
- Chef::Log.level = :info
- end
-
- after do
- Chef::Log.level = @original_log_level
- end
-
- context "on an unsuccessful response with a JSON error" do
- let(:http_response) do
- http_response = Net::HTTPServerError.new("1.1", "500", "drooling from inside of mouth")
- http_response.add_field("content-type", "application/json")
- allow(http_response).to receive(:body).and_return('{ "error":[ "Ears get sore!", "Not even four" ] }')
- allow(http_response).to receive(:read_body)
- http_response
- end
-
- it "should show the JSON error message" do
- allow(rest).to receive(:sleep)
-
- expect { rest.request(:GET, url) }.to raise_error(Net::HTTPFatalError)
- expect(log_stringio.string).to match(Regexp.escape("INFO: HTTP Request Returned 500 drooling from inside of mouth: Ears get sore!, Not even four"))
- end
- end
-
- context "on an unsuccessful response with a JSON error that is compressed" do
- let(:http_response) do
- http_response = Net::HTTPServerError.new("1.1", "500", "drooling from inside of mouth")
- http_response.add_field("content-type", "application/json")
- http_response.add_field("content-encoding", "deflate")
- unzipped_body = '{ "error":[ "Ears get sore!", "Not even four" ] }'
- gzipped_body = Zlib::Deflate.deflate(unzipped_body)
- gzipped_body.force_encoding(Encoding::BINARY) if "strings".respond_to?(:force_encoding)
-
- allow(http_response).to receive(:body).and_return gzipped_body
- allow(http_response).to receive(:read_body)
- http_response
- end
-
- before do
- allow(rest).to receive(:sleep)
- allow(rest).to receive(:http_retry_count).and_return(0)
- end
-
- it "decompresses the JSON error message" do
- expect { rest.request(:GET, url) }.to raise_error(Net::HTTPFatalError)
- expect(log_stringio.string).to match(Regexp.escape("INFO: HTTP Request Returned 500 drooling from inside of mouth: Ears get sore!, Not even four"))
- end
-
- it "fails when the compressed body is truncated" do
- http_response["Content-Length"] = (body.bytesize + 99).to_s
- expect { rest.request(:GET, url) }.to raise_error(Chef::Exceptions::ContentLengthMismatch)
- end
- end
-
- context "on a generic unsuccessful request" do
- let(:http_response) do
- http_response = Net::HTTPServerError.new("1.1", "500", "drooling from inside of mouth")
- allow(http_response).to receive(:body)
- allow(http_response).to receive(:read_body)
- http_response
- end
-
- it "retries then throws an exception" do
- allow(rest).to receive(:sleep)
- expect { rest.request(:GET, url) }.to raise_error(Net::HTTPFatalError)
- count = Chef::Config[:http_retry_count]
- expect(log_stringio.string).to match(Regexp.escape("ERROR: Server returned error 500 for #{url}, retrying #{count}/#{count}"))
- end
- end
- end
- end # as JSON API requests
-
- context "when streaming downloads to a tempfile" do
- let!(:tempfile) { Tempfile.open("chef-rspec-rest_spec-line-@{__LINE__}--") }
-
- let(:request_mock) { {} }
-
- let(:http_response) do
- http_response = Net::HTTPSuccess.new("1.1", "200", "it-works")
-
- allow(http_response).to receive(:read_body)
- expect(http_response).not_to receive(:body)
- http_response["Content-Length"] = "0" # call set_content_length (in test), if otherwise
- http_response
- end
-
- def set_content_length
- content_length = 0
- http_response.read_body do |chunk|
- content_length += chunk.bytesize
- end
- http_response["Content-Length"] = content_length.to_s
- end
-
- before do
- allow(Tempfile).to receive(:new).with("chef-rest").and_return(tempfile)
- allow(Net::HTTP::Get).to receive(:new).and_return(request_mock)
- end
-
- after do
- tempfile.close!
- end
-
- it " build a new HTTP GET request without the application/json accept header" do
- expected_headers = { "Accept" => "*/*",
- "X-Chef-Version" => Chef::VERSION,
- "Accept-Encoding" => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE,
- "Host" => host_header,
- "X-REMOTE-REQUEST-ID" => request_id,
- "X-Ops-Server-API-Version" => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION,
- }
- expect(Net::HTTP::Get).to receive(:new).with("/?foo=bar", expected_headers).and_return(request_mock)
- rest.streaming_request(url, {})
- end
-
- it "build a new HTTP GET request with the X-Remote-Request-Id header" do
- expected_headers = { "Accept" => "*/*",
- "X-Chef-Version" => Chef::VERSION,
- "Accept-Encoding" => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE,
- "Host" => host_header,
- "X-REMOTE-REQUEST-ID" => request_id,
- "X-Ops-Server-API-Version" => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION,
- }
- expect(Net::HTTP::Get).to receive(:new).with("/?foo=bar", expected_headers).and_return(request_mock)
- rest.streaming_request(url, {})
- end
-
- it "returns a tempfile containing the streamed response body" do
- expect(rest.streaming_request(url, {})).to equal(tempfile)
- end
-
- it "writes the response body to a tempfile" do
- allow(http_response).to receive(:read_body).and_yield("real").and_yield("ultimate").and_yield("power")
- set_content_length
- rest.streaming_request(url, {})
- expect(IO.read(tempfile.path).chomp).to eq("realultimatepower")
- end
-
- it "closes the tempfile" do
- rest.streaming_request(url, {})
- expect(tempfile).to be_closed
- end
-
- it "yields the tempfile containing the streamed response body and then unlinks it when given a block" do
- allow(http_response).to receive(:read_body).and_yield("real").and_yield("ultimate").and_yield("power")
- set_content_length
- tempfile_path = nil
- rest.streaming_request(url, {}) do |tempfile|
- tempfile_path = tempfile.path
- expect(File.exist?(tempfile.path)).to be_truthy
- expect(IO.read(tempfile.path).chomp).to eq("realultimatepower")
- end
- expect(File.exist?(tempfile_path)).to be_falsey
- end
-
- it "does not raise a divide by zero exception if the content's actual size is 0" do
- http_response["Content-Length"] = "5"
- allow(http_response).to receive(:read_body).and_yield("")
- expect { rest.streaming_request(url, {}) }.to raise_error(Chef::Exceptions::ContentLengthMismatch)
- end
-
- it "does not raise a divide by zero exception when the Content-Length is 0" do
- http_response["Content-Length"] = "0"
- allow(http_response).to receive(:read_body).and_yield("ninja")
- expect { rest.streaming_request(url, {}) }.to raise_error(Chef::Exceptions::ContentLengthMismatch)
- end
-
- it "it raises an exception when the download is truncated" do
- http_response["Content-Length"] = (body.bytesize + 99).to_s
- allow(http_response).to receive(:read_body).and_yield("ninja")
- expect { rest.streaming_request(url, {}) }.to raise_error(Chef::Exceptions::ContentLengthMismatch)
- end
-
- it "fetches a file and yields the tempfile it is streamed to" do
- allow(http_response).to receive(:read_body).and_yield("real").and_yield("ultimate").and_yield("power")
- set_content_length
- tempfile_path = nil
- rest.fetch("cookbooks/a_cookbook") do |tempfile|
- tempfile_path = tempfile.path
- expect(IO.read(tempfile.path).chomp).to eq("realultimatepower")
- end
- expect(File.exist?(tempfile_path)).to be_falsey
- end
-
- it "closes and unlinks the tempfile if there is an error while streaming the content to the tempfile" do
- path = tempfile.path
- expect(path).not_to be_nil
- allow(tempfile).to receive(:write).and_raise(IOError)
- rest.fetch("cookbooks/a_cookbook") { |tmpfile| "shouldn't get here" }
- expect(File.exists?(path)).to be_falsey
- end
-
- it "closes and unlinks the tempfile when the response is a redirect" do
- tempfile = double("A tempfile", :path => "/tmp/ragefist", :close => true, :binmode => true)
- expect(tempfile).to receive(:close!).at_least(1).times
- allow(Tempfile).to receive(:new).with("chef-rest").and_return(tempfile)
-
- redirect = Net::HTTPFound.new("1.1", "302", "bob is taking care of that one for me today")
- redirect.add_field("location", url.path)
- allow(redirect).to receive(:read_body)
-
- expect(http_client).to receive(:request).and_yield(redirect).and_return(redirect)
- expect(http_client).to receive(:request).and_yield(http_response).and_return(http_response)
- rest.fetch("cookbooks/a_cookbook") { |tmpfile| "shouldn't get here" }
- end
-
- it "passes the original block to the redirected request" do
- http_redirect = Net::HTTPFound.new("1.1", "302", "bob is taking care of that one for me today")
- http_redirect.add_field("location", "/that-thing-is-here-now")
- allow(http_redirect).to receive(:read_body)
-
- block_called = false
- allow(http_client).to receive(:request).and_yield(http_response).and_return(http_redirect, http_response)
- rest.fetch("cookbooks/a_cookbook") do |tmpfile|
- block_called = true
- end
- expect(block_called).to be_truthy
- end
- end
- end # when making REST requests
-
- context "when following redirects" do
- let(:rest) do
- Chef::REST.new(base_url)
- end
-
- before do
- Chef::Config[:node_name] = "webmonkey.example.com"
- Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem"
- end
-
- it "raises a RedirectLimitExceeded when redirected more than 10 times" do
- redirected = lambda { rest.follow_redirect { redirected.call } }
- expect { redirected.call }.to raise_error(Chef::Exceptions::RedirectLimitExceeded)
- end
-
- it "does not count redirects from previous calls against the redirect limit" do
- total_redirects = 0
- redirected = lambda do
- rest.follow_redirect do
- total_redirects += 1
- redirected.call unless total_redirects >= 9
- end
- end
- expect { redirected.call }.not_to raise_error
- total_redirects = 0
- expect { redirected.call }.not_to raise_error
- end
-
- it "does not sign the redirected request when sign_on_redirect is false" do
- rest.sign_on_redirect = false
- rest.follow_redirect { expect(rest.sign_requests?).to be_falsey }
- end
-
- it "resets sign_requests to the original value after following an unsigned redirect" do
- rest.sign_on_redirect = false
- expect(rest.sign_requests?).to be_truthy
-
- rest.follow_redirect { expect(rest.sign_requests?).to be_falsey }
- expect(rest.sign_requests?).to be_truthy
- end
-
- it "configures the redirect limit" do
- total_redirects = 0
- redirected = lambda do
- rest.follow_redirect do
- total_redirects += 1
- redirected.call unless total_redirects >= 9
- end
- end
- expect { redirected.call }.not_to raise_error
-
- total_redirects = 0
- rest.redirect_limit = 3
- expect { redirected.call }.to raise_error(Chef::Exceptions::RedirectLimitExceeded)
- end
-
- end
-end
diff --git a/spec/unit/run_context/child_run_context_spec.rb b/spec/unit/run_context/child_run_context_spec.rb
index 13a035c871..47a6c84f7a 100644
--- a/spec/unit/run_context/child_run_context_spec.rb
+++ b/spec/unit/run_context/child_run_context_spec.rb
@@ -24,16 +24,16 @@ require "support/lib/library_load_order"
describe Chef::RunContext::ChildRunContext do
context "with a run context with stuff in it" do
let(:chef_repo_path) { File.expand_path(File.join(CHEF_SPEC_DATA, "run_context", "cookbooks")) }
- let(:cookbook_collection) {
+ let(:cookbook_collection) do
cl = Chef::CookbookLoader.new(chef_repo_path)
cl.load_cookbooks
Chef::CookbookCollection.new(cl)
- }
- let(:node) {
+ end
+ let(:node) do
node = Chef::Node.new
node.run_list << "test" << "test::one" << "test::two"
node
- }
+ end
let(:events) { Chef::EventDispatch::Dispatcher.new }
let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) }
diff --git a/spec/unit/run_context/cookbook_compiler_spec.rb b/spec/unit/run_context/cookbook_compiler_spec.rb
index 868bed4bfd..e93088cd5f 100644
--- a/spec/unit/run_context/cookbook_compiler_spec.rb
+++ b/spec/unit/run_context/cookbook_compiler_spec.rb
@@ -158,7 +158,47 @@ describe Chef::RunContext::CookbookCompiler do
end
describe "loading recipes" do
- # Tests for this behavior are in RunContext's tests
+ # Additional tests for this behavior are in RunContext's tests
+
+ describe "event dispatch" do
+ let(:recipe) { "dependency1::default" }
+ let(:recipe_path) do
+ File.expand_path("../../../data/run_context/cookbooks/dependency1/recipes/default.rb", __FILE__)
+ end
+ before do
+ node.run_list(recipe)
+ end
+ subject { compiler.compile_recipes }
+
+ it "dispatches normally" do
+ allow(run_context).to receive(:load_recipe)
+ expect(events).to receive(:recipe_load_start).with(1)
+ expect(events).to receive(:recipe_file_loaded).with(recipe_path, "dependency1::default")
+ expect(events).to receive(:recipe_load_complete).with(no_args)
+ subject
+ end
+
+ it "dispatches when a recipe is not found" do
+ exc = Chef::Exceptions::RecipeNotFound.new
+ allow(run_context).to receive(:load_recipe).and_raise(exc)
+ expect(events).to receive(:recipe_load_start).with(1)
+ expect(events).to_not receive(:recipe_file_loaded)
+ expect(events).to receive(:recipe_not_found).with(exc)
+ expect(events).to_not receive(:recipe_load_complete)
+ expect { subject }.to raise_error(exc)
+ end
+
+ it "dispatches when a recipe has an error" do
+ exc = ArgumentError.new
+ allow(run_context).to receive(:load_recipe).and_raise(exc)
+ expect(events).to receive(:recipe_load_start).with(1)
+ expect(events).to_not receive(:recipe_file_loaded)
+ expect(events).to receive(:recipe_file_load_failed).with(recipe_path, exc, "dependency1::default")
+ expect(events).to_not receive(:recipe_load_complete)
+ expect { subject }.to raise_error(exc)
+ end
+ end
+
end
describe "listing cookbook order" do
diff --git a/spec/unit/run_context_spec.rb b/spec/unit/run_context_spec.rb
index 7b2a27e9f6..f1c3072b8e 100644
--- a/spec/unit/run_context_spec.rb
+++ b/spec/unit/run_context_spec.rb
@@ -23,16 +23,16 @@ require "support/lib/library_load_order"
describe Chef::RunContext do
let(:chef_repo_path) { File.expand_path(File.join(CHEF_SPEC_DATA, "run_context", "cookbooks")) }
- let(:cookbook_collection) {
+ let(:cookbook_collection) do
cl = Chef::CookbookLoader.new(chef_repo_path)
cl.load_cookbooks
Chef::CookbookCollection.new(cl)
- }
- let(:node) {
+ end
+ let(:node) do
node = Chef::Node.new
node.run_list << "test" << "test::one" << "test::two"
node
- }
+ end
let(:events) { Chef::EventDispatch::Dispatcher.new }
let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) }
@@ -151,13 +151,13 @@ describe Chef::RunContext do
describe "querying the contents of cookbooks" do
let(:chef_repo_path) { File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) }
- let(:node) {
+ let(:node) do
node = Chef::Node.new
- node.set[:platform] = "ubuntu"
- node.set[:platform_version] = "13.04"
+ node.normal[:platform] = "ubuntu"
+ node.normal[:platform_version] = "13.04"
node.name("testing")
node
- }
+ end
it "queries whether a given cookbook has a specific template" do
expect(run_context).to have_template_in_cookbook("openldap", "test.erb")
@@ -212,11 +212,11 @@ describe Chef::RunContext do
shared_context "notifying resource is a subclass of Chef::Resource" do
let(:declared_type) { :alpaca }
- let(:notifying_resource) {
+ let(:notifying_resource) do
r = Class.new(Chef::Resource).new("guinea pig")
r.declared_type = declared_type
r
- }
+ end
it "should be keyed off the resource declared key" do
run_context.send(setter, notification)
diff --git a/spec/unit/run_list/run_list_expansion_spec.rb b/spec/unit/run_list/run_list_expansion_spec.rb
index e1af112dbf..3a39bc79cc 100644
--- a/spec/unit/run_list/run_list_expansion_spec.rb
+++ b/spec/unit/run_list/run_list_expansion_spec.rb
@@ -104,8 +104,8 @@ describe Chef::RunList::RunListExpansion do
end
it "produces json tree upon tracing expansion" do
- jsonRunList = @expansion.to_json
- expect(jsonRunList).to eq(@json)
+ json_run_list = @expansion.to_json
+ expect(json_run_list).to eq(@json)
end
it "has the ordered list of recipes" do
diff --git a/spec/unit/run_list/versioned_recipe_list_spec.rb b/spec/unit/run_list/versioned_recipe_list_spec.rb
index 91c601b294..859fee75ec 100644
--- a/spec/unit/run_list/versioned_recipe_list_spec.rb
+++ b/spec/unit/run_list/versioned_recipe_list_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Delano (<stephen@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright 2010-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -186,9 +186,12 @@ describe Chef::RunList::VersionedRecipeList do
end
end
- context "with duplicated names", chef: ">= 13" do
- it "should fail in Chef 13" do
- expect(list).to_not respond_to(:with_duplicate_names)
+ context "with duplicate names" do
+ let(:fq_names) { list.with_duplicate_names }
+ let(:recipes) { %w{ foo bar::default } }
+
+ it "expands default recipes" do
+ expect(fq_names).to eq(%w{foo foo::default bar bar::default})
end
end
end
diff --git a/spec/unit/run_status_spec.rb b/spec/unit/run_status_spec.rb
index 6305b7497b..60717fb3a8 100644
--- a/spec/unit/run_status_spec.rb
+++ b/spec/unit/run_status_spec.rb
@@ -100,7 +100,7 @@ describe Chef::RunStatus do
describe "and some have been updated" do
before do
- @all_resources.first.updated = true
+ @all_resources.first.updated_by_last_action true
end
it "lists the updated resources" do
diff --git a/spec/unit/runner_spec.rb b/spec/unit/runner_spec.rb
index f15f3951e0..db52862a9c 100644
--- a/spec/unit/runner_spec.rb
+++ b/spec/unit/runner_spec.rb
@@ -1,6 +1,6 @@
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright 2008-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -98,22 +98,8 @@ describe Chef::Runner do
run_context.resource_collection << first_resource
end
- context "when we fall through to old Chef::Platform resolution" do
- let(:provider_resolver) { Chef::ProviderResolver.new(node, first_resource, nil) }
- before do
- # set up old Chef::Platform resolution instead of provider_resolver
- Chef::Platform.set(
- :resource => :cat,
- :provider => Chef::Provider::SnakeOil
- )
- allow(Chef::ProviderResolver).to receive(:new).and_return(provider_resolver)
- allow(provider_resolver).to receive(:maybe_dynamic_provider_resolution).with(first_resource, anything()).and_return(nil)
- end
-
- it "should use the platform provider if it has one" do
- expect(Chef::Platform).to receive(:find_provider_for_node).with(node, first_resource).and_call_original
- runner.converge
- end
+ it "runner sets up a pointer back to itself in the run_context" do
+ expect(runner).to eql(run_context.runner)
end
context "when we are doing dynamic provider resolution" do
@@ -140,25 +126,26 @@ describe Chef::Runner do
it "should raise exceptions as thrown by a provider" do
provider = Chef::Provider::SnakeOil.new(run_context.resource_collection[0], run_context)
- allow(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider)
- allow(provider).to receive(:action_sell).once.and_raise(ArgumentError)
+ expect(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider)
+ expect(provider).to receive(:action_sell).once.and_raise(ArgumentError)
expect { runner.converge }.to raise_error(ArgumentError)
end
it "should not raise exceptions thrown by providers if the resource has ignore_failure set to true" do
allow(run_context.resource_collection[0]).to receive(:ignore_failure).and_return(true)
provider = Chef::Provider::SnakeOil.new(run_context.resource_collection[0], run_context)
- allow(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider)
- allow(provider).to receive(:action_sell).once.and_raise(ArgumentError)
+ expect(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider)
+ expect(provider).to receive(:action_sell).once.and_raise(ArgumentError)
expect { runner.converge }.not_to raise_error
end
it "should retry with the specified delay if retries are specified" do
- first_resource.retries 3
+ num_retries = 3
+ allow(run_context.resource_collection[0]).to receive(:retries).and_return(num_retries)
provider = Chef::Provider::SnakeOil.new(run_context.resource_collection[0], run_context)
- allow(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider)
- allow(provider).to receive(:action_sell).and_raise(ArgumentError)
- expect(first_resource).to receive(:sleep).with(2).exactly(3).times
+ expect(Chef::Provider::SnakeOil).to receive(:new).exactly(num_retries + 1).times.and_return(provider)
+ expect(provider).to receive(:action_sell).exactly(num_retries + 1).times.and_raise(ArgumentError)
+ expect(run_context.resource_collection[0]).to receive(:sleep).with(2).exactly(num_retries).times
expect { runner.converge }.to raise_error(ArgumentError)
end
@@ -392,12 +379,12 @@ Multiple failures occurred:
it "should resolve resource references in notifications when resources are defined lazily" do
first_resource.action = :nothing
- lazy_resources = lambda {
+ lazy_resources = lambda do
last_resource = Chef::Resource::Cat.new("peanut", run_context)
run_context.resource_collection << last_resource
last_resource.notifies(:purr, first_resource.to_s, :delayed)
last_resource.action = :purr
- }
+ end
second_resource = Chef::Resource::RubyBlock.new("myblock", run_context)
run_context.resource_collection << second_resource
second_resource.block { lazy_resources.call }
diff --git a/spec/unit/search/query_spec.rb b/spec/unit/search/query_spec.rb
index 7f815f1757..151fcb51b3 100644
--- a/spec/unit/search/query_spec.rb
+++ b/spec/unit/search/query_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright 2009-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,18 +22,19 @@ require "chef/search/query"
describe Chef::Search::Query do
let(:rest) { double("Chef::ServerAPI") }
let(:query) { Chef::Search::Query.new }
+ let(:default_rows) { 1000 }
shared_context "filtered search" do
- let(:query_string) { "search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=0" }
+ let(:query_string) { "search/node?q=platform:rhel&start=0&rows=#{default_rows}" }
let(:server_url) { "https://api.opscode.com/organizations/opscode/nodes" }
let(:args) { { filter_key => filter_hash } }
- let(:filter_hash) {
+ let(:filter_hash) do
{
"env" => [ "chef_environment" ],
"ruby_plat" => %w{languages ruby platform},
}
- }
- let(:response) {
+ end
+ let(:response) do
{
"rows" => [
{ "url" => "#{server_url}/my-name-is-node",
@@ -64,15 +65,15 @@ describe Chef::Search::Query do
"start" => 0,
"total" => 4,
}
- }
- let(:response_rows) {
+ end
+ let(:response_rows) do
[
{ "env" => "elysium", "ruby_plat" => "nudibranch" },
{ "env" => "hades", "ruby_plat" => "i386-mingw32" },
{ "env" => "elysium", "ruby_plat" => "centos" },
{ "env" => "moon", "ruby_plat" => "solaris2" },
]
- }
+ end
end
before(:each) do
@@ -81,12 +82,13 @@ describe Chef::Search::Query do
end
describe "search" do
- let(:query_string) { "search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=0" }
- let(:query_string_continue) { "search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=4" }
- let(:query_string_with_rows) { "search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=0&rows=4" }
- let(:query_string_continue_with_rows) { "search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=4&rows=4" }
+ let(:query_string) { "search/node?q=platform:rhel&start=0&rows=#{default_rows}" }
+ let(:query_string_continue) { "search/node?q=platform:rhel&start=4&rows=#{default_rows}" }
+ let(:query_string_with_rows) { "search/node?q=platform:rhel&start=0&rows=4" }
+ let(:query_string_continue_with_rows) { "search/node?q=platform:rhel&start=4&rows=4" }
- let(:response) { {
+ let(:response) do
+ {
"rows" => [
{ "name" => "my-name-is-node",
"chef_environment" => "elysium",
@@ -147,28 +149,22 @@ describe Chef::Search::Query do
],
"start" => 0,
"total" => 4,
- } }
-
- let(:big_response) {
- r = response.dup
- r["total"] = 8
- r
- }
+ } end
- let(:big_response_empty) {
+ let(:big_response_empty) do
{
"start" => 0,
"total" => 8,
"rows" => [],
}
- }
+ end
- let(:big_response_end) {
+ let(:big_response_end) do
r = response.dup
r["start"] = 4
r["total"] = 8
r
- }
+ end
it "accepts a type as the first argument" do
expect { query.search("node") }.not_to raise_error
@@ -177,27 +173,22 @@ describe Chef::Search::Query do
end
it "queries for every object of a type by default" do
- expect(rest).to receive(:get).with("search/node?q=*:*&sort=X_CHEF_id_CHEF_X%20asc&start=0").and_return(response)
+ expect(rest).to receive(:get).with("search/node?q=*:*&start=0&rows=#{default_rows}").and_return(response)
query.search(:node)
end
it "allows a custom query" do
- expect(rest).to receive(:get).with("search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=0").and_return(response)
+ expect(rest).to receive(:get).with("search/node?q=platform:rhel&start=0&rows=#{default_rows}").and_return(response)
query.search(:node, "platform:rhel")
end
- it "lets you set a sort order" do
- expect(rest).to receive(:get).with("search/node?q=platform:rhel&sort=id%20desc&start=0").and_return(response)
- query.search(:node, "platform:rhel", sort: "id desc")
- end
-
it "lets you set a starting object" do
- expect(rest).to receive(:get).with("search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=2").and_return(response)
+ expect(rest).to receive(:get).with("search/node?q=platform:rhel&start=2&rows=#{default_rows}").and_return(response)
query.search(:node, "platform:rhel", start: 2)
end
it "lets you set how many rows to return" do
- expect(rest).to receive(:get).with("search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=0&rows=40").and_return(response)
+ expect(rest).to receive(:get).with("search/node?q=platform:rhel&start=0&rows=40").and_return(response)
query.search(:node, "platform:rhel", rows: 40)
end
@@ -222,12 +213,12 @@ describe Chef::Search::Query do
it "pages through the responses" do
@call_me = double("blocky")
response["rows"].each { |r| expect(@call_me).to receive(:do).with(Chef::Node.from_hash(r)) }
- query.search(:node, "*:*", sort: nil, start: 0, rows: 4) { |r| @call_me.do(r) }
+ query.search(:node, "*:*", start: 0, rows: 4) { |r| @call_me.do(r) }
end
- it "sends multiple API requests when the server indicates there is more data" do
- expect(rest).to receive(:get).with(query_string).and_return(big_response)
- expect(rest).to receive(:get).with(query_string_continue).and_return(big_response_end)
+ # This test would loop infinitely if pagination didn't advance
+ it "paginates correctly in the face of filtered nodes without explicit rows" do
+ allow(rest).to receive(:get).with(query_string).and_return(big_response_empty)
query.search(:node, "platform:rhel") do |r|
nil
end
@@ -241,6 +232,27 @@ describe Chef::Search::Query do
end
end
+ it "fuzzifies node searches when fuzz is set" do
+ expect(rest).to receive(:get).with(
+ "search/node?q=tags:*free.messi*%20OR%20roles:*free.messi*%20OR%20fqdn:*free.messi*%20OR%20addresses:*free.messi*%20OR%20policy_name:*free.messi*%20OR%20policy_group:*free.messi*&start=0&rows=#{default_rows}"
+ ).and_return(response)
+ query.search(:node, "free.messi", fuzz: true)
+ end
+
+ it "does not fuzzify node searches when fuzz is not set" do
+ expect(rest).to receive(:get).with(
+ "search/node?q=free.messi&start=0&rows=#{default_rows}"
+ ).and_return(response)
+ query.search(:node, "free.messi")
+ end
+
+ it "does not fuzzify client searches" do
+ expect(rest).to receive(:get).with(
+ "search/client?q=messi&start=0&rows=#{default_rows}"
+ ).and_return(response)
+ query.search(:client, "messi", fuzz: true)
+ end
+
context "when :filter_result is provided as a result" do
include_context "filtered search" do
let(:filter_key) { :filter_result }
@@ -268,22 +280,4 @@ describe Chef::Search::Query do
end
end
- describe "#partial_search" do
- include_context "filtered search" do
- let(:filter_key) { :keys }
-
- it "emits a deprecation warning" do
- # partial_search calls search, so we'll stub search to return empty
- allow(query).to receive(:search).and_return( [ [], 0, 0 ] )
- expect(Chef::Log).to receive(:warn).with(/DEPRECATED: The 'partial_search' API is deprecated/)
- query.partial_search(:node, "platform:rhel", args)
- end
-
- it "returns an array of filtered hashes" do
- expect(rest).to receive(:post).with(query_string, args[filter_key]).and_return(response)
- results = query.partial_search(:node, "platform:rhel", args)
- expect(results[0]).to match_array(response_rows)
- end
- end
- end
end
diff --git a/spec/unit/server_api_spec.rb b/spec/unit/server_api_spec.rb
new file mode 100644
index 0000000000..3f1d9b0e90
--- /dev/null
+++ b/spec/unit/server_api_spec.rb
@@ -0,0 +1,124 @@
+require "spec_helper"
+
+SIGNING_KEY_DOT_PEM = "-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA49TA0y81ps0zxkOpmf5V4/c4IeR5yVyQFpX3JpxO4TquwnRh
+8VSUhrw8kkTLmB3cS39Db+3HadvhoqCEbqPE6915kXSuk/cWIcNozujLK7tkuPEy
+YVsyTioQAddSdfe+8EhQVf3oHxaKmUd6waXrWqYCnhxgOjxocenREYNhZ/OETIei
+PbOku47vB4nJK/0GhKBytL2XnsRgfKgDxf42BqAi1jglIdeq8lAWZNF9TbNBU21A
+O1iuT7Pm6LyQujhggPznR5FJhXKRUARXBJZawxpGV4dGtdcahwXNE4601aXPra+x
+PcRd2puCNoEDBzgVuTSsLYeKBDMSfs173W1QYwIDAQABAoIBAGF05q7vqOGbMaSD
+2Q7YbuE/JTHKTBZIlBI1QC2x+0P5GDxyEFttNMOVzcs7xmNhkpRw8eX1LrInrpMk
+WsIBKAFFEfWYlf0RWtRChJjNl+szE9jQxB5FJnWtJH/FHa78tR6PsF24aQyzVcJP
+g0FGujBihwgfV0JSCNOBkz8MliQihjQA2i8PGGmo4R4RVzGfxYKTIq9vvRq/+QEa
+Q4lpVLoBqnENpnY/9PTl6JMMjW2b0spbLjOPVwDaIzXJ0dChjNXo15K5SHI5mALJ
+I5gN7ODGb8PKUf4619ez194FXq+eob5YJdilTFKensIUvt3YhP1ilGMM+Chi5Vi/
+/RCTw3ECgYEA9jTw4wv9pCswZ9wbzTaBj9yZS3YXspGg26y6Ohq3ZmvHz4jlT6uR
+xK+DDcUiK4072gci8S4Np0fIVS7q6ivqcOdzXPrTF5/j+MufS32UrBbUTPiM1yoO
+ECcy+1szl/KoLEV09bghPbvC58PFSXV71evkaTETYnA/F6RK12lEepcCgYEA7OSy
+bsMrGDVU/MKJtwqyGP9ubA53BorM4Pp9VVVSCrGGVhb9G/XNsjO5wJC8J30QAo4A
+s59ZzCpyNRy046AB8jwRQuSwEQbejSdeNgQGXhZ7aIVUtuDeFFdaIz/zjVgxsfj4
+DPOuzieMmJ2MLR4F71ocboxNoDI7xruPSE8dDhUCgYA3vx732cQxgtHwAkeNPJUz
+dLiE/JU7CnxIoSB9fYUfPLI+THnXgzp7NV5QJN2qzMzLfigsQcg3oyo6F2h7Yzwv
+GkjlualIRRzCPaCw4Btkp7qkPvbs1QngIHALt8fD1N69P3DPHkTwjG4COjKWgnJq
+qoHKS6Fe/ZlbigikI6KsuwKBgQCTlSLoyGRHr6oj0hqz01EDK9ciMJzMkZp0Kvn8
+OKxlBxYW+jlzut4MQBdgNYtS2qInxUoAnaz2+hauqhSzntK3k955GznpUatCqx0R
+b857vWviwPX2/P6+E3GPdl8IVsKXCvGWOBZWTuNTjQtwbDzsUepWoMgXnlQJSn5I
+YSlLxQKBgQD16Gw9kajpKlzsPa6XoQeGmZALT6aKWJQlrKtUQIrsIWM0Z6eFtX12
+2jjHZ0awuCQ4ldqwl8IfRogWMBkHOXjTPVK0YKWWlxMpD/5+bGPARa5fir8O1Zpo
+Y6S6MeZ69Rp89ma4ttMZ+kwi1+XyHqC/dlcVRW42Zl5Dc7BALRlJjQ==
+-----END RSA PRIVATE KEY-----".freeze
+
+describe Chef::ServerAPI do
+ let(:url) { "http://chef.example.com:4000" }
+ let(:key_path) { "/tmp/foo" }
+
+ let(:client) do
+ Chef::ServerAPI.new(url)
+ end
+
+ before do
+ Chef::Config[:node_name] = "silent-bob"
+ Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem"
+ Chef::Config[:http_retry_delay] = 0
+ end
+
+ describe "#initialize" do
+ it "uses the configured key file" do
+ allow(IO).to receive(:read).with(key_path).and_return(SIGNING_KEY_DOT_PEM)
+ Chef::Config[:client_key] = key_path
+
+ api = described_class.new(url)
+ expect(api.options[:signing_key_filename]).to eql(key_path)
+ end
+
+ it "allows a user to set a raw_key" do
+ api = described_class.new(url, raw_key: SIGNING_KEY_DOT_PEM)
+ expect(api.options[:signing_key_filename]).to be_nil
+ expect(api.options[:raw_key]).to eql(SIGNING_KEY_DOT_PEM)
+ end
+ end
+
+ context "versioned apis" do
+ class VersionedClassV0
+ extend Chef::Mixin::VersionedAPI
+ minimum_api_version 0
+ end
+
+ class VersionedClassV2
+ extend Chef::Mixin::VersionedAPI
+ minimum_api_version 2
+ end
+
+ class VersionedClassVersions
+ extend Chef::Mixin::VersionedAPIFactory
+ add_versioned_api_class VersionedClassV0
+ add_versioned_api_class VersionedClassV2
+ end
+
+ before do
+ Chef::ServerAPIVersions.instance.reset!
+ end
+
+ let(:versioned_client) do
+ Chef::ServerAPI.new(url, version_class: VersionedClassVersions)
+ end
+
+ it "on protocol negotiation it posts the same message body without doubly-encoding the json string" do
+ WebMock.disable_net_connect!
+ post_body = { bar: "baz" }
+ body_406 = '{"error":"invalid-x-ops-server-api-version","message":"Specified version 2 not supported","min_version":0,"max_version":1}'
+ stub_request(:post, "http://chef.example.com:4000/foo").with(body: post_body.to_json, headers: { "X-Ops-Server-Api-Version" => "2" }).to_return(status: [406, "Not Acceptable"], body: body_406 )
+ stub_request(:post, "http://chef.example.com:4000/foo").with(body: post_body.to_json, headers: { "X-Ops-Server-Api-Version" => "0" }).to_return(status: 200, body: "", headers: {})
+ versioned_client.post("foo", post_body)
+ end
+ end
+
+ context "retrying normal requests" do
+ it "500 on a post retries and posts correctly " do
+ WebMock.disable_net_connect!
+ post_body = { bar: "baz" }
+ headers = { "Accept" => "application/json", "Content-Type" => "application/json", "Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "Content-Length" => "13", "Host" => "chef.example.com:4000", "X-Chef-Version" => Chef::VERSION, "X-Ops-Sign" => "algorithm=sha1;version=1.1;", "X-Ops-Userid" => "silent-bob" }
+ stub_request(:post, "http://chef.example.com:4000/foo").with(body: post_body.to_json, headers: headers).to_return(status: [500, "Internal Server Error"])
+ stub_request(:post, "http://chef.example.com:4000/foo").with(body: post_body.to_json, headers: headers).to_return(status: 200, body: "", headers: {})
+ client.post("foo", post_body)
+ end
+
+ it "500 on a put retries and puts correctly " do
+ WebMock.disable_net_connect!
+ put_body = { bar: "baz" }
+ headers = { "Accept" => "application/json", "Content-Type" => "application/json", "Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "Content-Length" => "13", "Host" => "chef.example.com:4000", "X-Chef-Version" => Chef::VERSION, "X-Ops-Sign" => "algorithm=sha1;version=1.1;", "X-Ops-Userid" => "silent-bob" }
+ stub_request(:put, "http://chef.example.com:4000/foo").with(body: put_body.to_json, headers: headers).to_return(status: [500, "Internal Server Error"])
+ stub_request(:put, "http://chef.example.com:4000/foo").with(body: put_body.to_json, headers: headers).to_return(status: 200, body: "", headers: {})
+ client.put("foo", put_body)
+ end
+
+ it "500 on a get retries and gets correctly " do
+ WebMock.disable_net_connect!
+ get_body = { bar: "baz" }
+ headers = { "Accept" => "application/json", "Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "Host" => "chef.example.com:4000", "X-Chef-Version" => Chef::VERSION, "X-Ops-Sign" => "algorithm=sha1;version=1.1;", "X-Ops-Userid" => "silent-bob" }
+ stub_request(:get, "http://chef.example.com:4000/foo").with(headers: headers).to_return(status: [500, "Internal Server Error"])
+ stub_request(:get, "http://chef.example.com:4000/foo").with(headers: headers).to_return(status: 200, body: "", headers: {})
+ client.get("foo")
+ end
+ end
+end
diff --git a/spec/unit/server_api_versions_spec.rb b/spec/unit/server_api_versions_spec.rb
new file mode 100644
index 0000000000..1dab0548cb
--- /dev/null
+++ b/spec/unit/server_api_versions_spec.rb
@@ -0,0 +1,66 @@
+#
+# Copyright:: Copyright 2017, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::ServerAPIVersions do
+ before do
+ Chef::ServerAPIVersions.instance.reset!
+ end
+
+ describe "#reset!" do
+ it "resets the version information" do
+ Chef::ServerAPIVersions.instance.set_versions({ "min_version" => 0, "max_version" => 2 })
+ Chef::ServerAPIVersions.instance.reset!
+ expect(Chef::ServerAPIVersions.instance.min_server_version).to be_nil
+ end
+
+ it "resets the unversioned flag" do
+ Chef::ServerAPIVersions.instance.unversioned!
+ Chef::ServerAPIVersions.instance.reset!
+ expect(Chef::ServerAPIVersions.instance.unversioned?).to be false
+ end
+ end
+
+ describe "#min_server_version" do
+ it "returns nil if no versions have been recorded" do
+ expect(Chef::ServerAPIVersions.instance.min_server_version).to be_nil
+ end
+ it "returns 0 if unversioned" do
+ Chef::ServerAPIVersions.instance.unversioned!
+ expect(Chef::ServerAPIVersions.instance.min_server_version).to eq(0)
+ end
+ it "returns the correct value" do
+ Chef::ServerAPIVersions.instance.set_versions({ "min_version" => 0, "max_version" => 2 })
+ expect(Chef::ServerAPIVersions.instance.min_server_version).to eq(0)
+ end
+ end
+
+ describe "#max_server_version" do
+ it "returns nil if no versions have been recorded" do
+ expect(Chef::ServerAPIVersions.instance.max_server_version).to be_nil
+ end
+ it "returns 0 if unversioned" do
+ Chef::ServerAPIVersions.instance.unversioned!
+ expect(Chef::ServerAPIVersions.instance.min_server_version).to eq(0)
+ end
+ it "returns the correct value" do
+ Chef::ServerAPIVersions.instance.set_versions({ "min_version" => 0, "max_version" => 2 })
+ expect(Chef::ServerAPIVersions.instance.max_server_version).to eq(2)
+ end
+ end
+end
diff --git a/spec/unit/shell/shell_session_spec.rb b/spec/unit/shell/shell_session_spec.rb
index 22f3dc6f0c..c96453eeae 100644
--- a/spec/unit/shell/shell_session_spec.rb
+++ b/spec/unit/shell/shell_session_spec.rb
@@ -47,92 +47,147 @@ describe Shell::ShellSession do
end
describe Shell::ClientSession do
+ let(:json_attribs) { { "a" => "b" } }
+ let(:chef_rest) { double("Chef::ServerAPI") }
+ let(:node) { Chef::Node.build("foo") }
+ let(:session) { Shell::ClientSession.instance }
+ let(:client) do
+ double("Chef::Client.new",
+ :run_ohai => true,
+ :load_node => true,
+ :build_node => true,
+ :register => true,
+ :sync_cookbooks => {}
+ )
+ end
+
before do
Chef::Config[:shell_config] = { :override_runlist => [Chef::RunList::RunListItem.new("shell::override")] }
- @chef_rest = double("Chef::ServerAPI")
- @session = Shell::ClientSession.instance
- @node = Chef::Node.build("foo")
- @session.node = @node
- @client = double("Chef::Client.new",
- :run_ohai => true,
- :load_node => true,
- :build_node => true,
- :register => true,
- :sync_cookbooks => {})
+ session.node = node
+ session.json_configuration = json_attribs
end
it "builds the node's run_context with the proper environment" do
- @session.instance_variable_set(:@client, @client)
- @expansion = Chef::RunList::RunListExpansion.new(@node.chef_environment, [])
+ session.instance_variable_set(:@client, client)
+ expansion = Chef::RunList::RunListExpansion.new(node.chef_environment, [])
- expect(@node.run_list).to receive(:expand).with(@node.chef_environment).and_return(@expansion)
- expect(Chef::ServerAPI).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(@chef_rest)
- @session.rebuild_context
+ expect(node.run_list).to receive(:expand).with(node.chef_environment).and_return(expansion)
+ expect(Chef::ServerAPI).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(chef_rest)
+ session.rebuild_context
end
it "passes the shell CLI args to the client" do
- expect(Chef::Client).to receive(:new).with(nil, Chef::Config[:shell_config]).and_return(@client)
- @session.send(:rebuild_node)
+ expect(Chef::Client).to receive(:new).with(json_attribs, Chef::Config[:shell_config]).and_return(client)
+ session.send(:rebuild_node)
+ end
+
+end
+
+describe Shell::SoloSession do
+ let(:json_attribs) { { "a" => "b" } }
+ let(:chef_rest) { double("Chef::ServerAPI") }
+ let(:node) { Chef::Node.build("foo") }
+ let(:session) { Shell::SoloSession.instance }
+ let(:client) do
+ double("Chef::Client.new",
+ :run_ohai => true,
+ :load_node => true,
+ :build_node => true,
+ :register => true,
+ :sync_cookbooks => {}
+ )
+ end
+
+ before do
+ Chef::Config[:shell_config] = { :override_runlist => [Chef::RunList::RunListItem.new("shell::override")] }
+ session.node = node
+ session.json_configuration = json_attribs
+ end
+
+ it "builds the node's run_context with the proper environment" do
+ session.instance_variable_set(:@client, client)
+ expansion = Chef::RunList::RunListExpansion.new(node.chef_environment, [])
+
+ expect(node.run_list).to receive(:expand).with(node.chef_environment).and_return(expansion)
+ expect(Chef::ServerAPI).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(chef_rest)
+ session.rebuild_context
+ end
+
+ it "passes the shell CLI args to the client" do
+ expect(Chef::Client).to receive(:new).with(json_attribs, Chef::Config[:shell_config]).and_return(client)
+ session.send(:rebuild_node)
end
end
describe Shell::StandAloneSession do
+ let(:json_attribs) { { "a" => "b" } }
+ let(:chef_rest) { double("Chef::ServerAPI") }
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:session) { Shell::StandAloneSession.instance }
+ let(:client) do
+ double("Chef::Client.new",
+ :run_ohai => true,
+ :load_node => true,
+ :build_node => true,
+ :register => true,
+ :sync_cookbooks => {}
+ )
+ end
+ let(:recipe) { Chef::Recipe.new(nil, nil, run_context) }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+
before do
Chef::Config[:shell_config] = { :override_runlist => [Chef::RunList::RunListItem.new("shell::override")] }
- @session = Shell::StandAloneSession.instance
- @node = @session.node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = @session.run_context = Chef::RunContext.new(@node, {}, @events)
- @recipe = @session.recipe = Chef::Recipe.new(nil, nil, @run_context)
- Shell::Extensions.extend_context_recipe(@recipe)
+ session.node = node
+ session.json_configuration = json_attribs
+ session.run_context = run_context
+ session.recipe = recipe
+ Shell::Extensions.extend_context_recipe(recipe)
end
it "has a run_context" do
- expect(@session.run_context).to equal(@run_context)
+ expect(session.run_context).to equal(run_context)
end
it "returns a collection based on it's standalone recipe file" do
- expect(@session.resource_collection).to eq(@recipe.run_context.resource_collection)
+ expect(session.resource_collection).to eq(recipe.run_context.resource_collection)
end
it "gives nil for the definitions (for now)" do
- expect(@session.definitions).to be_nil
+ expect(session.definitions).to be_nil
end
it "gives nil for the cookbook_loader" do
- expect(@session.cookbook_loader).to be_nil
+ expect(session.cookbook_loader).to be_nil
end
it "runs chef with the standalone recipe" do
- allow(@session).to receive(:node_built?).and_return(true)
+ allow(session).to receive(:node_built?).and_return(true)
allow(Chef::Log).to receive(:level)
chef_runner = double("Chef::Runner.new", :converge => :converged)
# pre-heat resource collection cache
- @session.resource_collection
+ session.resource_collection
- expect(Chef::Runner).to receive(:new).with(@session.recipe.run_context).and_return(chef_runner)
- expect(@recipe.run_chef).to eq(:converged)
+ expect(Chef::Runner).to receive(:new).with(session.recipe.run_context).and_return(chef_runner)
+ expect(recipe.run_chef).to eq(:converged)
end
it "passes the shell CLI args to the client" do
- @client = double("Chef::Client.new",
- :run_ohai => true,
- :load_node => true,
- :build_node => true,
- :register => true,
- :sync_cookbooks => {})
- expect(Chef::Client).to receive(:new).with(nil, Chef::Config[:shell_config]).and_return(@client)
- @session.send(:rebuild_node)
+ expect(Chef::Client).to receive(:new).with(json_attribs, Chef::Config[:shell_config]).and_return(client)
+ session.send(:rebuild_node)
end
end
-describe Shell::SoloSession do
+describe Shell::SoloLegacySession do
+ let(:json_attribs) { { "a" => "b" } }
+
before do
Chef::Config[:shell_config] = { :override_runlist => [Chef::RunList::RunListItem.new("shell::override")] }
Chef::Config[:shell_solo] = true
- @session = Shell::SoloSession.instance
+ @session = Shell::SoloLegacySession.instance
@node = Chef::Node.new
@events = Chef::EventDispatch::Dispatcher.new
@run_context = @session.run_context = Chef::RunContext.new(@node, {}, @events)
@@ -158,7 +213,7 @@ describe Shell::SoloSession do
it "keeps json attribs and passes them to the node for consumption" do
@session.node_attributes = { "besnard_lakes" => "are_the_dark_horse" }
- expect(@session.node.besnard_lakes).to eq("are_the_dark_horse")
+ expect(@session.node["besnard_lakes"]).to eq("are_the_dark_horse")
#pending "1) keep attribs in an ivar 2) pass them to the node 3) feed them to the node on reset"
end
@@ -174,7 +229,6 @@ describe Shell::SoloSession do
it "runs chef with a resource collection from the compiled cookbooks" do
allow(@session).to receive(:node_built?).and_return(true)
- allow(Chef::Log).to receive(:level)
chef_runner = double("Chef::Runner.new", :converge => :converged)
expect(Chef::Runner).to receive(:new).with(an_instance_of(Chef::RunContext)).and_return(chef_runner)
@@ -188,8 +242,8 @@ describe Shell::SoloSession do
:build_node => true,
:register => true,
:sync_cookbooks => {})
- expect(Chef::Client).to receive(:new).with(nil, Chef::Config[:shell_config]).and_return(@client)
+ expect(Chef::Client).to receive(:new).with(json_attribs, Chef::Config[:shell_config]).and_return(@client)
+ @session.json_configuration = json_attribs
@session.send(:rebuild_node)
end
-
end
diff --git a/spec/unit/shell_out_spec.rb b/spec/unit/shell_out_spec.rb
deleted file mode 100644
index 35844f4961..0000000000
--- a/spec/unit/shell_out_spec.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-require File.expand_path("../../spec_helper", __FILE__)
-
-describe "Chef::ShellOut deprecation notices" do
- it "logs a warning when initializing a new Chef::ShellOut object" do
- expect(Chef::Log).to receive(:warn).with("Chef::ShellOut is deprecated, please use Mixlib::ShellOut")
- expect(Chef::Log).to receive(:warn).with(/Called from\:/)
- Chef::ShellOut.new("pwd")
- end
-end
-
-describe "Chef::Exceptions::ShellCommandFailed deprecation notices" do
-
- it "logs a warning when referencing the constant Chef::Exceptions::ShellCommandFailed" do
- expect(Chef::Log).to receive(:warn).with("Chef::Exceptions::ShellCommandFailed is deprecated, use Mixlib::ShellOut::ShellCommandFailed")
- expect(Chef::Log).to receive(:warn).with(/Called from\:/)
- Chef::Exceptions::ShellCommandFailed
- end
-end
diff --git a/spec/unit/user_spec.rb b/spec/unit/user_spec.rb
index a1806db987..10a79f6c8a 100644
--- a/spec/unit/user_spec.rb
+++ b/spec/unit/user_spec.rb
@@ -17,7 +17,7 @@
#
# DEPRECATION NOTE
-# This code only remains to support users still operating with
+# This code only remains to support users still operating with
# Open Source Chef Server 11 and should be removed once support
# for OSC 11 ends. New development should occur in user_spec.rb.
diff --git a/spec/unit/user_v1_spec.rb b/spec/unit/user_v1_spec.rb
index 51dc3c9118..16f0d0158b 100644
--- a/spec/unit/user_v1_spec.rb
+++ b/spec/unit/user_v1_spec.rb
@@ -332,7 +332,7 @@ describe Chef::UserV1 do
@user.password "some_password"
end
- let(:payload) {
+ let(:payload) do
{
:username => "some_username",
:display_name => "some_display_name",
@@ -342,7 +342,7 @@ describe Chef::UserV1 do
:email => "some_email",
:password => "some_password",
}
- }
+ end
context "when server API V1 is valid on the Chef Server receiving the request" do
context "when the user submits valid data" do
@@ -354,7 +354,7 @@ describe Chef::UserV1 do
end
context "when server API V1 is not valid on the Chef Server receiving the request" do
- let(:payload) {
+ let(:payload) do
{
:username => "some_username",
:display_name => "some_display_name",
@@ -365,7 +365,7 @@ describe Chef::UserV1 do
:password => "some_password",
:public_key => "some_public_key",
}
- }
+ end
before do
@user.public_key "some_public_key"
@@ -442,7 +442,7 @@ describe Chef::UserV1 do
end # update
describe "create" do
- let(:payload) {
+ let(:payload) do
{
:username => "some_username",
:display_name => "some_display_name",
@@ -451,7 +451,7 @@ describe Chef::UserV1 do
:email => "some_email",
:password => "some_password",
}
- }
+ end
before do
@user.username "some_username"
@user.display_name "some_display_name"
@@ -506,11 +506,11 @@ describe Chef::UserV1 do
# DEPRECATION
# This can be removed after API V0 support is gone
describe "reregister" do
- let(:payload) {
+ let(:payload) do
{
"username" => "some_username",
}
- }
+ end
before do
@user.username "some_username"
diff --git a/spec/unit/util/dsc/configuration_generator_spec.rb b/spec/unit/util/dsc/configuration_generator_spec.rb
index 12f62deb5a..cfa7a4e264 100644
--- a/spec/unit/util/dsc/configuration_generator_spec.rb
+++ b/spec/unit/util/dsc/configuration_generator_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Util::DSC::ConfigurationGenerator do
Chef::Util::DSC::ConfigurationGenerator.new(node, "tmp")
end
- describe '#validate_configuration_name!' do
+ describe "#validate_configuration_name!" do
it "should not raise an error if a name contains all upper case letters" do
conf_man.send(:validate_configuration_name!, "HELLO")
end
@@ -40,9 +40,9 @@ describe Chef::Util::DSC::ConfigurationGenerator do
%w{! @ # $ % ^ & * & * ( ) - = + \{ \} . ? < > \\ /}.each do |sym|
it "raises an Argument error if it configuration name contains #{sym}" do
- expect {
+ expect do
conf_man.send(:validate_configuration_name!, "Hello#{sym}")
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
end
end
@@ -57,15 +57,15 @@ describe Chef::Util::DSC::ConfigurationGenerator do
end
it "should raise an ArgumentError if you try to override outputpath" do
- expect {
+ expect do
conf_man.send(:get_merged_configuration_flags!, { "outputpath" => "a" }, "hello")
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should be case insensitive for switches that are not allowed" do
- expect {
+ expect do
conf_man.send(:get_merged_configuration_flags!, { "OutputPath" => "a" }, "hello")
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should be case insensitive to switches that are allowed" do
@@ -83,15 +83,15 @@ describe Chef::Util::DSC::ConfigurationGenerator do
end
it "should raise an ArgumentError if you try to override outputpath" do
- expect {
+ expect do
conf_man.send(:get_merged_configuration_flags!, { :outputpath => "a" }, "hello")
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should be case insensitive for switches that are not allowed" do
- expect {
+ expect do
conf_man.send(:get_merged_configuration_flags!, { :OutputPath => "a" }, "hello")
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "should be case insensitive to switches that are allowed" do
@@ -121,7 +121,7 @@ describe Chef::Util::DSC::ConfigurationGenerator do
#
end
- describe '#write_document_generation_script' do
+ describe "#write_document_generation_script" do
let(:file_like_object) { double("file like object") }
it "should write the input to a file" do
diff --git a/spec/unit/util/dsc/lcm_output_parser_spec.rb b/spec/unit/util/dsc/lcm_output_parser_spec.rb
index d59497de6f..65eaafe19c 100644
--- a/spec/unit/util/dsc/lcm_output_parser_spec.rb
+++ b/spec/unit/util/dsc/lcm_output_parser_spec.rb
@@ -19,20 +19,33 @@
require "chef/util/dsc/lcm_output_parser"
describe Chef::Util::DSC::LocalConfigurationManager::Parser do
- context "empty input parameter" do
+ context "empty input parameter for WhatIfParser" do
it "raises an exception when there are no valid lines" do
str = <<-EOF
EOF
- expect { Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str) }.to raise_error(Chef::Exceptions::LCMParser)
+ expect { Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false) }.to raise_error(Chef::Exceptions::LCMParser)
end
it "raises an exception for a nil input" do
- expect { Chef::Util::DSC::LocalConfigurationManager::Parser.parse(nil) }.to raise_error(Chef::Exceptions::LCMParser)
+ expect { Chef::Util::DSC::LocalConfigurationManager::Parser.parse(nil, false) }.to raise_error(Chef::Exceptions::LCMParser)
end
end
- context "correctly formatted output from lcm" do
+ context "empty input parameter for TestDSCParser" do
+ it "raises an exception when there are no valid lines" do
+ str = <<-EOF
+
+ EOF
+ expect { Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, true) }.to raise_error(Chef::Exceptions::LCMParser)
+ end
+
+ it "raises an exception for a nil input" do
+ expect { Chef::Util::DSC::LocalConfigurationManager::Parser.parse(nil, true) }.to raise_error(Chef::Exceptions::LCMParser)
+ end
+ end
+
+ context "correctly formatted output from lcm for WhatIfParser" do
it "returns a single resource when only 1 logged with the correct name" do
str = <<EOF
logtype: [machinename]: LCM: [ Start Set ]
@@ -40,7 +53,7 @@ logtype: [machinename]: LCM: [ Start Resource ] [name]
logtype: [machinename]: LCM: [ End Resource ] [name]
logtype: [machinename]: LCM: [ End Set ]
EOF
- resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str)
+ resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
expect(resources.length).to eq(1)
expect(resources[0].name).to eq("[name]")
end
@@ -54,7 +67,7 @@ logtype: [machinename]: LCM: [ End Set ] [name]
logtype: [machinename]: LCM: [ End Resource ] [name]
logtype: [machinename]: LCM: [ End Set ]
EOF
- resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str)
+ resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
expect(resources[0].changes_state?).to be_truthy
end
@@ -68,11 +81,11 @@ logtype: [machinename]: LCM: [ End Set ] [name]
logtype: [machinename]: LCM: [ End Resource ] [name]
logtype: [machinename]: LCM: [ End Set ]
EOF
- resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str)
+ resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
expect(resources[0].change_log).to match_array(["[name]", "[message]", "[name]"])
end
- it "should return false for changes_state?" do
+ it "returns false for changes_state?" do
str = <<EOF
logtype: [machinename]: LCM: [ Start Set ]
logtype: [machinename]: LCM: [ Start Resource ] [name]
@@ -80,11 +93,11 @@ logtype: [machinename]: LCM: [ Skip Set ] [name]
logtype: [machinename]: LCM: [ End Resource ] [name]
logtype: [machinename]: LCM: [ End Set ]
EOF
- resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str)
+ resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
expect(resources[0].changes_state?).to be_falsey
end
- it "should return an empty array for change_log if changes_state? is false" do
+ it "returns an empty array for change_log if changes_state? is false" do
str = <<EOF
logtype: [machinename]: LCM: [ Start Set ]
logtype: [machinename]: LCM: [ Start Resource ] [name]
@@ -92,13 +105,64 @@ logtype: [machinename]: LCM: [ Skip Set ] [name]
logtype: [machinename]: LCM: [ End Resource ] [name]
logtype: [machinename]: LCM: [ End Set ]
EOF
- resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str)
+ resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
expect(resources[0].change_log).to be_empty
end
end
- context "Incorrectly formatted output from LCM" do
- it "should allow missing a [End Resource] when its the last one and still find all the resource" do
+ context "correctly formatted output from lcm for TestDSCParser" do
+ it "returns a single resource when only 1 logged with the correct name" do
+ str = <<EOF
+InDesiredState : False
+ResourcesInDesiredState :
+ResourcesNotInDesiredState: [name]
+ReturnValue : 0
+PSComputerName : .
+EOF
+ resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, true)
+ expect(resources.length).to eq(1)
+ expect(resources[0].name).to eq("[name]")
+ end
+
+ it "identifies when a resource changes the state of the system" do
+ str = <<EOF
+InDesiredState : False
+ResourcesInDesiredState :
+ResourcesNotInDesiredState: [name]
+ReturnValue : 0
+PSComputerName : .
+EOF
+ resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, true)
+ expect(resources[0].changes_state?).to be_truthy
+ end
+
+ it "returns false for changes_state?" do
+ str = <<EOF
+InDesiredState : True
+ResourcesInDesiredState : [name]
+ResourcesNotInDesiredState:
+ReturnValue : 0
+PSComputerName : .
+EOF
+ resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, true)
+ expect(resources[0].changes_state?).to be_falsey
+ end
+
+ it "returns an empty array for change_log if changes_state? is false" do
+ str = <<EOF
+InDesiredState : True
+ResourcesInDesiredState : [name]
+ResourcesNotInDesiredState:
+ReturnValue : 0
+PSComputerName : .
+EOF
+ resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, true)
+ expect(resources[0].change_log).to be_empty
+ end
+ end
+
+ context "Incorrectly formatted output from LCM for WhatIfParser" do
+ it "allows missing [End Resource] when its the last one and still find all the resource" do
str = <<-EOF
logtype: [machinename]: LCM: [ Start Set ]
logtype: [machinename]: LCM: [ Start Resource ] [name]
@@ -114,12 +178,12 @@ logtype: [machinename]: LCM: [ End Set ]
logtype: [machinename]: LCM: [ End Set ]
EOF
- resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str)
+ resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
expect(resources[0].changes_state?).to be_falsey
expect(resources[1].changes_state?).to be_truthy
end
- it "should allow missing a [End Resource] when its the first one and still find all the resource" do
+ it "allow missing [End Resource] when its the first one and still find all the resource" do
str = <<-EOF
logtype: [machinename]: LCM: [ Start Set ]
logtype: [machinename]: LCM: [ Start Resource ] [name]
@@ -135,12 +199,12 @@ logtype: [machinename]: LCM: [ End Resource ]
logtype: [machinename]: LCM: [ End Set ]
EOF
- resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str)
+ resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
expect(resources[0].changes_state?).to be_falsey
expect(resources[1].changes_state?).to be_truthy
end
- it "should allow missing set and end resource and assume an unconverged resource in this case" do
+ it "allows missing set and end resource and assume an unconverged resource in this case" do
str = <<-EOF
logtype: [machinename]: LCM: [ Start Set ]
logtype: [machinename]: LCM: [ Start Resource ] [name]
@@ -154,11 +218,31 @@ logtype: [machinename]: LCM: [ End Set ]
logtype: [machinename]: LCM: [ End Resource ]
logtype: [machinename]: LCM: [ End Set ]
EOF
- resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str)
+ resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
expect(resources[0].changes_state?).to be_truthy
expect(resources[0].name).to eql("[name]")
expect(resources[1].changes_state?).to be_truthy
expect(resources[1].name).to eql("[name2]")
end
end
+
+ context "Incorrectly formatted output from LCM for TestDSCParser" do
+ it "allows missing [End Resource] when its the last one and still find all the resource" do
+ str = <<EOF
+InDesiredState : True
+ResourcesInDesiredState :
+ResourcesNotInDesiredState: [name]
+ReturnValue : 0
+PSComputerName : .
+InDesiredState : True
+ResourcesInDesiredState :
+ResourcesNotInDesiredState: [name2]
+ReturnValue : 0
+PSComputerName : .
+EOF
+
+ resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, true)
+ expect(resources[0].changes_state?).to be_falsey
+ end
+ end
end
diff --git a/spec/unit/util/dsc/local_configuration_manager_spec.rb b/spec/unit/util/dsc/local_configuration_manager_spec.rb
index 15cf38394e..4c33fc8616 100644
--- a/spec/unit/util/dsc/local_configuration_manager_spec.rb
+++ b/spec/unit/util/dsc/local_configuration_manager_spec.rb
@@ -23,15 +23,17 @@ describe Chef::Util::DSC::LocalConfigurationManager do
let(:lcm) { Chef::Util::DSC::LocalConfigurationManager.new(nil, "tmp") }
- let(:normal_lcm_output) { <<-EOH
+ let(:normal_lcm_output) do
+ <<-EOH
logtype: [machinename]: LCM: [ Start Set ]
logtype: [machinename]: LCM: [ Start Resource ] [name]
logtype: [machinename]: LCM: [ End Resource ] [name]
logtype: [machinename]: LCM: [ End Set ]
EOH
- }
+ end
- let(:no_whatif_lcm_output) { <<-EOH
+ let(:no_whatif_lcm_output) do
+ <<-EOH
Start-DscConfiguration : A parameter cannot be found\r\n that matches parameter name 'whatif'.
At line:1 char:123
+ run-somecommand -whatif
@@ -39,32 +41,44 @@ At line:1 char:123
+ CategoryInfo : InvalidArgument: (:) [Start-DscConfiguration], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,SomeCompany.SomeAssembly.Commands.RunSomeCommand
EOH
- }
+ end
- let(:dsc_resource_import_failure_output) { <<-EOH
+ let(:dsc_resource_import_failure_output) do
+ <<-EOH
PowerShell provider MSFT_xWebsite failed to execute Test-TargetResource functionality with error message: Please ensure that WebAdministration module is installed. + CategoryInfo : InvalidOperation: (:) [], CimException + FullyQualifiedErrorId : ProviderOperationExecutionFailure + PSComputerName : . PowerShell provider MSFT_xWebsite failed to execute Test-TargetResource functionality with error message: Please ensure that WebAdministration module is installed. + CategoryInfo : InvalidOperation: (:) [], CimException + FullyQualifiedErrorId : ProviderOperationExecutionFailure + PSComputerName : . The SendConfigurationApply function did not succeed. + CategoryInfo : NotSpecified: (root/Microsoft/...gurationManager:String) [], CimException + FullyQualifiedErrorId : MI RESULT 1 + PSComputerName : .
EOH
- }
+ end
- let(:lcm_status) {
+ let(:lcm_status) do
double("LCM cmdlet status", :stderr => lcm_standard_error, :return_value => lcm_standard_output, :succeeded? => lcm_cmdlet_success)
- }
+ end
describe "test_configuration method invocation" do
context "when interacting with the LCM using a PowerShell cmdlet" do
before(:each) do
allow(lcm).to receive(:run_configuration_cmdlet).and_return(lcm_status)
+ allow(lcm).to receive(:ps_version_gte_5?).and_return(false)
end
context "that returns successfully" do
- before(:each) do
- allow(lcm).to receive(:run_configuration_cmdlet).and_return(lcm_status)
+ let(:lcm_standard_output) { normal_lcm_output }
+ let(:lcm_standard_error) { nil }
+ let(:lcm_cmdlet_success) { true }
+
+ it "successfully returns resource information for normally formatted output when cmdlet the cmdlet succeeds" do
+ test_configuration_result = lcm.test_configuration("config", {})
+ expect(test_configuration_result.class).to be(Array)
+ expect(test_configuration_result.length).to be > 0
+ expect(Chef::Log).not_to receive(:warn)
end
+ end
+ context "when running on PowerShell version 5" do
let(:lcm_standard_output) { normal_lcm_output }
let(:lcm_standard_error) { nil }
let(:lcm_cmdlet_success) { true }
- it "should successfully return resource information for normally formatted output when cmdlet the cmdlet succeeds" do
+ it "successfully returns resource information for normally formatted output when cmdlet the cmdlet succeeds" do
+ allow(lcm).to receive(:ps_version_gte_5?).and_return(true)
test_configuration_result = lcm.test_configuration("config", {})
expect(test_configuration_result.class).to be(Array)
expect(test_configuration_result.length).to be > 0
@@ -72,16 +86,54 @@ EOH
end
end
+ context "when running on PowerShell version less than 5" do
+ let(:lcm_standard_output) { normal_lcm_output }
+ let(:lcm_standard_error) { nil }
+ let(:lcm_cmdlet_success) { true }
+
+ it "successfully returns resource information for normally formatted output when cmdlet the cmdlet succeeds" do
+ allow(lcm).to receive(:ps_version_gte_5?).and_return(false)
+ test_configuration_result = lcm.test_configuration("config", {})
+ expect(test_configuration_result.class).to be(Array)
+ expect(test_configuration_result.length).to be > 0
+ expect(Chef::Log).not_to receive(:warn)
+ end
+ end
+
+ context "#lcm_command" do
+ let(:common_command_prefix) { "$ProgressPreference = 'SilentlyContinue';" }
+ let(:ps4_base_command) { "#{common_command_prefix} Start-DscConfiguration -path tmp -wait -erroraction 'stop' -force" }
+ let(:lcm_command_ps4) { ps4_base_command + " -whatif; if (! $?) { exit 1 }" }
+ let(:lcm_command_ps5) { "#{common_command_prefix} Test-DscConfiguration -path tmp | format-list" }
+ let(:lcm_standard_output) { normal_lcm_output }
+ let(:lcm_standard_error) { nil }
+ let(:lcm_cmdlet_success) { true }
+
+ it "successfully returns command when apply_configuration true" do
+ expect(lcm.send(:lcm_command, true)).to eq(ps4_base_command)
+ end
+
+ it "successfully returns command when PowerShell version 4" do
+ allow(lcm).to receive(:ps_version_gte_5?).and_return(false)
+ expect(lcm.send(:lcm_command, false)).to eq(lcm_command_ps4)
+ end
+
+ it "successfully returns command when PowerShell version 5" do
+ allow(lcm).to receive(:ps_version_gte_5?).and_return(true)
+ expect(lcm.send(:lcm_command, false)).to eq(lcm_command_ps5)
+ end
+ end
+
context "that fails due to missing what-if switch in DSC resource cmdlet implementation" do
let(:lcm_standard_output) { "" }
let(:lcm_standard_error) { no_whatif_lcm_output }
let(:lcm_cmdlet_success) { false }
- it 'returns true when passed to #whatif_not_supported?' do
+ it "returns true when passed to #whatif_not_supported?" do
expect(lcm.send(:whatif_not_supported?, no_whatif_lcm_output)).to be_truthy
end
- it "should should return a (possibly empty) array of ResourceInfo instances" do
+ it "returns a (possibly empty) array of ResourceInfo instances" do
expect(Chef::Log).to receive(:warn).at_least(:once)
expect(lcm).to receive(:whatif_not_supported?).and_call_original
test_configuration_result = nil
@@ -95,14 +147,14 @@ EOH
let(:lcm_standard_error) { dsc_resource_import_failure_output }
let(:lcm_cmdlet_success) { false }
- it "should log a warning if the message is formatted as expected when a resource import failure occurs" do
+ it "logs a warning if the message is formatted as expected when a resource import failure occurs" do
expect(Chef::Log).to receive(:warn).at_least(:once)
expect(lcm).to receive(:dsc_module_import_failure?).and_call_original
test_configuration_result = nil
expect { test_configuration_result = lcm.test_configuration("config", {}) }.not_to raise_error
end
- it "should return a (possibly empty) array of ResourceInfo instances" do
+ it "returns a (possibly empty) array of ResourceInfo instances" do
expect(Chef::Log).to receive(:warn).at_least(:once)
test_configuration_result = nil
expect { test_configuration_result = lcm.test_configuration("config", {}) }.not_to raise_error
@@ -115,7 +167,7 @@ EOH
let(:lcm_standard_error) { "Abort, Retry, Fail?" }
let(:lcm_cmdlet_success) { false }
- it "should log a warning" do
+ it "logs a warning" do
expect(Chef::Log).to receive(:warn).at_least(:once)
expect(lcm).to receive(:dsc_module_import_failure?).and_call_original
expect { lcm.test_configuration("config", {}) }.not_to raise_error
@@ -123,16 +175,26 @@ EOH
end
end
- it "should identify a correctly formatted error message as a resource import failure" do
+ it "identify a correctly formatted error message as a resource import failure" do
expect(lcm.send(:dsc_module_import_failure?, dsc_resource_import_failure_output)).to be(true)
end
- it "should not identify an incorrectly formatted error message as a resource import failure" do
+ it "does not identify an incorrectly formatted error message as a resource import failure" do
expect(lcm.send(:dsc_module_import_failure?, dsc_resource_import_failure_output.gsub("module", "gibberish"))).to be(false)
end
- it "should not identify a message without a CimException reference as a resource import failure" do
+ it "does not identify a message without a CimException reference as a resource import failure" do
expect(lcm.send(:dsc_module_import_failure?, dsc_resource_import_failure_output.gsub("CimException", "ArgumentException"))).to be(false)
end
end
+
+ describe "#run_configuration_cmdlet" do
+ context "when invalid dsc script is given" do
+ it "raises exception" do
+ configuration_document = "invalid-config"
+ shellout_flags = { :cwd => nil, :environment => nil, :timeout => nil }
+ expect { lcm.send(:run_configuration_cmdlet, configuration_document, true, shellout_flags) }.to raise_error(Chef::Exceptions::PowershellCmdletException)
+ end
+ end
+ end
end
diff --git a/spec/unit/util/dsc/resource_store.rb b/spec/unit/util/dsc/resource_store.rb
index 84b39190b2..a864a2c1da 100644
--- a/spec/unit/util/dsc/resource_store.rb
+++ b/spec/unit/util/dsc/resource_store.rb
@@ -21,28 +21,30 @@ require "chef/util/dsc/resource_store"
describe Chef::Util::DSC::ResourceStore do
let(:resource_store) { Chef::Util::DSC::ResourceStore.new }
- let(:resource_a) { {
+ let(:resource_a) do
+ {
"ResourceType" => "AFoo",
"Name" => "Foo",
"Module" => { "Name" => "ModuleA" },
}
- }
+ end
- let(:resource_b) { {
+ let(:resource_b) do
+ {
"ResourceType" => "BFoo",
"Name" => "Foo",
"Module" => { "Name" => "ModuleB" },
}
- }
+ end
context "when resources are not cached" do
- context 'when calling #resources' do
+ context "when calling #resources" do
it "returns an empty array" do
expect(resource_store.resources).to eql([])
end
end
- context 'when calling #find' do
+ context "when calling #find" do
it "returns an empty list if it cannot find any matching resources" do
expect(resource_store).to receive(:query_resource).and_return([])
expect(resource_store.find("foo")).to eql([])
diff --git a/spec/unit/util/editor_spec.rb b/spec/unit/util/editor_spec.rb
index 7a0ec91533..e53bc9662a 100644
--- a/spec/unit/util/editor_spec.rb
+++ b/spec/unit/util/editor_spec.rb
@@ -2,7 +2,7 @@ require "spec_helper"
require "chef/util/editor"
describe Chef::Util::Editor do
- describe '#initialize' do
+ describe "#initialize" do
it "takes an Enumerable of lines" do
editor = described_class.new(File.open(__FILE__))
expect(editor.lines).to be == IO.readlines(__FILE__)
@@ -18,7 +18,7 @@ describe Chef::Util::Editor do
subject(:editor) { described_class.new(input_lines) }
let(:input_lines) { %w{one two two three} }
- describe '#append_line_after' do
+ describe "#append_line_after" do
context "when there is no match" do
subject(:execute) { editor.append_line_after("missing", "new") }
@@ -44,7 +44,7 @@ describe Chef::Util::Editor do
end
end
- describe '#append_line_if_missing' do
+ describe "#append_line_if_missing" do
context "when there is no match" do
subject(:execute) { editor.append_line_if_missing("missing", "new") }
@@ -70,7 +70,7 @@ describe Chef::Util::Editor do
end
end
- describe '#remove_lines' do
+ describe "#remove_lines" do
context "when there is no match" do
subject(:execute) { editor.remove_lines("missing") }
@@ -96,7 +96,7 @@ describe Chef::Util::Editor do
end
end
- describe '#replace' do
+ describe "#replace" do
context "when there is no match" do
subject(:execute) { editor.replace("missing", "new") }
@@ -123,7 +123,7 @@ describe Chef::Util::Editor do
end
end
- describe '#replace_lines' do
+ describe "#replace_lines" do
context "when there is no match" do
subject(:execute) { editor.replace_lines("missing", "new") }
diff --git a/spec/unit/util/powershell/cmdlet_spec.rb b/spec/unit/util/powershell/cmdlet_spec.rb
index 5c0e66aee2..800e4cc9c0 100644
--- a/spec/unit/util/powershell/cmdlet_spec.rb
+++ b/spec/unit/util/powershell/cmdlet_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Util::Powershell::Cmdlet do
@cmdlet = Chef::Util::Powershell::Cmdlet.new(@node, "Some-Commandlet")
end
- describe '#validate_switch_name!' do
+ describe "#validate_switch_name!" do
it "should not raise an error if a name contains all upper case letters" do
@cmdlet.send(:validate_switch_name!, "HELLO")
end
@@ -40,14 +40,14 @@ describe Chef::Util::Powershell::Cmdlet do
%w{! @ # $ % ^ & * & * ( ) - = + \{ \} . ? < > \\ /}.each do |sym|
it "raises an Argument error if it configuration name contains #{sym}" do
- expect {
+ expect do
@cmdlet.send(:validate_switch_name!, "Hello#{sym}")
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
end
end
- describe '#escape_parameter_value' do
+ describe "#escape_parameter_value" do
# Is this list really complete?
%w{` " # '}.each do |c|
it "escapse #{c}" do
@@ -60,23 +60,23 @@ describe Chef::Util::Powershell::Cmdlet do
end
end
- describe '#escape_string_parameter_value' do
+ describe "#escape_string_parameter_value" do
it "surrounds a string with ''" do
expect(@cmdlet.send(:escape_string_parameter_value, "stuff")).to eql("'stuff'")
end
end
- describe '#command_switches_string' do
+ describe "#command_switches_string" do
it "raises an ArgumentError if the key is not a symbol" do
- expect {
+ expect do
@cmdlet.send(:command_switches_string, { "foo" => "bar" })
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "does not allow invalid switch names" do
- expect {
+ expect do
@cmdlet.send(:command_switches_string, { :foo! => "bar" })
- }.to raise_error(ArgumentError)
+ end.to raise_error(ArgumentError)
end
it "ignores switches with a false value" do
diff --git a/spec/unit/util/selinux_spec.rb b/spec/unit/util/selinux_spec.rb
index 609ff02215..5081281cf4 100644
--- a/spec/unit/util/selinux_spec.rb
+++ b/spec/unit/util/selinux_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright 2013-2017, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -124,45 +124,31 @@ describe Chef::Util::Selinux do
end
it "should call restorecon non-recursive by default" do
- restorecon_command = "#{@restorecon_enabled_path} -R \"#{path}\""
- expect(@test_instance).to receive(:shell_out!).twice.with(restorecon_command)
+ expect(@test_instance).to receive(:shell_out_compact!).twice.with(@restorecon_enabled_path, [ "-R", path ])
@test_instance.restore_security_context(path)
expect(File).not_to receive(:executable?)
@test_instance.restore_security_context(path)
end
it "should call restorecon recursive when recursive is set" do
- restorecon_command = "#{@restorecon_enabled_path} -R -r \"#{path}\""
- expect(@test_instance).to receive(:shell_out!).twice.with(restorecon_command)
+ expect(@test_instance).to receive(:shell_out_compact!).twice.with(@restorecon_enabled_path, [ "-R", "-r", path ])
@test_instance.restore_security_context(path, true)
expect(File).not_to receive(:executable?)
@test_instance.restore_security_context(path, true)
end
it "should call restorecon non-recursive when recursive is not set" do
- restorecon_command = "#{@restorecon_enabled_path} -R \"#{path}\""
- expect(@test_instance).to receive(:shell_out!).twice.with(restorecon_command)
+ expect(@test_instance).to receive(:shell_out_compact!).twice.with(@restorecon_enabled_path, [ "-R", path ])
@test_instance.restore_security_context(path)
expect(File).not_to receive(:executable?)
@test_instance.restore_security_context(path)
end
describe "when restorecon doesn't exist on the system" do
- before do
- allow(File).to receive(:executable?) do |file_path|
- expect(file_path.end_with?("restorecon")).to be_truthy
- false
- end
- end
-
it "should log a warning message" do
- log = [ ]
- allow(Chef::Log).to receive(:warn) do |message|
- log << message
- end
-
+ allow(File).to receive(:executable?).with(/restorecon$/).and_return(false)
+ expect(Chef::Log).to receive(:warn).with(/Can not find 'restorecon' on the system. Skipping selinux security context restore./).at_least(:once)
@test_instance.restore_security_context(path)
- expect(log).not_to be_empty
expect(File).not_to receive(:executable?)
@test_instance.restore_security_context(path)
end
diff --git a/spec/unit/util/windows/logon_session_spec.rb b/spec/unit/util/windows/logon_session_spec.rb
new file mode 100644
index 0000000000..8a94802bf6
--- /dev/null
+++ b/spec/unit/util/windows/logon_session_spec.rb
@@ -0,0 +1,285 @@
+#
+# Author:: Adam Edwards (<adamed@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/util/windows/logon_session"
+
+describe ::Chef::Util::Windows::LogonSession do
+ before do
+ stub_const("Chef::ReservedNames::Win32::API::Security", Class.new)
+ stub_const("Chef::ReservedNames::Win32::API::Security::LOGON32_LOGON_NEW_CREDENTIALS", 314)
+ stub_const("Chef::ReservedNames::Win32::API::Security::LOGON32_PROVIDER_DEFAULT", 159)
+ stub_const("Chef::ReservedNames::Win32::API::System", Class.new )
+ end
+
+ let(:session) { ::Chef::Util::Windows::LogonSession.new(session_user, password, session_domain, authentication) }
+ let(:authentication) { :remote }
+
+ shared_examples_for "it received syntactically invalid credentials" do
+ it "does not raisees an exception when it is initialized" do
+ expect { session }.to raise_error(ArgumentError)
+ end
+ end
+
+ shared_examples_for "it received an incorrect username and password combination" do
+ before do
+ expect(Chef::ReservedNames::Win32::API::Security).to receive(:LogonUserW).and_return(false)
+ end
+
+ it "raises a Chef::Exceptions::Win32APIError exception when the open method is called" do
+ expect { session.open }.to raise_error(Chef::Exceptions::Win32APIError)
+ expect(session).not_to receive(:close)
+ expect(Chef::ReservedNames::Win32::API::System).not_to receive(:CloseHandle)
+ end
+ end
+
+ shared_examples_for "it received valid credentials" do
+ it "does not raise an exception when the open method is called" do
+ expect(Chef::ReservedNames::Win32::API::Security).to receive(:LogonUserW).and_return(true)
+ expect { session.open }.not_to raise_error
+ end
+ end
+
+ shared_examples_for "the session is not open" do
+ it "does not raise an exception when #open is called" do
+ expect(Chef::ReservedNames::Win32::API::Security).to receive(:LogonUserW).and_return(true)
+ expect { session.open }.not_to raise_error
+ end
+
+ it "raises an exception if #close is called" do
+ expect { session.close }.to raise_error(RuntimeError)
+ end
+
+ it "raises an exception if #restore_user_context is called" do
+ expect { session.restore_user_context }.to raise_error(RuntimeError)
+ end
+ end
+
+ shared_examples_for "the session is open" do
+ before do
+ allow(Chef::ReservedNames::Win32::API::System).to receive(:CloseHandle)
+ end
+ it "does not result in an exception when #restore_user_context is called" do
+ expect { session.restore_user_context }.not_to raise_error
+ end
+
+ it "does not result in an exception when #close is called" do
+ expect { session.close }.not_to raise_error
+ end
+
+ it "does close the operating system handle when #close is called" do
+ expect(Chef::ReservedNames::Win32::API::System).not_to receive(:CloseHandle)
+ expect { session.restore_user_context }.not_to raise_error
+ end
+ end
+
+ context "when the session is initialized with a nil user" do
+ context "when the password, and domain are all nil" do
+ let(:session_user) { nil }
+ let(:session_domain) { nil }
+ let(:password) { nil }
+ it_behaves_like "it received syntactically invalid credentials"
+ end
+
+ context "when the password is non-nil password, and the domain is nil" do
+ let(:session_user) { nil }
+ let(:password) { "ponies" }
+ let(:session_domain) { nil }
+ it_behaves_like "it received syntactically invalid credentials"
+ end
+
+ context "when the password is nil and the domain is non-nil" do
+ let(:session_user) { nil }
+ let(:password) { nil }
+ let(:session_domain) { "fairyland" }
+ it_behaves_like "it received syntactically invalid credentials"
+ end
+
+ context "when the password and domain are non-nil" do
+ let(:session_user) { nil }
+ let(:password) { "ponies" }
+ let(:session_domain) { "fairyland" }
+ it_behaves_like "it received syntactically invalid credentials"
+ end
+ end
+
+ context "when the session is initialized with a valid user" do
+ let(:session_user) { "chalena" }
+
+ context "when the password is nil" do
+ let(:password) { nil }
+ context "when the domain is non-nil" do
+ let(:session_domain) { "fairyland" }
+ it_behaves_like "it received syntactically invalid credentials"
+ end
+
+ context "when the domain is nil" do
+ context "when the domain is non-nil" do
+ let(:session_domain) { nil }
+ it_behaves_like "it received syntactically invalid credentials"
+ end
+ end
+ end
+
+ context "when a syntactically valid username and password are supplied" do
+ context "when the password is non-nil and the domain is nil" do
+ let(:password) { "ponies" }
+ let(:session_domain) { nil }
+ it "does not raise an exception if it is initialized with a non-nil username, non-nil password, and a nil domain" do
+ expect { session }.not_to raise_error
+ end
+
+ it_behaves_like "it received valid credentials"
+ it_behaves_like "it received an incorrect username and password combination"
+ end
+
+ context "when the password and domain are non-nil" do
+ let(:password) { "ponies" }
+ let(:session_domain) { "fairyland" }
+ it "does not raise an exception if it is initialized with a non-nil username, non-nil password, and non-nil domain" do
+ expect { session }.not_to raise_error
+ end
+
+ it_behaves_like "it received valid credentials"
+ it_behaves_like "it received an incorrect username and password combination"
+ end
+
+ context "when the #open method has not been called" do
+ let(:password) { "ponies" }
+ let(:session_domain) { "fairyland" }
+ it_behaves_like "the session is not open"
+ end
+
+ context "when the session was opened" do
+ let(:password) { "ponies" }
+ let(:session_domain) { "fairyland" }
+
+ before do
+ expect(Chef::ReservedNames::Win32::API::Security).to receive(:LogonUserW).and_return(true)
+ expect { session.open }.not_to raise_error
+ end
+
+ it "raises an exception if #open is called" do
+ expect { session.open }.to raise_error(RuntimeError)
+ end
+
+ context "when the session was opened and then closed with the #close method" do
+ before do
+ expect(Chef::ReservedNames::Win32::API::System).to receive(:CloseHandle)
+ expect { session.close }.not_to raise_error
+ end
+ it_behaves_like "the session is not open"
+ end
+
+ it "can be closed and close the operating system handle" do
+ expect(Chef::ReservedNames::Win32::API::System).to receive(:CloseHandle)
+ expect { session.close }.not_to raise_error
+ end
+
+ it "can impersonate the user" do
+ expect(Chef::ReservedNames::Win32::API::Security).to receive(:ImpersonateLoggedOnUser).and_return(true)
+ expect { session.set_user_context }.not_to raise_error
+ end
+
+ context "when #set_user_context fails due to low resources causing a failure to impersonate" do
+ before do
+ expect(Chef::ReservedNames::Win32::API::Security).to receive(:ImpersonateLoggedOnUser).and_return(false)
+ end
+
+ it "raises an exception when #set_user_context fails because impersonation failed" do
+ expect { session.set_user_context }.to raise_error(Chef::Exceptions::Win32APIError)
+ end
+
+ context "when calling subsequent methods" do
+ before do
+ expect { session.set_user_context }.to raise_error(Chef::Exceptions::Win32APIError)
+ expect(Chef::ReservedNames::Win32::API::Security).not_to receive(:RevertToSelf)
+ end
+
+ it_behaves_like "the session is open"
+ end
+ end
+
+ context "when #set_user_context successfully impersonates the user" do
+ before do
+ expect(Chef::ReservedNames::Win32::API::Security).to receive(:ImpersonateLoggedOnUser).and_return(true)
+ expect { session.set_user_context }.not_to raise_error
+ end
+
+ context "when attempting to impersonate while already impersonating" do
+ it "raises an error if the #set_user_context is called again" do
+ expect { session.set_user_context }.to raise_error(RuntimeError)
+ end
+ end
+
+ describe "the impersonation will be reverted" do
+ before do
+ expect(Chef::ReservedNames::Win32::API::Security).to receive(:RevertToSelf).and_return(true)
+ end
+ it_behaves_like "the session is open"
+ end
+
+ context "when the attempt to revert impersonation fails" do
+ before do
+ expect(Chef::ReservedNames::Win32::API::Security).to receive(:RevertToSelf).and_return(false)
+ end
+
+ it "raises an exception when #restore_user_context is called" do
+ expect { session.restore_user_context }.to raise_error(Chef::Exceptions::Win32APIError)
+ end
+
+ it "raises an exception when #close is called and impersonation fails" do
+ expect { session.close }.to raise_error(Chef::Exceptions::Win32APIError)
+ end
+
+ context "when calling methods after revert fails in #restore_user_context" do
+ before do
+ expect { session.restore_user_context }.to raise_error(Chef::Exceptions::Win32APIError)
+ end
+
+ context "when revert continues to fail" do
+ before do
+ expect(Chef::ReservedNames::Win32::API::Security).to receive(:RevertToSelf).and_return(false)
+ end
+ it "raises an exception when #close is called and impersonation fails" do
+ expect { session.close }.to raise_error(Chef::Exceptions::Win32APIError)
+ end
+ end
+
+ context "when revert stops failing and succeeds" do
+ before do
+ expect(Chef::ReservedNames::Win32::API::Security).to receive(:RevertToSelf).and_return(true)
+ end
+
+ it "does not raise an exception when #restore_user_context is called" do
+ expect { session.restore_user_context }.not_to raise_error
+ end
+
+ it "does not raise an exception when #close is called" do
+ expect(Chef::ReservedNames::Win32::API::System).to receive(:CloseHandle)
+ expect { session.close }.not_to raise_error
+ end
+ end
+ end
+ end
+ end
+
+ end
+ end
+ end
+end
diff --git a/spec/unit/version_string_spec.rb b/spec/unit/version_string_spec.rb
new file mode 100644
index 0000000000..14fc786d1b
--- /dev/null
+++ b/spec/unit/version_string_spec.rb
@@ -0,0 +1,79 @@
+# Copyright:: Copyright 2017, Noah Kantrowitz
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "spec_helper"
+require "chef/version_string"
+
+describe Chef::VersionString do
+ let(:input) { "1.2.3" }
+ subject(:described_object) { described_class.new(input) }
+
+ it { is_expected.to eq "1.2.3" }
+ it { is_expected.to eql "1.2.3" }
+ it { is_expected.to be == "1.2.3" }
+ it { is_expected.to be < "abc" }
+ it { is_expected.to be > "0" }
+ it { is_expected.to eq described_class.new("1.2.3") }
+ it { is_expected.to be == described_class.new("1.2.3") }
+
+ context "with !=" do
+ subject { described_object != "1.2.4" }
+ it { is_expected.to be true }
+ end
+
+ context "with +" do
+ subject { described_object + "asdf" }
+ it { is_expected.to eq "1.2.3asdf" }
+ end
+
+ context "with *" do
+ subject { described_object * 3 }
+ it { is_expected.to eq "1.2.31.2.31.2.3" }
+ end
+
+ context "with version-like comparisons" do
+ subject { described_class.new("1.02.3") }
+
+ it { is_expected.to eq "1.2.3" }
+ it { is_expected.to be > "1.2.2" }
+ it { is_expected.to be > "1.2.3a" }
+ it { is_expected.to be < "1.2.4" }
+ end
+
+ context "with =~ Regexp" do
+ subject { described_object =~ /^1/ }
+ it { is_expected.to eq 0 }
+ end
+
+ context "with =~ Requirement" do
+ subject { described_object =~ Gem::Requirement.create("~> 1.0") }
+ it { is_expected.to be true }
+ end
+
+ context "with =~ String" do
+ subject { described_object =~ "~> 1.0" }
+ it { is_expected.to be true }
+ end
+
+ context "with Regexp =~" do
+ subject { /^2/ =~ described_object }
+ it { is_expected.to be nil }
+ end
+
+ context "with String =~" do
+ subject { "~> 1.0" =~ described_object }
+ it { expect { subject }.to raise_error TypeError }
+ end
+end
diff --git a/spec/unit/win32/error_spec.rb b/spec/unit/win32/error_spec.rb
new file mode 100644
index 0000000000..e8b6d5af6a
--- /dev/null
+++ b/spec/unit/win32/error_spec.rb
@@ -0,0 +1,67 @@
+#
+# Author:: Aliasgar Batterywala (aliasgar.batterywala@msystechnologies.com)
+# Copyright:: Copyright 2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+if Chef::Platform.windows?
+ require "chef/win32/error"
+ require "chef/win32/api/error"
+end
+
+describe "Chef::Win32::Error", :windows_only do
+ describe "self.raise!" do
+ context "code is not passed to the raise! method" do
+ context "last error received is user_not_found" do
+ it "raises UserIDNotFound exception" do
+ expect(Chef::ReservedNames::Win32::Error).to receive(:get_last_error).and_return(
+ Chef::ReservedNames::Win32::API::Error::ERROR_USER_NOT_FOUND
+ )
+ expect(Chef::ReservedNames::Win32::Error).to receive_message_chain(:format_message, :strip)
+ expect { Chef::ReservedNames::Win32::Error.raise! }.to raise_error Chef::Exceptions::UserIDNotFound
+ end
+ end
+
+ context "last error received is not user_not_found" do
+ it "raises Win32APIError exception" do
+ expect(Chef::ReservedNames::Win32::Error).to receive(:get_last_error).and_return(
+ Chef::ReservedNames::Win32::API::Error::ERROR_BAD_USERNAME
+ )
+ expect(Chef::ReservedNames::Win32::Error).to receive_message_chain(:format_message, :strip).and_return("Bad Username")
+ expect { Chef::ReservedNames::Win32::Error.raise! }.to raise_error Chef::Exceptions::Win32APIError
+ end
+ end
+ end
+
+ context "code is passed to the raise! method" do
+ context "last error received is user_not_found" do
+ it "raises UserIDNotFound exception" do
+ expect(Chef::ReservedNames::Win32::Error).to_not receive(:get_last_error)
+ expect(Chef::ReservedNames::Win32::Error).to receive_message_chain(:format_message, :strip)
+ expect { Chef::ReservedNames::Win32::Error.raise! nil, Chef::ReservedNames::Win32::API::Error::ERROR_USER_NOT_FOUND }.to raise_error Chef::Exceptions::UserIDNotFound
+ end
+ end
+
+ context "last error received is not user_not_found" do
+ it "raises Win32APIError exception" do
+ expect(Chef::ReservedNames::Win32::Error).to_not receive(:get_last_error)
+ expect(Chef::ReservedNames::Win32::Error).to receive_message_chain(:format_message, :strip).and_return("Bad Username")
+ expect { Chef::ReservedNames::Win32::Error.raise! nil, Chef::ReservedNames::Win32::API::Error::ERROR_BAD_USERNAME }.to raise_error Chef::Exceptions::Win32APIError
+ end
+ end
+ end
+ end
+end
diff --git a/spec/unit/win32/link_spec.rb b/spec/unit/win32/link_spec.rb
new file mode 100644
index 0000000000..5f749c034e
--- /dev/null
+++ b/spec/unit/win32/link_spec.rb
@@ -0,0 +1,73 @@
+#
+# Copyright:: Copyright 2012-2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+if Chef::Platform.windows?
+ require "chef/win32/api/file"
+ require "chef/win32/file"
+ require "chef/win32/version"
+end
+
+describe Chef::ReservedNames::Win32::File, :windows_only do
+ context "#symlink" do
+ let(:with_privilege) { Chef::ReservedNames::Win32::API::File::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE }
+ let(:without_privilege) { 0x0 }
+
+ context "an invalid parameter is passed" do
+ it "will throw an exception if an invalid parameter is passed" do
+ allow(File).to receive(:directory?).and_return(false)
+ allow(Chef::ReservedNames::Win32::File).to receive(:encode_path) { |a| a }
+ allow_any_instance_of(Chef::ReservedNames::Win32::Version).to receive(:windows_10?).and_return(true)
+ allow_any_instance_of(Chef::ReservedNames::Win32::Version).to receive(:build_number).and_return(1)
+ allow(Chef::ReservedNames::Win32::File).to receive(:CreateSymbolicLinkW).and_return(nil)
+
+ expect { Chef::ReservedNames::Win32::File.symlink("a", "b") }.to raise_error Chef::Exceptions::Win32APIError
+ end
+ end
+
+ context "a valid parameter is passed" do
+ before(:each) do
+ allow(File).to receive(:directory?).and_return(false)
+ allow(Chef::ReservedNames::Win32::File).to receive(:encode_path) { |a| a }
+ allow(Chef::ReservedNames::Win32::File).to receive(:CreateSymbolicLinkW).with(any_args) { "don't //actually// do this" }
+ end
+
+ it "will not pass the unpirivileged symlink flag if the node is not Windows 10" do
+ allow_any_instance_of(Chef::ReservedNames::Win32::Version).to receive(:windows_10?).and_return(false)
+
+ expect(Chef::ReservedNames::Win32::File).to receive(:CreateSymbolicLinkW).with("b", "a", without_privilege)
+ described_class.symlink("a", "b")
+ end
+
+ it "will not pass the unpirivileged symlink flag if the node is not at least Windows 10 Creators Update" do
+ allow_any_instance_of(Chef::ReservedNames::Win32::Version).to receive(:windows_10?).and_return(true)
+ allow_any_instance_of(Chef::ReservedNames::Win32::Version).to receive(:build_number).and_return(1)
+
+ expect(Chef::ReservedNames::Win32::File).to receive(:CreateSymbolicLinkW).with("b", "a", without_privilege)
+ described_class.symlink("a", "b")
+ end
+
+ it "will pass the unpirivileged symlink flag if the node is Windows 10 Creators Update or higher" do
+ allow_any_instance_of(Chef::ReservedNames::Win32::Version).to receive(:windows_10?).and_return(true)
+ allow_any_instance_of(Chef::ReservedNames::Win32::Version).to receive(:build_number).and_return(15063)
+
+ expect(Chef::ReservedNames::Win32::File).to receive(:CreateSymbolicLinkW).with("b", "a", with_privilege)
+ described_class.symlink("a", "b")
+ end
+ end
+ end
+end
diff --git a/spec/unit/win32/security_spec.rb b/spec/unit/win32/security_spec.rb
new file mode 100644
index 0000000000..b5e441f2a0
--- /dev/null
+++ b/spec/unit/win32/security_spec.rb
@@ -0,0 +1,109 @@
+#
+# Author:: Aliasgar Batterywala (aliasgar.batterywala@msystechnologies.com)
+# Copyright:: Copyright 2017, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+if Chef::Platform.windows?
+ require "chef/win32/error"
+ require "chef/win32/security"
+ require "chef/win32/api/error"
+end
+
+describe "Chef::Win32::Security", :windows_only do
+ describe "self.get_named_security_info" do
+ context "when HR result is ERROR_SUCCESS" do
+ it "does not raise the exception" do
+ expect(Chef::ReservedNames::Win32::Security).to receive(:GetNamedSecurityInfoW).and_return(
+ Chef::ReservedNames::Win32::API::Error::ERROR_SUCCESS
+ )
+ expect { Chef::ReservedNames::Win32::Security.get_named_security_info "/temp_path" }.to_not raise_error
+ end
+ end
+
+ context "when HR result is not ERROR_SUCCESS and not ERROR_USER_NOT_FOUND" do
+ it "raises Win32APIError exception" do
+ expect(Chef::ReservedNames::Win32::Security).to receive(:GetNamedSecurityInfoW).and_return(
+ Chef::ReservedNames::Win32::API::Error::ERROR_INVALID_ACCESS
+ )
+ expect { Chef::ReservedNames::Win32::Security.get_named_security_info "/temp_path" }.to raise_error Chef::Exceptions::Win32APIError
+ end
+ end
+ end
+
+ describe "self.set_named_security_info" do
+ context "when HR result is ERROR_SUCCESS" do
+ it "does not raise the exception" do
+ expect(Chef::ReservedNames::Win32::Security).to receive(:SetNamedSecurityInfoW).and_return(
+ Chef::ReservedNames::Win32::API::Error::ERROR_SUCCESS
+ )
+ expect { Chef::ReservedNames::Win32::Security.set_named_security_info "/temp_path", :SE_FILE_OBJECT, {} }.to_not raise_error
+ end
+ end
+
+ context "when HR result is not ERROR_SUCCESS but it is ERROR_USER_NOT_FOUND" do
+ it "raises UserIDNotFound exception" do
+ expect(Chef::ReservedNames::Win32::Security).to receive(:SetNamedSecurityInfoW).and_return(
+ Chef::ReservedNames::Win32::API::Error::ERROR_USER_NOT_FOUND
+ )
+ expect { Chef::ReservedNames::Win32::Security.set_named_security_info "/temp_path", :SE_FILE_OBJECT, {} }.to raise_error Chef::Exceptions::UserIDNotFound
+ end
+ end
+ end
+
+ describe "self.has_admin_privileges?" do
+ context "when the user doesn't have admin privileges" do
+ it "returns false" do
+ allow(Chef::ReservedNames::Win32::Security).to receive(:open_current_process_token).and_raise("Access is denied.")
+ expect(Chef::ReservedNames::Win32::Security.has_admin_privileges?).to be false
+ end
+ end
+
+ context "when open_current_process_token fails with some other error than `Access is Denied`" do
+ it "raises error" do
+ allow(Chef::ReservedNames::Win32::Security).to receive(:open_current_process_token).and_raise("boom")
+ expect { Chef::ReservedNames::Win32::Security.has_admin_privileges? }.to raise_error(Chef::Exceptions::Win32APIError)
+ end
+ end
+
+ context "when the user has admin privileges" do
+ it "returns true" do
+ allow(Chef::ReservedNames::Win32::Security).to receive(:open_current_process_token)
+ token = Chef::ReservedNames::Win32::Security.open_current_process_token
+ allow(token).to receive_message_chain(:handle, :handle)
+ allow(Chef::ReservedNames::Win32::Security).to receive(:get_token_information_elevation_type)
+ allow(Chef::ReservedNames::Win32::Security).to receive(:GetTokenInformation).and_return(true)
+ allow_any_instance_of(FFI::Buffer).to receive(:read_ulong).and_return(1)
+ expect(Chef::ReservedNames::Win32::Security.has_admin_privileges?).to be true
+ end
+ end
+ end
+
+ describe "self.get_token_information_elevation_type" do
+ let(:token_rights) { Chef::ReservedNames::Win32::Security::TOKEN_READ }
+
+ let(:token) do
+ Chef::ReservedNames::Win32::Security.open_process_token(
+ Chef::ReservedNames::Win32::Process.get_current_process,
+ token_rights)
+ end
+
+ it "raises error if GetTokenInformation fails" do
+ allow(Chef::ReservedNames::Win32::Security).to receive(:GetTokenInformation).and_return(false)
+ expect { Chef::ReservedNames::Win32::Security.get_token_information_elevation_type(token) }.to raise_error(Chef::Exceptions::Win32APIError)
+ end
+ end
+end
diff --git a/spec/unit/windows_service_spec.rb b/spec/unit/windows_service_spec.rb
index 3555b70f1b..71e01a0b6b 100644
--- a/spec/unit/windows_service_spec.rb
+++ b/spec/unit/windows_service_spec.rb
@@ -38,10 +38,12 @@ describe "Chef::Application::WindowsService", :windows_only do
end
before do
+ monologger = instance_double("MonoLogger", :level= => nil, :add => nil, :formatter= => nil, :formatter => nil)
+ allow(MonoLogger).to receive(:new).and_return(monologger)
+
Chef::Config.merge!(config_options)
allow(subject).to receive(:configure_chef)
allow(subject).to receive(:parse_options)
- allow(MonoLogger).to receive(:new)
allow(subject).to receive(:running?).and_return(true, false)
allow(subject).to receive(:state).and_return(4)
subject.service_init
@@ -98,7 +100,7 @@ describe "Chef::Application::WindowsService", :windows_only do
end
end
- context "configueres a watchdog timeout" do
+ context "configures a watchdog timeout" do
let(:timeout) { 10 }
before do
diff --git a/tasks/announce.rb b/tasks/announce.rb
new file mode 100644
index 0000000000..0399137eb0
--- /dev/null
+++ b/tasks/announce.rb
@@ -0,0 +1,58 @@
+#
+# Copyright:: Copyright (c) 2016 Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "date"
+require "erb"
+
+class ReleaseAnnouncement
+ include ERB::Util
+ attr_accessor :type, :version, :maj_minor, :date, :release_notes
+
+ def initialize(version, date, type)
+ @version = version
+ @maj_minor = version.split(".")[0..1].join(".")
+ @date = Date.parse(date) unless date.nil?
+ @release_notes = release_notes_from_file
+ @type = type
+ end
+
+ def render
+ puts "-" * 30
+ puts ERB.new(template_for(@type)).result(binding)
+ puts "-" * 30
+ end
+
+ def template_for(type)
+ File.read("tasks/templates/#{type}.md.erb")
+ end
+
+ def release_notes_from_file
+ File.read("RELEASE_NOTES.md").match(/^# Chef Client Release Notes #{@maj_minor}:\n\n(.*)/m)[1]
+ end
+end
+
+namespace :announce do
+ desc "Generate the Prerelease Announcement (version: X.Y.Z, release_date: YYYY-MM-DD)"
+ task :prerelease, :version, :release_date do |t, args|
+ ReleaseAnnouncement.new(args[:version], args[:release_date], "prerelease").render
+ end
+
+ desc "Generate the Release Announcement (version: X.Y.Z)"
+ task :release, :version do |t, args|
+ ReleaseAnnouncement.new(args[:version], nil, "release").render
+ end
+end
diff --git a/tasks/bin/bundle-platform b/tasks/bin/bundle-platform
deleted file mode 100755
index 7c77393cb1..0000000000
--- a/tasks/bin/bundle-platform
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env ruby
-
-platforms = ARGV.shift
-old_platforms = Gem.platforms
-Gem.platforms = platforms.split(" ").map { |p| Gem::Platform.new(p) }
-puts "bundle-platform set Gem.platforms to #{Gem.platforms.map { |p| p.to_s }} (was #{old_platforms.map { |p| p.to_s } })"
-
-# The rest of this is a normal bundler binstub
-require "pathname"
-ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile",
- Pathname.new(__FILE__).realpath)
-
-require "rubygems"
-
-load Gem.bin_path("bundler", "bundle")
diff --git a/tasks/bin/bundle-platform.bat b/tasks/bin/bundle-platform.bat
deleted file mode 100644
index d193eb0c05..0000000000
--- a/tasks/bin/bundle-platform.bat
+++ /dev/null
@@ -1,2 +0,0 @@
-@ECHO OFF
-ruby "%~dpn0" %*
diff --git a/tasks/bin/create-override-gemfile b/tasks/bin/create-override-gemfile
deleted file mode 100755
index b67da025d2..0000000000
--- a/tasks/bin/create-override-gemfile
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/usr/bin/env ruby
-
-require "rubygems"
-require "bundler"
-
-Bundler.with_clean_env do
- require_relative "../gemfile_util"
-
- options = {}
- opts = OptionParser.new do |opts|
- opts.banner = "Usage: create-override-gemfile [OPTIONS]"
-
- opts.on("--gemfile GEMFILE", "The Gemfile to read (default: Gemfile).") { |path| options[:gemfile_path] = path }
- opts.on("--lockfile GEMFILE", "The lockfile to read (default: <gemfile>.lock).") { |path| options[:lockfile_path] = path }
-
- opts.on("--group GROUP", "Groups to include (whitelist).") do |group|
- options[:groups] ||= []
- options[:groups] << group.to_sym
- end
-
- opts.on("--without GROUP", "Groups to exclude.") do |group|
- options[:without_groups] ||= []
- options[:without_groups] << group.to_sym
- end
-
- opts.on("--gem GEM", "Gems to include regardless of groups.") do |name|
- options[:gems] ||= []
- options[:gems] << name
- end
-
- opts.on("--relative-to PATH", "A path to prepend to any relative paths in the Gemfile.") do |path|
- unless Pathname.new(path).absolute?
- puts opts
- raise "--relative-to #{path} was not an absolute path!"
- end
- options[:relative_to] = path
- end
-
- opts.on("--[no-]copy-groups", "Whether to copy groups over from the original Gemfile or not (default: false).") { |val| options[:copy_groups] = val }
-
- opts.on("--[no-]override", "Whether to emit override: true on each gem line (default: false).") { |val| options[:override] = val }
-
- opts.on("-h", "--help", "Print this message.") do
- puts opts
- exit(0)
- end
- end
-
- args = opts.parse(ARGV)
-
- if args.size > 0
- puts opts
- raise "Invalid arguments #{args}"
- end
-
- def create_override_gemfile(gemfile_path: "Gemfile", lockfile_path: "#{gemfile_path}.lock", groups: nil, without_groups: nil, gems: [], copy_groups: false, relative_to: ".", override: false)
- relative_to = Pathname.new(relative_to).realpath
- # Select the gems we want
- bundle = GemfileUtil::Bundle.parse(gemfile_path, lockfile_path)
- gems_to_include = bundle.select_gems(groups: groups, without_groups: without_groups)
- gems.each do |name|
- raise "Requested gem #{name} is not in #{gemfile_path}.lock!" if !bundle.gems[name]
- gems_to_include[name] ||= bundle.gems[name]
- gems_to_include[name][:dependencies].each do |dep|
- gems_to_include[name] ||= bundle.gems[dep]
- end
- end
-
- # Add the gems to the Gemfile
- gem_root = Pathname.new(gemfile_path).dirname.realpath
- gems_to_include.sort_by { |name, options| options[:declared_groups].empty? ? 1 : 0 }.each do |name, options|
- comment = nil
- options = options.dup
- version = options.delete(:version)
- if copy_groups
- # Some dependencies have no groups (are not in the Gemfile--just runtime
- # dependencies). If we actually record that they have no groups, they
- # will *always* be installed (or perhaps never). We only want them to
- # install if their other deps do, so we mark them with the groups of the
- # things that brought them in (the gems that depended on them). To do
- # this, we just leave :groups intact.
- if options[:declared_groups].empty?
- options.delete(:declared_groups)
- comment = " # Transitive dependency, not actually in original Gemfile"
- else
- # For other things, we want to copy the actual declared_groups--the
- # ones that were in the Gemfile. We want the same --with and --without
- # options to include and exclude them as worked with the original
- # Gemfile.
- options[:groups] = options.delete(:declared_groups)
- end
- else
- options.delete(:groups)
- options.delete(:declared_groups)
- end
- options.delete(:dependencies)
- options.delete(:development_dependencies)
- options[:override] = true if override
- options[:path] = Pathname.new(options[:path]).expand_path(gem_root).relative_path_from(relative_to).to_s if options[:path]
- line = "gem #{name.inspect}, #{version.inspect}"
- options.each do |name, value|
- line << ", #{name}: #{value.inspect}"
- end
- line << comment if comment
- puts line
- end
- end
-
- create_override_gemfile(options)
-end
diff --git a/tasks/bin/run_chef_tests b/tasks/bin/run_chef_tests
deleted file mode 100755
index a128e59aec..0000000000
--- a/tasks/bin/run_chef_tests
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-
-# Fail fast (e) and echo commands (vx)
-set -evx
-
-echo --color > .rspec
-echo -fp >> .rspec
-
-sudo sed -i -e 's/^Defaults\tsecure_path.*$//' /etc/sudoers;
-# If we have any args, just run the given command
-if [ -n "$1" ]; then
- sudo -E $(which bundle) exec $@;
-else
- sudo -E $(which bundle) exec rake spec;
- bundle exec bundle-audit check --update;
-fi
diff --git a/tasks/bin/run_external_test b/tasks/bin/run_external_test
index 74f76d3229..01eba18943 100755
--- a/tasks/bin/run_external_test
+++ b/tasks/bin/run_external_test
@@ -1,47 +1,29 @@
-#!/bin/bash
+#!/usr/bin/env ruby
-# Fail fast (e) and echo commands (vx)
-set -evx
+$:.unshift(File.expand_path("../../lib", File.dirname(__FILE__)))
-# Arguments
-TEST_GEM=$1
-shift
+require "tmpdir"
+require "bundler"
+require "chef/mixin/shell_out"
-PROJECT_ROOT=$(pwd)
-PROJECT_BUNDLE_PATH=${BUNDLE_PATH:-$(grep BUNDLE_PATH: $PROJECT_ROOT/.bundle/config | cut -d' ' -f2-)}
-if [ -n "$PROJECT_BUNDLE_PATH" ]; then
- PROJECT_BUNDLE_PATH=$PROJECT_ROOT/$PROJECT_BUNDLE_PATH
-fi
+include Chef::Mixin::ShellOut
-TEST_GEM_ROOT=$(bundle show $TEST_GEM)
+github_repo = ARGV.shift
+git_thing = ARGV.shift
-# Make a copy of the original Gemfile and stitch in our Gemfile.lock
-TEST_GEMFILE=$TEST_GEM_ROOT/Gemfile
-MODIFIED_TEST_GEMFILE=$TEST_GEMFILE.externaltest
-cat <<EOM > $MODIFIED_TEST_GEMFILE
-require_relative "$PROJECT_ROOT/tasks/gemfile_util"
-GemfileUtil.include_locked_gemfile(self, "$PROJECT_ROOT/Gemfile", gems: ["$TEST_GEM"] + "$TEST_WITH_GEMS".split(/\s+/))
-$TEST_GEM_OVERRIDES
-EOM
-cat $TEST_GEMFILE >> $MODIFIED_TEST_GEMFILE
-if [ -f $TEST_GEMFILE.lock ]; then
- cp $TEST_GEMFILE.lock $MODIFIED_TEST_GEMFILE.lock
-elif [ -f $MODIFIED_TEST_GEMFILE.lock ]; then
- rm -f $MODIFIED_TEST_GEMFILE.lock
-fi
+build_dir = File.expand_path(ENV["TRAVIS_BUILD_DIR"] || Dir.pwd)
-# Run the bundle install
-cd $TEST_GEM_ROOT
-export BUNDLE_GEMFILE=$MODIFIED_TEST_GEMFILE
-# Don't read from the project .bundle/config, just our env vars
-export BUNDLE_IGNORE_CONFIG=true
-# Use the top level bundle cache so we don't have to reinstall their packages
-if [ -n "$PROJECT_BUNDLE_PATH" ]; then
- export BUNDLE_PATH=$PROJECT_BUNDLE_PATH
-fi
-export BUNDLE_FROZEN=
-bundle install
-export BUNDLE_FROZEN=true
+env = { "GEMFILE_MOD" => "gem 'chef', path: '#{build_dir}'" }
-bundle config
-bundle exec $@
+Dir.mktmpdir("chef-external-test") do |dir|
+ git_url = "https://github.com/#{github_repo}"
+ Dir.rmdir dir
+ shell_out!("git clone #{git_url} #{dir}", live_stream: STDOUT)
+ Dir.chdir(dir) do
+ shell_out!("git checkout #{git_thing}", live_stream: STDOUT)
+ Bundler.with_clean_env do
+ shell_out!("bundle install", live_stream: STDOUT, env: env)
+ shell_out!("bundle exec #{ARGV.join(" ")}", live_stream: STDOUT, env: env)
+ end
+ end
+end
diff --git a/tasks/bundle.rb b/tasks/bundle.rb
deleted file mode 100644
index 059b926f14..0000000000
--- a/tasks/bundle.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-#
-# Copyright:: Copyright (c) 2016 Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require_relative "bundle_util"
-require_relative "../version_policy"
-require "fileutils"
-
-desc "Tasks to work with the main Gemfile and Gemfile.<platform>"
-namespace :bundle do
- desc "Update Gemfile.lock and all Gemfile.<platform>.locks (or one or more gems via bundle:update gem1 gem2 ...)."
- task :update, [:args] do |t, rake_args|
- extend BundleUtil
- args = rake_args[:args] || ""
- with_bundle_unfrozen do
- puts ""
- puts "-------------------------------------------------------------------"
- puts "Updating Gemfile.lock ..."
- puts "-------------------------------------------------------------------"
- bundle "install #{args}", delete_gemfile_lock: true
- platforms.each do |platform|
- bundle "lock", platform: platform
- end
- end
- end
-
- desc "Conservatively update Gemfile.lock and all Gemfile.<platform>.locks"
- task :install, [:args] do |t, rake_args|
- extend BundleUtil
- args = rake_args[:args] || ""
- with_bundle_unfrozen do
- puts ""
- puts "-------------------------------------------------------------------"
- puts "Updating Gemfile.lock ..."
- puts "-------------------------------------------------------------------"
- bundle "install #{args}"
- platforms.each do |platform|
- bundle "lock", platform: platform
- end
- end
- end
-
- # Find out if we're using the latest gems we can (so we don't regress versions)
- desc "Check for gems that are not at the latest released version, and report if anything not in ACCEPTABLE_OUTDATED_GEMS (version_policy.rb) is out of date."
- task :outdated do
- extend BundleUtil
- puts ""
- puts "-------------------------------------------------------------------"
- puts "Checking for outdated gems ..."
- puts "-------------------------------------------------------------------"
- # TODO check for outdated windows gems too
- with_bundle_unfrozen do
- bundle_outdated = bundle("outdated", extract_output: true)
- puts bundle_outdated
- outdated_gems = parse_bundle_outdated(bundle_outdated).map { |line, gem_name| gem_name }
- # Weed out the acceptable ones
- outdated_gems = outdated_gems.reject { |gem_name| ACCEPTABLE_OUTDATED_GEMS.include?(gem_name) }
- if outdated_gems.empty?
- puts ""
- puts "SUCCESS!"
- else
- raise "ERROR: outdated gems: #{outdated_gems.join(", ")}. Either fix them or add them to ACCEPTABLE_OUTDATED_GEMS in #{__FILE__}."
- end
- end
- end
-end
-
-desc "Run bundle with arbitrary args against the given platform; e.g. rake bundle[show]. No platform to run against the main bundle; bundle[show,windows] to run the windows one; bundle[show,*] to run against all non-default platforms."
-task :bundle, [:args, :platform] do |t, rake_args|
- extend BundleUtil
- args = rake_args[:args] || ""
- platform = rake_args[:platform]
- with_bundle_unfrozen do
- if platform == "*"
- platforms.each do |platform|
- bundle args, platform: platform
- end
- elsif platform
- bundle args, platform: platform
- else
- bundle args
- end
- end
-end
diff --git a/tasks/bundle_util.rb b/tasks/bundle_util.rb
deleted file mode 100644
index a057db858a..0000000000
--- a/tasks/bundle_util.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-require "bundler"
-require "shellwords"
-
-module BundleUtil
- PLATFORMS = { "windows" => %w{ruby x86-mingw32} }
-
- def project_root
- File.expand_path("../..", __FILE__)
- end
-
- def bundle_platform
- File.join(project_root, "tasks", "bin", "bundle-platform")
- end
-
- # Parse the output of "bundle outdated" and get the list of gems that
- # were outdated
- def parse_bundle_outdated(bundle_outdated_output)
- result = []
- bundle_outdated_output.each_line do |line|
- if line =~ /^\s*\* (.+) \(newest ([^,]+), installed ([^,)])*/
- gem_name, newest_version, installed_version = $1, $2, $3
- result << [ line, gem_name ]
- end
- end
- result
- end
-
- def with_bundle_unfrozen(cwd: nil, leave_frozen: false)
- bundle "config --delete frozen", cwd: cwd
- begin
- yield
- ensure
- bundle "config --local frozen 1", cwd: cwd unless leave_frozen
- end
- end
-
- # Run bundle-platform with the given ruby platform(s)
- def bundle(args, gemfile: nil, platform: nil, cwd: nil, extract_output: false, delete_gemfile_lock: false)
- args = args.split(/\s+/)
- if cwd
- prefix = "[#{cwd}] "
- end
- cwd = File.expand_path(cwd || ".", project_root)
- Bundler.with_clean_env do
- Dir.chdir(cwd) do
- gemfile ||= "Gemfile"
- gemfile = File.expand_path(gemfile, cwd)
- raise "No platform #{platform} (supported: #{PLATFORMS.keys.join(", ")})" if platform && !PLATFORMS[platform]
-
- # First delete the gemfile.lock
- if delete_gemfile_lock
- if File.exist?("#{gemfile}.lock")
- puts "Deleting #{gemfile}.lock ..."
- File.delete("#{gemfile}.lock")
- end
- end
-
- # Run the bundle command
- ruby_platforms = platform ? PLATFORMS[platform].join(" ") : "ruby"
- cmd = Shellwords.join([Gem.ruby, "-S", bundle_platform, ruby_platforms, *args])
- puts "#{prefix}#{Shellwords.join(["bundle", *args])}#{platform ? " for #{platform} platform" : ""}:"
- with_gemfile(gemfile) do
- puts "#{prefix}BUNDLE_GEMFILE=#{gemfile}"
- puts "#{prefix}> #{cmd}"
- if extract_output
- `#{cmd}`
- else
- unless system(bundle_platform, ruby_platforms, *args)
- raise "#{bundle_platform} failed: exit code #{$?}"
- end
- end
- end
- end
- end
- end
-
- def with_gemfile(gemfile)
- old_gemfile = ENV["BUNDLE_GEMFILE"]
- ENV["BUNDLE_GEMFILE"] = gemfile
- begin
- yield
- ensure
- if old_gemfile
- ENV["BUNDLE_GEMFILE"] = old_gemfile
- else
- ENV.delete("BUNDLE_GEMFILE")
- end
- end
- end
-
- def platforms
- PLATFORMS.keys
- end
-end
diff --git a/tasks/cbgb.rb b/tasks/cbgb.rb
index 9aa6700137..09363b42d2 100644
--- a/tasks/cbgb.rb
+++ b/tasks/cbgb.rb
@@ -44,15 +44,15 @@ begin
out << cbgb(cbgb["corporations"], cbgb["Org"]["Corporate-Contributors"]["governers"]) + "\n\n"
out << "### " + cbgb["Org"]["Lieutenants"]["title"] + "\n\n"
out << cbgb(cbgb["people"], cbgb["Org"]["Lieutenants"]["governers"]) + "\n\n"
- File.open(CBGB_TARGET, "w") { |fn|
+ File.open(CBGB_TARGET, "w") do |fn|
fn.write out
- }
+ end
end
end
def components(list, cmp)
out = ""
- cmp.each do |k, v|
+ cmp.each_value do |v|
out << "\n#### #{v['title'].gsub('#', '\\#')}\n"
out << cbgb(list, v["cbgb"])
end
diff --git a/tasks/changelog.rb b/tasks/changelog.rb
deleted file mode 100644
index fda94764ae..0000000000
--- a/tasks/changelog.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-begin
- require "github_changelog_generator/task"
-
- GitHubChangelogGenerator::RakeTask.new :changelog do |config|
- config.future_release = Chef::VERSION
- config.enhancement_labels = "enhancement,Enhancement,New Feature,Feature".split(",")
- config.bug_labels = "bug,Bug,Improvement,Upstream Bug".split(",")
- config.exclude_labels = "duplicate,question,invalid,wontfix,no_changelog,Exclude From Changelog,Question,Discussion".split(",")
- end
-rescue LoadError
- puts "github_changelog_generator is not available. gem install github_changelog_generator to generate changelogs"
-end
diff --git a/tasks/dependencies.rb b/tasks/dependencies.rb
index 2faf3b883a..a66e166d92 100644
--- a/tasks/dependencies.rb
+++ b/tasks/dependencies.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright (c) 2016 Chef Software Inc.
+# Copyright:: Copyright (c) 2016-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,133 +15,80 @@
# limitations under the License.
#
-require_relative "bundle_util"
-require_relative "bundle"
-require_relative "../version_policy"
+require "bundler"
desc "Tasks to update and check dependencies"
namespace :dependencies do
+
+ # Running update_ci on your local system wont' work. The best way to update
+ # dependencies locally is by running the dependency update script.
+ desc "Update all dependencies. dependencies:update to update as little as possible."
+ task :update do |t, rake_args|
+ # FIXME: probably broken, and needs less indirection
+ system("#{File.join(Dir.pwd, "ci", "dependency_update.sh")}")
+ end
+
+ desc "Force update (when adding new gems to Gemfiles)"
+ task :force_update do |t, rake_args|
+ # FIXME: probably broken, and needs less indirection
+ FileUtils.rm_f(File.join(Dir.pwd, ".bundle", "config"))
+ system("#{File.join(Dir.pwd, "ci", "dependency_update.sh")}")
+ end
+
# Update all dependencies to the latest constraint-matching version
- desc "Update all dependencies. dependencies:update[conservative] to update as little as possible."
- task :update, [:conservative] => %w{
+ desc "Update all dependencies. dependencies:update to update as little as possible (CI-only)."
+ task :update_ci => %w{
dependencies:update_gemfile_lock
- dependencies:update_omnibus_overrides
dependencies:update_omnibus_gemfile_lock
dependencies:update_acceptance_gemfile_lock
- dependencies:update_kitchen_tests_gemfile_lock
- dependencies:update_kitchen_tests_berksfile_lock
+ dependencies:update_audit_tests_berksfile_lock
}
- desc "Update Gemfile.lock and all Gemfile.<platform>.locks. update_gemfile_lock[conservative] to update as little as possible."
- task :update_gemfile_lock, [:conservative] do |t, rake_args|
- conservative = rake_args[:conservative]
- if conservative
- Rake::Task["bundle:install"].invoke
- else
- Rake::Task["bundle:update"].invoke
- end
- end
-
- def gemfile_lock_task(task_name, dirs: [], other_platforms: true, leave_frozen: true)
- dirs.each do |dir|
- desc "Update #{dir}/Gemfile.lock. #{task_name}[conservative] to update as little as possible."
- task task_name, [:conservative] do |t, rake_args|
- extend BundleUtil
- conservative = rake_args[:conservative]
- puts ""
- puts "-------------------------------------------------------------------"
- puts "Updating #{dir}/Gemfile.lock#{conservative ? " (conservatively)" : ""} ..."
- puts "-------------------------------------------------------------------"
- with_bundle_unfrozen(cwd: dir, leave_frozen: leave_frozen) do
- bundle "install", cwd: dir, delete_gemfile_lock: !conservative
- if other_platforms
- # Include all other supported platforms into the lockfile as well
- platforms.each do |platform|
- bundle "lock", cwd: dir, platform: platform
- end
- end
+ def bundle_update_locked_multiplatform_task(task_name, dir)
+ desc "Update #{dir}/Gemfile.lock."
+ task task_name do
+ Dir.chdir(dir) do
+ Bundler.with_clean_env do
+ rm_f "#{dir}/Gemfile.lock"
+ sh "bundle lock --update --add-platform ruby"
+ sh "bundle lock --update --add-platform x64-mingw32"
+ sh "bundle lock --update --add-platform x86-mingw32"
end
end
end
end
- def berksfile_lock_task(task_name, dirs: [])
- dirs.each do |dir|
- desc "Update #{dir}/Berksfile.lock. #{task_name}[conservative] to update as little as possible."
- task task_name, [:conservative] do |t, rake_args|
- extend BundleUtil
- conservative = rake_args[:conservative]
- puts ""
- puts "-------------------------------------------------------------------"
- puts "Updating #{dir}/Berksfile.lock#{conservative ? " (conservatively)" : ""} ..."
- puts "-------------------------------------------------------------------"
- if !conservative && File.exist?("#{project_root}/#{dir}/Berksfile.lock")
- File.delete("#{project_root}/#{dir}/Berksfile.lock")
- end
- Dir.chdir("#{project_root}/#{dir}") do
- Bundler.with_clean_env do
- sh "bundle exec berks install"
- end
+ def bundle_update_task(task_name, dir)
+ desc "Update #{dir}/Gemfile.lock."
+ task task_name do
+ Dir.chdir(dir) do
+ Bundler.with_clean_env do
+ sh "bundle update"
end
end
end
end
- gemfile_lock_task :update_omnibus_gemfile_lock, dirs: %w{omnibus}
- gemfile_lock_task :update_acceptance_gemfile_lock, dirs: %w{acceptance},
- other_platforms: false, leave_frozen: false
- gemfile_lock_task :update_kitchen_tests_gemfile_lock, dirs: %w{
- kitchen-tests
- kitchen-tests/test/integration/webapp/serverspec
- }
- berksfile_lock_task :update_kitchen_tests_berksfile_lock, dirs: %w{
- kitchen-tests
- kitchen-tests/cookbooks/audit_test
- }
- # kitchen-tests/cookbooks/webapp isn't solving right now ....
-
- desc "Update omnibus overrides, including versions in version_policy.rb and latest version of gems: #{OMNIBUS_RUBYGEMS_AT_LATEST_VERSION.keys}. update_omnibus_overrides[conservative] does nothing."
- task :update_omnibus_overrides, [:conservative] do |t, rake_args|
- conservative = rake_args[:conservative]
- unless conservative
- puts ""
- puts "-------------------------------------------------------------------"
- puts "Updating omnibus_overrides.rb ..."
- puts "-------------------------------------------------------------------"
-
- # Generate the new overrides file
- overrides = "# DO NOT EDIT. Generated by \"rake dependencies\". Edit version_policy.rb instead.\n"
-
- # Replace the bundler and rubygems versions
- OMNIBUS_RUBYGEMS_AT_LATEST_VERSION.each do |override_name, gem_name|
- # Get the latest bundler version
- puts "Running gem list -r #{gem_name} ..."
- gem_list = `gem list -r #{gem_name}`
- unless gem_list =~ /^#{gem_name}\s*\(([^)]*)\)$/
- raise "gem list -r #{gem_name} failed with output:\n#{gem_list}"
+ def berks_update_task(task_name, dir)
+ desc "Update #{dir}/Berksfile.lock."
+ task task_name do
+ FileUtils.rm_f("#{dir}/Berksfile.lock")
+ Dir.chdir(dir) do
+ Bundler.with_clean_env do
+ sh "bundle exec berks install"
end
-
- # Emit it
- puts "Latest version of #{gem_name} is #{$1}"
- overrides << "override #{override_name.inspect}, version: #{$1.inspect}\n"
- end
-
- # Add explicit overrides
- OMNIBUS_OVERRIDES.each do |override_name, version|
- overrides << "override #{override_name.inspect}, version: #{version.inspect}\n"
- end
-
- # Write the file out (if changed)
- overrides_path = File.expand_path("../../omnibus_overrides.rb", __FILE__)
- if overrides != IO.read(overrides_path)
- puts "Overrides changed!"
- puts `git diff #{overrides_path}`
- puts "Writing modified #{overrides_path} ..."
- IO.write(overrides_path, overrides)
end
end
end
+
+ bundle_update_locked_multiplatform_task :update_gemfile_lock, "."
+ bundle_update_locked_multiplatform_task :update_omnibus_gemfile_lock, "omnibus"
+ bundle_update_task :update_acceptance_gemfile_lock, "acceptance"
+ berks_update_task :update_audit_tests_berksfile_lock, "kitchen-tests/cookbooks/audit_test"
+
end
-desc "Update all dependencies and check for outdated gems. Call dependencies[conservative] to update as little as possible."
-task :dependencies, [:conservative] => [ "dependencies:update", "bundle:outdated" ]
-task :update, [:conservative] => [ "dependencies:update", "bundle:outdated"]
+
+desc "Update all dependencies and check for outdated gems."
+task :dependencies_ci => [ "dependencies:update_ci" ]
+task :dependencies => [ "dependencies:update" ]
+task :update => [ "dependencies:update" ]
diff --git a/tasks/gemfile_util.rb b/tasks/gemfile_util.rb
deleted file mode 100644
index e21299705a..0000000000
--- a/tasks/gemfile_util.rb
+++ /dev/null
@@ -1,390 +0,0 @@
-require "rubygems"
-require "bundler"
-require "shellwords"
-require "set"
-
-module GemfileUtil
- #
- # Adds `override: true`, which allows your statement to override any other
- # gem statement about the same gem in the Gemfile.
- #
- def gem(name, *args)
- options = args[-1].is_a?(Hash) ? args[-1] : {}
-
- # Unless we're finished with everything, ignore gems that are being overridden
- unless overridden_gems == :finished
- # If it's a path or override gem, it overrides whatever else is there.
- if options[:path] || options[:override]
- options.delete(:override)
- warn_if_replacing(name, overridden_gems[name], args)
- overridden_gems[name] = args
- return
-
- # If there's an override gem, and we're *not* an override gem, don't do anything
- elsif overridden_gems[name]
- warn_if_replacing(name, args, overridden_gems[name])
- return
- end
- end
-
- # Otherwise, add the gem normally
- super
- rescue
- puts $!.backtrace
- raise
- end
-
- def overridden_gems
- @overridden_gems ||= {}
- end
-
- #
- # Just before we finish the Gemfile, finish up the override gems
- #
- def to_definition(*args)
- complete_overrides
- super
- end
-
- def complete_overrides
- to_override = overridden_gems
- unless to_override == :finished
- @overridden_gems = :finished
- to_override.each do |name, args|
- gem name, *args
- end
- end
- end
-
- #
- # Include all gems in the locked gemfile.
- #
- # @param gemfile_path Path to the Gemfile to load (relative to your Gemfile)
- # @param lockfile_path Path to the Gemfile to load (relative to your Gemfile).
- # Defaults to <gemfile_path>.lock.
- # @param groups A list of groups to include (whitelist). If not passed (or set
- # to nil), all gems will be selected.
- # @param without_groups A list of groups to ignore. Gems will be excluded from
- # the results if all groups they belong to are ignored. This matches
- # bundler's `without` behavior.
- # @param gems A list of gems to include above and beyond the given groups.
- # Gems in this list must be explicitly included in the Gemfile
- # with a `gem "gem_name", ...` line or they will be silently
- # ignored.
- # @param copy_groups Whether to copy the groups over from the old lockfile to
- # the new. Use this when the new lockfile has the same convention for
- # groups as the old. Defaults to `false`.
- #
- def include_locked_gemfile(gemfile_path, lockfile_path = "#{gemfile_path}.lock", groups: nil, without_groups: nil, gems: [], copy_groups: false)
- # Parse the desired lockfile
- gemfile_path = Pathname.new(gemfile_path).expand_path(Bundler.default_gemfile.dirname).realpath
- lockfile_path = Pathname.new(lockfile_path).expand_path(Bundler.default_gemfile.dirname).realpath
-
- # Calculate relative_to
- relative_to = Bundler.default_gemfile.dirname.realpath
-
- # Call out to create-override-gemfile to read the Gemfile+Gemfile.lock (bundler does not work well if you do two things in one process)
- create_override_gemfile_bin = File.expand_path("../bin/create-override-gemfile", __FILE__)
- arguments = [
- "--gemfile", gemfile_path,
- "--lockfile", lockfile_path,
- "--override"
- ]
- arguments += [ "--relative-to", relative_to ] if relative_to != "."
- arguments += Array(groups).flat_map { |group| [ "--group", group ] }
- arguments += Array(without_groups).flat_map { |without| [ "--without", without ] }
- arguments += Array(gems).flat_map { |name| [ "--gem", name ] }
- arguments << "--copy-groups" if copy_groups
- cmd = Shellwords.join([ Gem.ruby, "-S", create_override_gemfile_bin, *arguments ])
- output = nil
- Bundler.ui.info("> #{cmd}")
- Bundler.with_clean_env do
- output = `#{cmd}`
- end
- instance_eval(output, cmd, 1)
- end
-
- #
- # Include all gems in the locked gemfile.
- #
- # @param current_gemfile The Gemfile you are currently loading (`self`).
- # @param gemfile_path Path to the Gemfile to load (relative to your Gemfile)
- # @param lockfile_path Path to the Gemfile to load (relative to your Gemfile).
- # Defaults to <gemfile_path>.lock.
- # @param groups A list of groups to include (whitelist). If not passed (or set
- # to nil), all gems will be selected.
- # @param without_groups A list of groups to ignore. Gems will be excluded from
- # the results if all groups they belong to are ignored. This matches
- # bundler's `without` behavior.
- # @param gems A list of gems to include above and beyond the given groups.
- # Gems in this list must be explicitly included in the Gemfile
- # with a `gem "gem_name", ...` line or they will be silently
- # ignored.
- # @param copy_groups Whether to copy the groups over from the old lockfile to
- # the new. Use this when the new lockfile has the same convention for
- # groups as the old. Defaults to `false`.
- #
- def self.include_locked_gemfile(current_gemfile, gemfile_path, lockfile_path = "#{gemfile_path}.lock", groups: nil, without_groups: nil, gems: [], copy_groups: false)
- current_gemfile.instance_eval do
- extend GemfileUtil
- include_locked_gemfile(gemfile_path, lockfile_path, groups: groups, without_groups: without_groups, gems: gems, copy_groups: copy_groups)
- end
- end
-
- def warn_if_replacing(name, old_args, new_args)
- return if !old_args || !new_args
- if args_to_dep(name, *old_args) =~ args_to_dep(name, *new_args)
- Bundler.ui.debug "Replaced Gemfile dependency #{name} (#{old_args}) with (#{new_args})"
- else
- Bundler.ui.warn "Replaced Gemfile dependency #{name} (#{old_args}) with (#{new_args})"
- end
- end
-
- def args_to_dep(name, *version, **options)
- version = [">= 0"] if version.empty?
- Bundler::Dependency.new(name, version, options)
- end
-
- #
- # Reads a bundle, including a gemfile and lockfile.
- #
- # Does no validation, does not update the lockfile or its gems in any way.
- #
- class Bundle
- #
- # Parse the given gemfile/lockfile pair.
- #
- # @return [Bundle] The parsed bundle.
- #
- def self.parse(gemfile_path, lockfile_path = "#{gemfile_path}.lock")
- result = new(gemfile_path, lockfile_path)
- result.gems
- result
- end
-
- #
- # Create a new Bundle to parse the given gemfile/lockfile pair.
- #
- def initialize(gemfile_path, lockfile_path = "#{gemfile_path}.lock")
- @gemfile_path = gemfile_path
- @lockfile_path = lockfile_path
- end
-
- #
- # The path to the Gemfile
- #
- attr_reader :gemfile_path
-
- #
- # The path to the Lockfile
- #
- attr_reader :lockfile_path
-
- #
- # The list of gems.
- #
- # @return [Hash<String, Hash>] The resulting gems, where key = gem_name, and the
- # hash has:
- # - version: version of the gem.
- # - source info (:source/:git/:ref/:path) from the lockfile
- # - dependencies: A list of gem names this gem has a runtime
- # dependency on. Dependencies are transitive: if A depends on B,
- # and B depends on C, then A has C in its :dependencies list.
- # - development_dependencies: - A list of gem names this gem has a
- # development dependency on. Dependencies are transitive: if A
- # depends on B, and B depends on C, then A has C in its
- # :development_dependencies list. development dependencies *include*
- # runtime dependencies.
- # - groups: The list of groups (symbols) this gem is in. Groups
- # are transitive: if A has a runtime dependency on B, and A is
- # in group X, then B is also in group X.
- # - declared_groups: The list of groups (symbols) this gem was
- # declared in the Gemfile.
- #
- def gems
- @gems ||= begin
- gems = locks.dup
- gems.each do |name, g|
- if gem_declarations.has_key?(name)
- g[:declared_groups] = gem_declarations[name][:groups]
- else
- g[:declared_groups] = []
- end
- g[:groups] = g[:declared_groups].dup
- end
- # Transitivize groups (since dependencies are already transitive, this is easy)
- gems.each do |name, g|
- g[:dependencies].each do |dep|
- gems[dep][:groups] |= gems[name][:declared_groups].dup
- end
- end
- gems
- end
- end
-
- #
- # Get the gems (and their deps) in the given group.
- #
- # @param groups A list of groups to include (whitelist). If not passed (or set
- # to nil), all gems will be selected.
- # @param without_groups A list of groups to ignore. Gems will be excluded from
- # the results if all groups they belong to are ignored.
- # This matches bundler's `without` behavior.
- # @param gems A list of gems to include regardless of what groups are included.
- #
- # @return Hash[String, Hash] The resulting gems, where key = gem_name, and the
- # hash has:
- # - version: version of the gem.
- # - source info (:source/:git/:ref/:path) from the lockfile
- # - dependencies: A list of gem names this gem has a runtime
- # dependency on. Dependencies are transitive: if A depends on B,
- # and B depends on C, then A has C in its :dependencies list.
- # - development_dependencies: - A list of gem names this gem has a
- # development dependency on. Dependencies are transitive: if A
- # depends on B, and B depends on C, then A has C in its
- # :development_dependencies list. development dependencies
- # *include* runtime dependencies.
- # - groups: The list of groups (symbols) this gem is in. Groups
- # are transitive: if A has a runtime dependency on B, and A is
- # in group X, then B is also in group X.
- # - declared_groups: The list of groups (symbols) this gem was
- # declared in the Gemfile.
- #
- def select_gems(groups: nil, without_groups: nil)
- # First, select the gems that match
- result = {}
- gems.each do |name, g|
- dep_groups = g[:declared_groups] - [ :only_a_runtime_dependency_of_other_gems ]
- dep_groups = dep_groups & groups if groups
- dep_groups = dep_groups - without_groups if without_groups
- if dep_groups.any?
- result[name] ||= g
- g[:dependencies].each do |dep|
- result[dep] ||= gems[dep]
- end
- end
- end
- result
- end
-
- #
- # Get all locks from the given lockfile.
- #
- # @return Hash[String, Hash] The resulting gems, where key = gem_name, and the
- # hash has:
- # - version: version of the gem.
- # - source info (:source/:git/:ref/:path)
- # - dependencies: A list of gem names this gem has a runtime
- # dependency on. Dependencies are transitive: if A depends on B,
- # and B depends on C, then A has C in its :dependencies list.
- # - development_dependencies: - A list of gem names this gem has a
- # development dependency on. Dependencies are transitive: if A
- # depends on B, and B depends on C, then A has C in its
- # :development_dependencies list. development dependencies *include*
- # runtime dependencies.
- #
- def locks
- @locks ||= begin
- # Grab all the specs from the lockfile
- locks = {}
- parsed_lockfile = Bundler::LockfileParser.new(IO.read(lockfile_path))
- parsed_lockfile.specs.each do |spec|
- # Never include bundler, it can't be bundled and doesn't put itself in
- # the lockfile correctly anyway
- next if spec.name == "bundler"
- # Only the platform-specific locks for now (TODO make it possible to emit all locks)
- next if spec.platform && spec.platform != Gem::Platform::RUBY
- lock = lock_source_metadata(spec)
- lock[:version] = spec.version.to_s
- runtime = spec.dependencies.select { |dep| dep.type == :runtime }
- lock[:dependencies] = Set.new(runtime.map { |dep| dep.name })
- lock[:development_dependencies] = Set.new(spec.dependencies.map { |dep| dep.name })
- lock[:dependencies].delete("bundler")
- lock[:development_dependencies].delete("bundler")
- locks[spec.name] = lock
- end
-
- # Transitivize the deps.
- locks.each do |name, lock|
- # Not all deps were brought over (platform-specific ones) so weed them out
- lock[:dependencies] &= locks.keys
- lock[:development_dependencies] &= locks.keys
-
- lock[:dependencies] = transitive_dependencies(locks, name, :dependencies)
- lock[:development_dependencies] = transitive_dependencies(locks, name, :development_dependencies)
- end
-
- locks
- end
- end
-
- #
- # Get all desired gems, sans dependencies, from the gemfile.
- #
- # @param gemfile Path to the Gemfile to load
- #
- # @return Hash<String, Hash> An array of hashes where key = gem name and value
- # has :groups (an array of symbols representing the groups the gem
- # is in). :groups are not transitive, since we don't know the
- # dependency tree yet.
- #
- def gem_declarations
- @gem_declarations ||= begin
- Bundler.with_clean_env do
- # Set BUNDLE_GEMFILE to the new gemfile temporarily so all bundler's things work
- # This works around some issues in bundler 1.11.2.
- ENV["BUNDLE_GEMFILE"] = gemfile_path
-
- parsed_gemfile = Bundler::Dsl.new
- parsed_gemfile.eval_gemfile(gemfile_path)
- parsed_gemfile.complete_overrides if parsed_gemfile.respond_to?(:complete_overrides)
-
- result = {}
- parsed_gemfile.dependencies.each do |dep|
- groups = dep.groups.empty? ? [:default] : dep.groups
- result[dep.name] = { groups: groups, platforms: dep.platforms }
- end
- result
- end
- end
- end
-
- private
-
- #
- # Given a bunch of locks (name -> { dependencies: [name,name] }) and a
- # dependency name, add its dependencies to the result transitively.
- #
- def transitive_dependencies(locks, name, dep_key, result = Set.new)
- locks[name][dep_key].each do |dep|
- # Only ever add a dep once, so we don't infinitely recurse
- if result.add?(dep)
- transitive_dependencies(locks, dep, dep_key, result)
- end
- end
- result
- end
-
- #
- # Get source and version metadata for the given Bundler spec (coming from a lockfile).
- #
- # @return Hash { version: <version>, git: <git>, path: <path>, source: <source>, ref: <ref> }
- #
- def lock_source_metadata(spec)
- # Copy source information from included Gemfile
- result = {}
- case spec.source
- when Bundler::Source::Rubygems
- result[:source] = spec.source.remotes.first.to_s
- when Bundler::Source::Git
- result[:git] = spec.source.uri.to_s
- result[:ref] = spec.source.revision
- when Bundler::Source::Path
- result[:path] = spec.source.path.to_s
- else
- raise "Unknown source #{spec.source} for gem #{spec.name}"
- end
- result
- end
- end
-end
diff --git a/tasks/maintainers.rb b/tasks/maintainers.rb
index 91742854bb..d2fc482a28 100644
--- a/tasks/maintainers.rb
+++ b/tasks/maintainers.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2015-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ SOURCE = File.join(File.dirname(__FILE__), "..", "MAINTAINERS.toml")
TARGET = File.join(File.dirname(__FILE__), "..", "MAINTAINERS.md")
# The list of repositories that teams should own
-REPOSITORIES = ["chef/chef", "chef/chef-census", "chef/chef-repo",
+REPOSITORIES = ["chef/chef", "chef/chef-dk", "chef/chef-census", "chef/chef-repo",
"chef/client-docs", "chef/ffi-yajl", "chef/libyajl2-gem",
"chef/mixlib-authentication", "chef/mixlib-cli",
"chef/mixlib-config", "chef/mixlib-install", "chef/mixlib-log",
@@ -46,9 +46,9 @@ begin
out << format_person(source["Org"]["Lead"]["person"]) + "\n\n"
out << format_components(source["Org"]["Components"])
- File.open(TARGET, "w") { |fn|
+ File.open(TARGET, "w") do |fn|
fn.write out
- }
+ end
end
desc "Synchronize GitHub teams"
@@ -131,7 +131,7 @@ begin
else
%w{maintainers lieutenant title}.each { |k| cmp.delete(k) }
end
- cmp.each { |_k, v| prepare_teams(v) }
+ cmp.each_value { |v| prepare_teams(v) }
end
def update_team(team, additions, deletions)
@@ -139,8 +139,8 @@ begin
update_team_privacy(team)
add_team_members(team, additions)
remove_team_members(team, deletions)
- rescue
- puts "failed for #{team}"
+ rescue => e
+ puts "failed for #{team}: #{e.message}"
end
def update_team_privacy(team)
@@ -189,7 +189,7 @@ begin
end
out << format_maintainers(cmp.delete("maintainers")) + "\n" if cmp.has_key?("maintainers")
cmp.delete("paths")
- cmp.each { |k, v| out << format_components(v) }
+ cmp.each_value { |v| out << format_components(v) }
out
end
diff --git a/tasks/rspec.rb b/tasks/rspec.rb
index 616a68f09e..848c50eb40 100644
--- a/tasks/rspec.rb
+++ b/tasks/rspec.rb
@@ -25,7 +25,7 @@ CHEF_ROOT = File.join(File.dirname(__FILE__), "..")
begin
require "rspec/core/rake_task"
- desc "Run specs for Chef's Components"
+ desc "Run specs for Chef's Components (chef-config)"
task :component_specs do
Dir.chdir("chef-config") do
Bundler.with_clean_env do
diff --git a/tasks/templates/prerelease.md.erb b/tasks/templates/prerelease.md.erb
new file mode 100644
index 0000000000..0c5c55cffc
--- /dev/null
+++ b/tasks/templates/prerelease.md.erb
@@ -0,0 +1,26 @@
+Ohai Chefs!
+
+We have selected <%= @version %> as our Chef v<%= @maj_minor %> release candidate which is scheduled for release on <%= @date.strftime('%A %B %-d, %Y') %>.
+
+# Release Highlights
+
+<%= @release_notes %>
+
+Please see the [CHANGELOG](https://github.com/chef/chef/blob/master/CHANGELOG.md) for the complete list of changes.
+
+# Get the Build
+As always, you can download binaries directly from [downloads.chef.io](https://downloads.chef.io/chef/current/<%= @version %>) or by using the new `mixlib-install` command line utility available in ChefDK 0.19.6 or greater.
+
+```shell
+$ mixlib-install download chef -v <%= @version %> -c current
+```
+
+Alternatively, you can install Chef using one of the following command options:
+
+```shell
+# In Shell
+$ curl https://omnitruck.chef.io/install.sh | sudo bash -s -- -P chef -v <%= @version %> -c current
+
+# In Windows Powershell
+. { iwr -useb https://omnitruck.chef.io/install.ps1 } | iex; install -project chef -version <%= @version %> -channel current
+```
diff --git a/tasks/templates/release.md.erb b/tasks/templates/release.md.erb
new file mode 100644
index 0000000000..2c6ff0b7c6
--- /dev/null
+++ b/tasks/templates/release.md.erb
@@ -0,0 +1,26 @@
+Ohai Chefs!
+
+We're happy to announce the release of Chef v<%= @maj_minor %>!
+
+# Release Highlights
+
+<%= @release_notes %>
+
+Please see the [CHANGELOG](https://github.com/chef/chef/blob/master/CHANGELOG.md) for the complete list of changes.
+
+# Get the Build
+As always, you can download binaries directly from [downloads.chef.io](https://downloads.chef.io/chef/<%= @version %>) or by using the new `mixlib-install` command line utility available in ChefDK 0.19.6 or greater.
+
+```shell
+$ mixlib-install download chef -v <%= @version %>
+```
+
+Alternatively, you can install Chef using one of the following command options:
+
+```shell
+# In Shell
+$ curl https://omnitruck.chef.io/install.sh | sudo bash -s -- -P chef -v <%= @version %>
+
+# In Windows Powershell
+. { iwr -useb https://omnitruck.chef.io/install.ps1 } | iex; install -project chef -version <%= @version %>
+```
diff --git a/version_policy.rb b/version_policy.rb
deleted file mode 100644
index 59ebd58160..0000000000
--- a/version_policy.rb
+++ /dev/null
@@ -1,111 +0,0 @@
-#
-# Copyright:: Copyright (c) 2016 Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# Explicit omnibus overrides.
-OMNIBUS_OVERRIDES = {
- # Lower level library pins
- ## according to comment in omnibus-sw, latest versions don't work on solaris
- # https://github.com/chef/omnibus-software/blob/aefb7e79d29ca746c3f843673ef5e317fa3cba54/config/software/libtool.rb#L23
- "libffi" => "3.2.1",
- "libiconv" => "1.14",
- "liblzma" => "5.2.2",
- ## according to comment in omnibus-sw, the very latest versions don't work on solaris
- # https://github.com/chef/omnibus-software/blob/aefb7e79d29ca746c3f843673ef5e317fa3cba54/config/software/libtool.rb#L23
- "libtool" => "2.4.2",
- "libxml2" => "2.9.3",
- "libxslt" => "1.1.28",
- "libyaml" => "0.1.6",
- "makedepend" => "1.0.5",
- "ncurses" => "5.9",
- "pkg-config-lite" => "0.28-1",
- "ruby" => "2.1.8",
- # Leave dev-kit pinned to 4.5 on 32-bit, because 4.7 is 20MB larger and we don't want
- # to unnecessarily make the client any fatter. (Since it's different between
- # 32 and 64, we have to do it in the project file still.)
- # "ruby-windows-devkit" => "4.5.2-20111229-1559",
- "ruby-windows-devkit-bash" => "3.1.23-4-msys-1.0.18",
- "util-macros" => "1.19.0",
- "xproto" => "7.0.28",
- "zlib" => "1.2.8",
-
- ## These can float as they are frequently updated in a way that works for us
- #override "cacerts" =>"???",
- #override "openssl" =>"???",
-}
-
-#
-# rake dependencies:update_omnibus_overrides (tasks/dependencies.rb) reads this
-# and modifies omnibus_overrides.rb
-#
-# The left side is the software definition name, and the right side is the
-# name of the rubygem (gem list -re <rubygem name> gets us the latest version).
-#
-OMNIBUS_RUBYGEMS_AT_LATEST_VERSION = {
- rubygems: "rubygems-update",
- bundler: "bundler",
-}
-
-#
-# rake dependencies:check (tasks/dependencies.rb) uses this as a list of gems
-# that are allowed to be outdated according to `bundle updated`
-#
-# Once you decide that the list of outdated gems is OK, you can just
-# add gems to the output of bundle outdated here and we'll parse it to get the
-# list of outdated gems.
-#
-# gherkin - expected to update with new cucumber (and foodcritic?) release
-# jwt - expected to update with new oauth2 release
-# mini_portile2 - should go away *entirely* with new nokogiri release (not a dep anymore)
-# slop - expected to disappear with new pry release
-#
-ACCEPTABLE_OUTDATED_GEMS = %w{
- gherkin
- jwt
- mini_portile2
- slop
-}
-
-#
-# Some gems are part of our bundle (must be installed) but not important
-# enough to lock. We allow `bundle install` in test-kitchen, berks, etc.
-# to use their own versions of these.
-#
-# This mainly tells you which gems `chef verify` allows you to install and
-# run.
-#
-GEMS_ALLOWED_TO_FLOAT = [
-]
-
-#
-# The list of groups we install without: this drives both the `bundle install`
-# we do in chef-dk, and the `bundle check` we do to ensure installed gems don't
-# have extra deps hiding in their Gemfiles.
-#
-# NOTE: we DO install test, because there aren't many gems there, and it makes
-# our test phase a lot easier.
-#
-INSTALL_WITHOUT_GROUPS = %w{
- changelog
- development
- docgen
- guard
- integration
- maintenance
- tools
- travis
- style
-}